1#![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
35pub 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
63pub 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 OutOfMemory = ae_sys::A_Err_ALLOC,
137 WrongThread = ae_sys::A_Err_WRONG_THREAD,
140 ConstProjectModification = ae_sys::A_Err_CONST_PROJECT_MODIFICATION,
143 MissingSuite = ae_sys::A_Err_MISSING_SUITE,
145 InternalStructDamaged = ae_sys::PF_Err_INTERNAL_STRUCT_DAMAGED,
146 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 InterruptCancel = ae_sys::PF_Interrupt_CANCEL,
153 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
205impl 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 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 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 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#[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#[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#[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
395impl 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 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 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
617pub 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 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}