after_effects/
lib.rs

1// FIXME: make ALL the functions below return Result-wrapped values
2
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
4use after_effects_sys as ae_sys;
5use num_traits::identities::Zero;
6use std::{
7    cell::RefCell,
8    cmp::{max, min, PartialEq, PartialOrd},
9    error,
10    fmt::Display,
11    ops::{Add, RemAssign},
12    ptr,
13};
14#[cfg(feature = "ultraviolet")]
15use ultraviolet as uv;
16
17#[macro_use]
18mod macros;
19
20#[macro_use]
21mod plugin_base;
22
23#[macro_use]
24mod cross_thread_type;
25
26pub mod aegp;
27pub mod aeio;
28pub mod drawbot;
29pub mod pf;
30pub use pf::*;
31pub mod pr;
32pub mod pr_string;
33use pr_string::*;
34
35// re-exports
36pub use after_effects_sys as sys;
37pub use log;
38pub use cstr_literal;
39pub use fastrand;
40pub use parking_lot;
41pub use paste;
42pub use serde;
43#[cfg(target_os = "windows")]
44pub use win_dbg_logger;
45#[cfg(target_os = "macos")]
46pub use oslog;
47
48thread_local!(
49    pub(crate) static PICA_BASIC_SUITE: RefCell<*const ae_sys::SPBasicSuite> = RefCell::new(ptr::null_mut())
50);
51
52#[inline]
53pub(crate) fn borrow_pica_basic_as_ptr() -> *const ae_sys::SPBasicSuite {
54    let mut pica_basic_ptr: *const ae_sys::SPBasicSuite = ptr::null();
55
56    PICA_BASIC_SUITE.with(|pica_basic_ptr_cell| {
57        pica_basic_ptr = *pica_basic_ptr_cell.borrow();
58    });
59
60    pica_basic_ptr
61}
62
63/// This lets us access a thread-local version of the `PicaBasic`
64/// suite. Whenever we generate a new `SPBasic_Suite` from Ae somehow,
65/// we create a PicaBasicSuite::new() from that and use that to initialize
66/// access to any suites.
67///
68/// When we leave scope, `drop()` ic alled automatically and restores the
69/// previous value to our thread-local storage so the caller
70/// can continue using their pointer to the suite.
71///
72/// FIXME: Is this really neccessary? Check if the pointer is always the
73///        same and if so, confirm with Adobe we can get rid of it.
74pub struct PicaBasicSuite {
75    previous_pica_basic_suite_ptr: *const ae_sys::SPBasicSuite,
76}
77
78impl PicaBasicSuite {
79    fn set(pica_basic_suite_ptr: *const ae_sys::SPBasicSuite) -> *const ae_sys::SPBasicSuite {
80        let mut previous_pica_basic_suite_ptr: *const ae_sys::SPBasicSuite = ptr::null();
81
82        PICA_BASIC_SUITE.with(|pica_basic_ptr_cell| {
83            previous_pica_basic_suite_ptr = pica_basic_ptr_cell.replace(pica_basic_suite_ptr);
84        });
85
86        previous_pica_basic_suite_ptr
87    }
88
89    #[inline]
90    pub fn from_pr_in_data_raw(in_data_ptr: *const ae_sys::PR_InData) -> Self {
91        Self {
92            previous_pica_basic_suite_ptr: PicaBasicSuite::set(unsafe { *in_data_ptr }.pica_basicP),
93        }
94    }
95
96    #[inline]
97    pub fn from_pr_in_data(in_data_handle: pr::InDataHandle) -> Self {
98        Self {
99            previous_pica_basic_suite_ptr: PicaBasicSuite::set(
100                unsafe { *in_data_handle.as_ptr() }.pica_basicP,
101            ),
102        }
103    }
104
105    #[inline]
106    pub fn from_pf_in_data_raw(in_data_ptr: *const ae_sys::PF_InData) -> Self {
107        Self {
108            previous_pica_basic_suite_ptr: PicaBasicSuite::set(unsafe { *in_data_ptr }.pica_basicP),
109        }
110    }
111
112    #[inline]
113    pub fn from_sp_basic_suite_raw(pica_basic_suite_ptr: *const ae_sys::SPBasicSuite) -> Self {
114        Self {
115            previous_pica_basic_suite_ptr: PicaBasicSuite::set(pica_basic_suite_ptr),
116        }
117    }
118}
119
120impl Drop for PicaBasicSuite {
121    #[inline]
122    fn drop(&mut self) {
123        PICA_BASIC_SUITE.with(|pica_basic_ptr_cell| {
124            pica_basic_ptr_cell.replace(self.previous_pica_basic_suite_ptr);
125        });
126    }
127}
128
129define_enum! {
130    ae_sys::PF_Err,
131    Error {
132        Generic                  = ae_sys::A_Err_GENERIC,
133        Struct                   = ae_sys::A_Err_STRUCT,
134        Parameter                = ae_sys::A_Err_PARAMETER,
135        // Also called A_Err_ALLOC in Ae.
136        OutOfMemory              = ae_sys::A_Err_ALLOC,
137        // Some calls can only be used on UI (Main) or Render threads.
138        // Also, calls back to Ae can only be made from the same thread Ae called you on.
139        WrongThread              = ae_sys::A_Err_WRONG_THREAD,
140        // An attempt was made to write to a read only copy of an Ae project.
141        // Project changes must originate in the UI/Main thread.
142        ConstProjectModification = ae_sys::A_Err_CONST_PROJECT_MODIFICATION,
143        // Acquire suite failed on a required suite.
144        MissingSuite             = ae_sys::A_Err_MISSING_SUITE,
145        InternalStructDamaged    = ae_sys::PF_Err_INTERNAL_STRUCT_DAMAGED,
146        // Out of range, or action not allowed on this index.
147        InvalidIndex             = ae_sys::PF_Err_INVALID_INDEX,
148        UnrecogizedParameterType = ae_sys::PF_Err_UNRECOGNIZED_PARAM_TYPE,
149        InvalidCallback          = ae_sys::PF_Err_INVALID_CALLBACK,
150        BadCallbackParameter     = ae_sys::PF_Err_BAD_CALLBACK_PARAM,
151        // Returned when user interrupts rendering.
152        InterruptCancel          = ae_sys::PF_Interrupt_CANCEL,
153        // Returned from PF_Arbitrary_SCAN_FUNC when effect cannot parse arbitrary data from text
154        CannotParseKeyframeText  = ae_sys::PF_Err_CANNOT_PARSE_KEYFRAME_TEXT,
155
156        Reserved11               = ae_sys::A_Err_RESERVED_11,
157
158        StringNotFound           = ae_sys::suiteError_StringNotFound,
159        StringBufferTooSmall     = ae_sys::suiteError_StringBufferTooSmall,
160        InvalidParms             = ae_sys::suiteError_InvalidParms,
161
162        None = ae_sys::PF_Err_NONE,
163    }
164}
165
166impl From<Error> for &'static str {
167    fn from(error: Error) -> &'static str {
168        match error {
169            Error::Generic                  => "Generic error.",
170            Error::Struct                   => "Wrong struct.",
171            Error::Parameter                => "Wrong parameter.",
172            Error::OutOfMemory              => "Out of memory.",
173            Error::WrongThread              => "Call made from wrong thread.",
174            Error::ConstProjectModification => "Project changes must originate in the UI/Main thread.",
175            Error::MissingSuite             => "Could no aquire suite.",
176            Error::InternalStructDamaged    => "Internal struct is damaged.",
177            Error::InvalidIndex             => "Out of range, or action not allowed on this index.",
178            Error::UnrecogizedParameterType => "Unrecognized parameter type",
179            Error::InvalidCallback          => "Invalid callback.",
180            Error::BadCallbackParameter     => "Bad callback parameter.",
181            Error::InterruptCancel          => "Rendering interrupted.",
182            Error::CannotParseKeyframeText  => "Keyframe data damaged.",
183            Error::None                     => "No error",
184            Error::StringNotFound           => "StringNotFound",
185            Error::StringBufferTooSmall     => "StringBufferTooSmall",
186            Error::InvalidParms             => "InvalidParms",
187            Error::Reserved11               => "Reserved11",
188        }
189    }
190}
191
192impl Display for Error {
193    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
194        let err_str: &'static str = (*self).into();
195        write!(f, "{}", err_str)
196    }
197}
198
199impl error::Error for Error {
200    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
201        Some(self)
202    }
203}
204
205//FIXME uncomment this once TryReserve() becomes stable in nightly
206impl From<std::collections::TryReserveError> for Error {
207    fn from(_: std::collections::TryReserveError) -> Self {
208        Error::OutOfMemory
209    }
210}
211
212#[derive(Debug, Copy, Clone)]
213#[repr(C)]
214pub struct Matrix3([[f64; 3]; 3]);
215impl From<Matrix3> for ae_sys::A_Matrix3 {
216    #[inline]
217    fn from(val: Matrix3) -> Self {
218        ae_sys::A_Matrix3 {
219            mat: val.0
220        }
221    }
222}
223impl From<ae_sys::A_Matrix3> for Matrix3 {
224    #[inline]
225    fn from(item: ae_sys::A_Matrix3) -> Self  {
226        Self(item.mat)
227    }
228}
229
230#[derive(Debug, Copy, Clone)]
231#[repr(C)]
232pub struct Matrix4([[f64; 4]; 4]);
233
234impl From<Matrix4> for ae_sys::A_Matrix4 {
235    #[inline]
236    fn from(val: Matrix4) -> Self {
237        ae_sys::A_Matrix4 {
238            mat: val.0
239        }
240    }
241}
242impl From<ae_sys::A_Matrix4> for Matrix4 {
243    #[inline]
244    fn from(item: ae_sys::A_Matrix4) -> Self  {
245        Self(item.mat)
246    }
247}
248
249impl Matrix4 {
250    #[inline]
251    pub fn as_slice(&self) -> &[f64] {
252        unsafe { std::slice::from_raw_parts(self.0.as_ptr() as _, 16) }
253    }
254}
255
256impl From<Matrix4> for [f64; 16] {
257    #[inline]
258    fn from(m: Matrix4) -> Self {
259        // This can not panic.
260        m.as_slice().try_into().unwrap()
261    }
262}
263
264#[cfg(feature = "ultraviolet")]
265impl From<Matrix4> for uv::DMat4 {
266    #[inline]
267    fn from(m: Matrix4) -> Self {
268        // Ae is row-based – transpose
269        uv::DMat4::from(m.0).transposed()
270    }
271}
272
273#[cfg(feature = "ultraviolet")]
274impl From<uv::DMat4> for Matrix4 {
275    #[inline]
276    fn from(m: uv::DMat4) -> Self {
277        // Ae is row-based – transpose
278        Self(m.transposed().into())
279    }
280}
281
282#[cfg(feature = "ultraviolet")]
283#[test]
284fn test_from() {
285    let m = Matrix4 {
286        0: [[0.; 4], [0.; 4], [0.; 4], [0.; 4]],
287    };
288    let _matrix = uv::mat4D::from(m);
289}
290
291#[cfg(feature = "nalgebra")]
292impl From<Matrix4> for nalgebra::Matrix4<f64> {
293    #[inline]
294    fn from(m: Matrix4) -> Self {
295        nalgebra::Matrix4::<f64>::from_row_slice(m.as_slice())
296    }
297}
298
299#[cfg(feature = "nalgebra")]
300#[test]
301fn test_from() {
302    let m = Matrix4 {
303        0: [[0.; 4], [0.; 4], [0.; 4], [0.; 4]],
304    };
305    let _matrix = nalgebra::Matrix4::<f64>::from(m);
306}
307
308pub type Color = ae_sys::A_Color;
309
310define_struct! {
311    ae_sys::A_Time,
312    #[derive(Eq)]
313    Time {
314        value: i32,
315        scale: u32,
316    }
317}
318impl From<Time> for f64 {
319    #[inline]
320    fn from(time: Time) -> Self {
321        debug_assert!(time.scale != 0);
322        time.value as Self / time.scale as Self
323    }
324}
325impl From<Time> for f32 {
326    #[inline]
327    fn from(time: Time) -> Self {
328        debug_assert!(time.scale != 0);
329        time.value as Self / time.scale as Self
330    }
331}
332
333// Euclid's two-thousand-year-old algorithm for finding the
334// greatest common divisor. Copied non-recursive version from
335// Rust docs.
336#[inline]
337fn greatest_common_divisor<T>(mut n: T, mut m: T) -> T
338where
339    T: Copy + PartialEq + PartialOrd + RemAssign + Zero,
340{
341    debug_assert!(n != Zero::zero() && m != Zero::zero());
342    while m != Zero::zero() {
343        if m < n {
344            std::mem::swap(&mut m, &mut n);
345        }
346        m %= n;
347    }
348    n
349}
350
351/// Calculates the wrapped sum of two [`Time`]s. If no common integer
352/// demoninator can be found, `None` is returned.
353#[inline]
354fn add_time_lossless(time1: Time, time2: Time) -> Option<Time> {
355    if (time1.scale == 0) || (time2.scale == 0) {
356        return None;
357    }
358
359    let gcd = {
360        if time1.scale == time2.scale {
361            time1.scale
362        } else {
363            greatest_common_divisor(time1.scale, time2.scale)
364        }
365    };
366
367    let value1 = time1
368        .value
369        .checked_mul(time2.scale.checked_div(gcd)? as i32)?;
370    let value2 = time2
371        .value
372        .checked_mul(time1.scale.checked_div(gcd)? as i32)?;
373
374    Some(Time {
375        value: value1.checked_add(value2)?,
376        scale: time2.scale.checked_mul(time1.scale.checked_div(gcd)?)?,
377    })
378}
379
380/// Calculates the sum of two [`Time`]s using floating point math.
381#[inline]
382fn add_time_lossy(time1: Time, time2: Time) -> Time {
383    let time =
384        (time1.value as f64 / time1.scale as f64) + (time2.value as f64 / time2.scale as f64);
385
386    let num_bits = time.log2() as usize;
387    let scale: u32 = 1u32 << (30 - num_bits);
388
389    Time {
390        value: (time * scale as f64) as i32,
391        scale,
392    }
393}
394
395// Next bit (std::ops::Add) ported from aeutility.cpp
396// Rust version is so much shorter & cleaner!
397//
398/// Calculates the sum of two [`Time`]s, lossless if possible.
399// FIXME: is it worth going the lossless route at all???
400impl Add<Time> for Time {
401    type Output = Self;
402
403    #[inline]
404    fn add(self, rhs: Self) -> Self {
405        match add_time_lossless(self, rhs) {
406            Some(time) => time,
407            None => add_time_lossy(self, rhs),
408        }
409    }
410}
411
412define_struct! {
413    ae_sys::A_LRect,
414    #[derive(Eq)]
415    /// Note that this has a different ordering of values than LegacyRect!
416    Rect {
417        left: i32,
418        top: i32,
419        right: i32,
420        bottom: i32,
421    }
422}
423define_struct_conv!(ae_sys::A_LegacyRect, Rect { left, top, right, bottom });
424define_struct_conv!(ae_sys::PF_LRect,     Rect { left, top, right, bottom });
425
426impl Rect {
427    pub fn empty() -> Self {
428        Self {
429            left: 0,
430            top: 0,
431            right: 0,
432            bottom: 0,
433        }
434    }
435
436    pub fn is_empty(&self) -> bool {
437        (self.left >= self.right) || (self.top >= self.bottom)
438    }
439    pub fn width(&self) -> i32 {
440        self.right - self.left
441    }
442    pub fn height(&self) -> i32 {
443        self.bottom - self.top
444    }
445    pub fn origin(&self) -> Point {
446        Point {
447            h: self.left,
448            v: self.top,
449        }
450    }
451    pub fn set_width(&mut self, width: i32) {
452        self.right = self.left + width
453    }
454    pub fn set_height(&mut self, height: i32) {
455        self.bottom = self.top + height
456    }
457    pub fn set_origin(&mut self, origin: Point) {
458        self.left = origin.h;
459        self.top = origin.v;
460    }
461
462    pub fn union<'a>(&'a mut self, other: &Rect) -> &'a mut Rect {
463        if self.is_empty() {
464            *self = *other;
465        } else {
466            // if !other.is_empty()
467            self.left = min(self.left, other.left);
468            self.top = min(self.top, other.top);
469            self.right = max(self.right, other.right);
470            self.bottom = max(self.bottom, other.bottom);
471        }
472        self
473    }
474
475    pub fn is_edge_pixel(&self, x: i32, y: i32) -> bool {
476        let mut x_hit = (x == self.left) || (x == self.right);
477        let mut y_hit = (y == self.top) || (y == self.bottom);
478
479        if x_hit {
480            y_hit = (y >= self.top) && (y <= self.bottom);
481        } else if y_hit {
482            x_hit = (x >= self.left) && (x <= self.right);
483        }
484        x_hit && y_hit
485    }
486
487    pub fn contains(&self, x: i32, y: i32) -> bool {
488        (self.left <= x) && (x <= self.right) && (self.top <= y) && (y <= self.bottom)
489    }
490}
491
492define_struct! {
493    ae_sys::A_FloatPoint,
494    FloatPoint {
495        x: f64,
496        y: f64,
497    }
498}
499
500define_struct! {
501    ae_sys::A_FloatRect,
502    FloatRect {
503        left: f64,
504        top: f64,
505        right: f64,
506        bottom: f64,
507    }
508}
509
510impl FloatRect {
511    pub fn contains(&self, x: f64, y: f64) -> bool {
512        (self.left <= x) && (x <= self.right) && (self.top <= y) && (y <= self.bottom)
513    }
514}
515
516define_struct! {
517    ae_sys::A_Ratio,
518    Ratio {
519        num: i32,
520        den: u32,
521    }
522}
523
524impl From<Ratio> for f64 {
525    #[inline]
526    fn from(ratio: Ratio) -> Self {
527        debug_assert!(ratio.den != 0);
528        ratio.num as Self / ratio.den as Self
529    }
530}
531
532impl From<Ratio> for f32 {
533    #[inline]
534    fn from(ratio: Ratio) -> Self {
535        debug_assert!(ratio.den != 0);
536        ratio.num as Self / ratio.den as Self
537    }
538}
539
540pub enum Ownership<'a, T: Clone> {
541    AfterEffects(&'a T),
542    AfterEffectsMut(&'a mut T),
543    Rust(T),
544}
545impl<'a, T: Clone> Clone for Ownership<'a, T> {
546    fn clone(&self) -> Self {
547        match self {
548            Self::AfterEffects(ptr)    => Self::Rust((*ptr).clone()),
549            Self::AfterEffectsMut(ptr) => Self::Rust((*ptr).clone()),
550            Self::Rust(ptr)            => Self::Rust(ptr.clone()),
551        }
552    }
553}
554impl<'a, T: Clone> std::ops::Deref for Ownership<'a, T> {
555    type Target = T;
556    fn deref(&self) -> &Self::Target {
557        match self {
558            Self::AfterEffects(ptr) => ptr,
559            Self::AfterEffectsMut(ptr) => ptr,
560            Self::Rust(ptr) => ptr,
561        }
562    }
563}
564impl<'a, T: Clone> std::ops::DerefMut for Ownership<'a, T> {
565    fn deref_mut(&mut self) -> &mut T {
566        match self {
567            Self::AfterEffects(_) => panic!("Tried to mutably borrow immutable data"),
568            Self::AfterEffectsMut(ptr) => ptr,
569            Self::Rust(ptr) => ptr,
570        }
571    }
572}
573pub enum ReadOnlyOwnership<'a, T: Clone> {
574    AfterEffects(&'a T),
575    Rust(T),
576}
577impl<'a, T: Clone> ReadOnlyOwnership<'a, T> {
578    pub fn clone(&self) -> Ownership<'a, T> {
579        match self {
580            Self::AfterEffects(ptr) => Ownership::Rust((*ptr).clone()),
581            Self::Rust(ptr)         => Ownership::Rust(ptr.clone()),
582        }
583    }
584}
585impl<'a, T: Clone> std::ops::Deref for ReadOnlyOwnership<'a, T> {
586    type Target = T;
587    fn deref(&self) -> &Self::Target {
588        match self {
589            Self::AfterEffects(ptr) => ptr,
590            Self::Rust(ptr) => ptr,
591        }
592    }
593}
594
595pub enum PointerOwnership<T> {
596    AfterEffects(*mut T),
597    Rust(T),
598}
599impl<T> std::ops::Deref for PointerOwnership<T> {
600    type Target = T;
601    fn deref(&self) -> &Self::Target {
602        match self {
603            Self::AfterEffects(ptr) => unsafe { &**ptr },
604            Self::Rust(ptr) => ptr,
605        }
606    }
607}
608impl<T> std::ops::DerefMut for PointerOwnership<T> {
609    fn deref_mut(&mut self) -> &mut T {
610        match self {
611            Self::AfterEffects(ptr) => unsafe { &mut **ptr },
612            Self::Rust(ptr) => ptr,
613        }
614    }
615}
616
617// This is confusing: for some structs Ae expects the caller to
618// manage the memory and for others it doesn't (the caller only
619// deals with a pointer that gets dereferenced for actually
620// calling into the suite). In this case the struct ends
621// with a `H` (for handle).
622// When the struct misses the trailing `H`, Ae does expect us to
623// manage the memory. We then use a Box<T>.
624pub struct PicaBasicSuiteHandle {
625    pica_basic_suite_ptr: *const ae_sys::SPBasicSuite,
626}
627
628impl PicaBasicSuiteHandle {
629    #[inline]
630    pub fn from_raw(pica_basic_suite_ptr: *const ae_sys::SPBasicSuite) -> PicaBasicSuiteHandle {
631        /*if pica_basic_suite_ptr == ptr::null() {
632            panic!()
633        }*/
634        PicaBasicSuiteHandle {
635            pica_basic_suite_ptr,
636        }
637    }
638
639    #[inline]
640    pub fn as_ptr(&self) -> *const ae_sys::SPBasicSuite {
641        self.pica_basic_suite_ptr
642    }
643}
644
645pub(crate) trait Suite {
646    fn new() -> Result<Self, Error>
647    where
648        Self: Sized;
649}
650
651pub trait AsPtr<T> {
652    fn as_ptr(&self) -> T
653    where
654        T: Sized;
655}
656
657pub trait AsMutPtr<T> {
658    fn as_mut_ptr(&mut self) -> T
659    where T: Sized;
660}