#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
use after_effects_sys as ae_sys;
use num_traits::identities::Zero;
use std::{
cell::RefCell,
cmp::{max, min, PartialEq, PartialOrd},
error,
fmt::Display,
ops::{Add, RemAssign},
ptr,
};
#[cfg(feature = "ultraviolet")]
use ultraviolet as uv;
#[macro_use]
mod macros;
#[macro_use]
mod plugin_base;
pub use plugin_base::{AegpPlugin, AegpSeal};
#[macro_use]
mod cross_thread_type;
pub mod aegp;
pub mod aeio;
pub mod drawbot;
pub mod pf;
pub use pf::*;
pub mod pr;
pub mod pr_string;
use pr_string::*;
pub use after_effects_sys as sys;
pub use log;
pub use cstr_literal;
pub use fastrand;
pub use parking_lot;
pub use paste;
pub use serde;
#[cfg(target_os = "windows")]
pub use win_dbg_logger;
#[cfg(target_os = "macos")]
pub use oslog;
thread_local! {
pub(crate) static PICA_BASIC_SUITE: RefCell<*const ae_sys::SPBasicSuite> = const { RefCell::new(ptr::null_mut()) };
}
#[inline]
pub(crate) fn borrow_pica_basic_as_ptr() -> *const ae_sys::SPBasicSuite {
let mut pica_basic_ptr: *const ae_sys::SPBasicSuite = ptr::null();
PICA_BASIC_SUITE.with(|pica_basic_ptr_cell| {
pica_basic_ptr = *pica_basic_ptr_cell.borrow();
});
pica_basic_ptr
}
pub struct PicaBasicSuite {
previous_pica_basic_suite_ptr: *const ae_sys::SPBasicSuite,
}
impl PicaBasicSuite {
fn set(pica_basic_suite_ptr: *const ae_sys::SPBasicSuite) -> *const ae_sys::SPBasicSuite {
let mut previous_pica_basic_suite_ptr: *const ae_sys::SPBasicSuite = ptr::null();
PICA_BASIC_SUITE.with(|pica_basic_ptr_cell| {
previous_pica_basic_suite_ptr = pica_basic_ptr_cell.replace(pica_basic_suite_ptr);
});
previous_pica_basic_suite_ptr
}
#[inline]
pub fn from_pr_in_data_raw(in_data_ptr: *const ae_sys::PR_InData) -> Self {
Self {
previous_pica_basic_suite_ptr: PicaBasicSuite::set(unsafe { *in_data_ptr }.pica_basicP),
}
}
#[inline]
pub fn from_pr_in_data(in_data_handle: pr::InDataHandle) -> Self {
Self {
previous_pica_basic_suite_ptr: PicaBasicSuite::set(
unsafe { *in_data_handle.as_ptr() }.pica_basicP,
),
}
}
#[inline]
pub fn from_pf_in_data_raw(in_data_ptr: *const ae_sys::PF_InData) -> Self {
Self {
previous_pica_basic_suite_ptr: PicaBasicSuite::set(unsafe { *in_data_ptr }.pica_basicP),
}
}
#[inline]
pub fn from_sp_basic_suite_raw(pica_basic_suite_ptr: *const ae_sys::SPBasicSuite) -> Self {
Self {
previous_pica_basic_suite_ptr: PicaBasicSuite::set(pica_basic_suite_ptr),
}
}
}
impl Drop for PicaBasicSuite {
#[inline]
fn drop(&mut self) {
PICA_BASIC_SUITE.with(|pica_basic_ptr_cell| {
pica_basic_ptr_cell.replace(self.previous_pica_basic_suite_ptr);
});
}
}
#[cfg(target_os = "macos")]
const UNKNOWN_ERR_10007: ::std::os::raw::c_uint = 10007;
#[cfg(target_os = "windows")]
const UNKNOWN_ERR_10007: ::std::os::raw::c_int = 10007;
define_enum! {
ae_sys::PF_Err,
Error {
Generic = ae_sys::A_Err_GENERIC,
Struct = ae_sys::A_Err_STRUCT,
Parameter = ae_sys::A_Err_PARAMETER,
OutOfMemory = ae_sys::A_Err_ALLOC,
WrongThread = ae_sys::A_Err_WRONG_THREAD,
ConstProjectModification = ae_sys::A_Err_CONST_PROJECT_MODIFICATION,
MissingSuite = ae_sys::A_Err_MISSING_SUITE,
InternalStructDamaged = ae_sys::PF_Err_INTERNAL_STRUCT_DAMAGED,
InvalidIndex = ae_sys::PF_Err_INVALID_INDEX,
UnrecogizedParameterType = ae_sys::PF_Err_UNRECOGNIZED_PARAM_TYPE,
InvalidCallback = ae_sys::PF_Err_INVALID_CALLBACK,
BadCallbackParameter = ae_sys::PF_Err_BAD_CALLBACK_PARAM,
InterruptCancel = ae_sys::PF_Interrupt_CANCEL,
CannotParseKeyframeText = ae_sys::PF_Err_CANNOT_PARSE_KEYFRAME_TEXT,
Reserved11 = ae_sys::A_Err_RESERVED_11,
StringNotFound = ae_sys::suiteError_StringNotFound,
StringBufferTooSmall = ae_sys::suiteError_StringBufferTooSmall,
InvalidParms = ae_sys::suiteError_InvalidParms,
Unknown10007 = UNKNOWN_ERR_10007,
NotInComputeCache = ae_sys::A_Err_NOT_IN_CACHE_OR_COMPUTE_PENDING,
None = ae_sys::PF_Err_NONE,
}
}
impl From<Error> for &'static str {
fn from(error: Error) -> &'static str {
match error {
Error::Generic => "Generic error.",
Error::Struct => "Wrong struct.",
Error::Parameter => "Wrong parameter.",
Error::OutOfMemory => "Out of memory.",
Error::WrongThread => "Call made from wrong thread.",
Error::ConstProjectModification => "Project changes must originate in the UI/Main thread.",
Error::MissingSuite => "Could not aquire suite.",
Error::InternalStructDamaged => "Internal struct is damaged.",
Error::InvalidIndex => "Out of range, or action not allowed on this index.",
Error::UnrecogizedParameterType => "Unrecognized parameter type",
Error::InvalidCallback => "Invalid callback.",
Error::BadCallbackParameter => "Bad callback parameter.",
Error::InterruptCancel => "Rendering interrupted.",
Error::CannotParseKeyframeText => "Keyframe data damaged.",
Error::None => "No error",
Error::StringNotFound => "StringNotFound",
Error::StringBufferTooSmall => "StringBufferTooSmall",
Error::InvalidParms => "InvalidParms",
Error::Reserved11 => "Reserved11",
Error::Unknown10007 => "Unknown10007",
Error::NotInComputeCache => "Value not found in compute cache.",
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let err_str: &'static str = (*self).into();
write!(f, "{}", err_str)
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl From<std::collections::TryReserveError> for Error {
fn from(_: std::collections::TryReserveError) -> Self {
Error::OutOfMemory
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct Matrix3([[f64; 3]; 3]);
impl From<Matrix3> for ae_sys::A_Matrix3 {
#[inline]
fn from(val: Matrix3) -> Self {
ae_sys::A_Matrix3 {
mat: val.0
}
}
}
impl From<ae_sys::A_Matrix3> for Matrix3 {
#[inline]
fn from(item: ae_sys::A_Matrix3) -> Self {
Self(item.mat)
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct Matrix4([[f64; 4]; 4]);
impl From<Matrix4> for ae_sys::A_Matrix4 {
#[inline]
fn from(val: Matrix4) -> Self {
ae_sys::A_Matrix4 {
mat: val.0
}
}
}
impl From<ae_sys::A_Matrix4> for Matrix4 {
#[inline]
fn from(item: ae_sys::A_Matrix4) -> Self {
Self(item.mat)
}
}
impl Matrix4 {
#[inline]
pub fn as_slice(&self) -> &[f64] {
unsafe { std::slice::from_raw_parts(self.0.as_ptr() as _, 16) }
}
}
impl From<Matrix4> for [f64; 16] {
#[inline]
fn from(m: Matrix4) -> Self {
m.as_slice().try_into().unwrap()
}
}
#[cfg(feature = "ultraviolet")]
impl From<Matrix4> for uv::DMat4 {
#[inline]
fn from(m: Matrix4) -> Self {
uv::DMat4::from(m.0).transposed()
}
}
#[cfg(feature = "ultraviolet")]
impl From<uv::DMat4> for Matrix4 {
#[inline]
fn from(m: uv::DMat4) -> Self {
Self(m.transposed().into())
}
}
#[cfg(feature = "ultraviolet")]
#[test]
fn test_from() {
let m = Matrix4 {
0: [[0.; 4], [0.; 4], [0.; 4], [0.; 4]],
};
let _matrix = uv::mat4D::from(m);
}
#[cfg(feature = "nalgebra")]
impl From<Matrix4> for nalgebra::Matrix4<f64> {
#[inline]
fn from(m: Matrix4) -> Self {
nalgebra::Matrix4::<f64>::from_row_slice(m.as_slice())
}
}
#[cfg(feature = "nalgebra")]
#[test]
fn test_from() {
let m = Matrix4 {
0: [[0.; 4], [0.; 4], [0.; 4], [0.; 4]],
};
let _matrix = nalgebra::Matrix4::<f64>::from(m);
}
pub type Color = ae_sys::A_Color;
define_struct! {
ae_sys::A_Time,
#[derive(Eq)]
Time {
value: i32,
scale: u32,
}
}
impl From<Time> for f64 {
#[inline]
fn from(time: Time) -> Self {
debug_assert!(time.scale != 0);
time.value as Self / time.scale as Self
}
}
impl From<Time> for f32 {
#[inline]
fn from(time: Time) -> Self {
debug_assert!(time.scale != 0);
time.value as Self / time.scale as Self
}
}
#[inline]
fn greatest_common_divisor<T>(mut n: T, mut m: T) -> T
where
T: Copy + PartialEq + PartialOrd + RemAssign + Zero,
{
debug_assert!(n != Zero::zero() && m != Zero::zero());
while m != Zero::zero() {
if m < n {
std::mem::swap(&mut m, &mut n);
}
m %= n;
}
n
}
#[inline]
fn add_time_lossless(time1: Time, time2: Time) -> Option<Time> {
if (time1.scale == 0) || (time2.scale == 0) {
return None;
}
let gcd = {
if time1.scale == time2.scale {
time1.scale
} else {
greatest_common_divisor(time1.scale, time2.scale)
}
};
let value1 = time1
.value
.checked_mul(time2.scale.checked_div(gcd)? as i32)?;
let value2 = time2
.value
.checked_mul(time1.scale.checked_div(gcd)? as i32)?;
Some(Time {
value: value1.checked_add(value2)?,
scale: time2.scale.checked_mul(time1.scale.checked_div(gcd)?)?,
})
}
#[inline]
fn add_time_lossy(time1: Time, time2: Time) -> Time {
let time =
(time1.value as f64 / time1.scale as f64) + (time2.value as f64 / time2.scale as f64);
let num_bits = time.log2() as usize;
let scale: u32 = 1u32 << (30 - num_bits);
Time {
value: (time * scale as f64) as i32,
scale,
}
}
impl Add<Time> for Time {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
match add_time_lossless(self, rhs) {
Some(time) => time,
None => add_time_lossy(self, rhs),
}
}
}
define_struct! {
ae_sys::A_LRect,
#[derive(Eq)]
Rect {
left: i32,
top: i32,
right: i32,
bottom: i32,
}
}
define_struct_conv!(ae_sys::A_LegacyRect, Rect { left, top, right, bottom });
define_struct_conv!(ae_sys::PF_LRect, Rect { left, top, right, bottom });
impl Rect {
pub fn empty() -> Self {
Self {
left: 0,
top: 0,
right: 0,
bottom: 0,
}
}
pub fn is_empty(&self) -> bool {
(self.left >= self.right) || (self.top >= self.bottom)
}
pub fn width(&self) -> i32 {
self.right - self.left
}
pub fn height(&self) -> i32 {
self.bottom - self.top
}
pub fn origin(&self) -> Point {
Point {
h: self.left,
v: self.top,
}
}
pub fn set_width(&mut self, width: i32) {
self.right = self.left + width
}
pub fn set_height(&mut self, height: i32) {
self.bottom = self.top + height
}
pub fn set_origin(&mut self, origin: Point) {
self.left = origin.h;
self.top = origin.v;
}
pub fn union<'a>(&'a mut self, other: &Rect) -> &'a mut Rect {
if self.is_empty() {
*self = *other;
} else {
self.left = min(self.left, other.left);
self.top = min(self.top, other.top);
self.right = max(self.right, other.right);
self.bottom = max(self.bottom, other.bottom);
}
self
}
pub fn is_edge_pixel(&self, x: i32, y: i32) -> bool {
let mut x_hit = (x == self.left) || (x == self.right);
let mut y_hit = (y == self.top) || (y == self.bottom);
if x_hit {
y_hit = (y >= self.top) && (y <= self.bottom);
} else if y_hit {
x_hit = (x >= self.left) && (x <= self.right);
}
x_hit && y_hit
}
pub fn contains(&self, x: i32, y: i32) -> bool {
(self.left <= x) && (x <= self.right) && (self.top <= y) && (y <= self.bottom)
}
}
define_struct! {
ae_sys::A_FloatPoint,
FloatPoint {
x: f64,
y: f64,
}
}
define_struct! {
ae_sys::A_FloatRect,
FloatRect {
left: f64,
top: f64,
right: f64,
bottom: f64,
}
}
impl FloatRect {
pub fn contains(&self, x: f64, y: f64) -> bool {
(self.left <= x) && (x <= self.right) && (self.top <= y) && (y <= self.bottom)
}
}
define_struct! {
ae_sys::A_Ratio,
Ratio {
num: i32,
den: u32,
}
}
impl From<Ratio> for f64 {
#[inline]
fn from(ratio: Ratio) -> Self {
debug_assert!(ratio.den != 0);
ratio.num as Self / ratio.den as Self
}
}
impl From<Ratio> for f32 {
#[inline]
fn from(ratio: Ratio) -> Self {
debug_assert!(ratio.den != 0);
ratio.num as Self / ratio.den as Self
}
}
pub enum Ownership<'a, T: Clone> {
AfterEffects(&'a T),
AfterEffectsMut(&'a mut T),
Rust(T),
}
impl<'a, T: Clone> Clone for Ownership<'a, T> {
fn clone(&self) -> Self {
match self {
Self::AfterEffects(ptr) => Self::Rust((*ptr).clone()),
Self::AfterEffectsMut(ptr) => Self::Rust((*ptr).clone()),
Self::Rust(ptr) => Self::Rust(ptr.clone()),
}
}
}
impl<'a, T: Clone> std::ops::Deref for Ownership<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::AfterEffects(ptr) => ptr,
Self::AfterEffectsMut(ptr) => ptr,
Self::Rust(ptr) => ptr,
}
}
}
impl<'a, T: Clone> std::ops::DerefMut for Ownership<'a, T> {
fn deref_mut(&mut self) -> &mut T {
match self {
Self::AfterEffects(_) => panic!("Tried to mutably borrow immutable data"),
Self::AfterEffectsMut(ptr) => ptr,
Self::Rust(ptr) => ptr,
}
}
}
pub enum ReadOnlyOwnership<'a, T: Clone> {
AfterEffects(&'a T),
Rust(T),
}
impl<'a, T: Clone> ReadOnlyOwnership<'a, T> {
pub fn clone(&self) -> Ownership<'a, T> {
match self {
Self::AfterEffects(ptr) => Ownership::Rust((*ptr).clone()),
Self::Rust(ptr) => Ownership::Rust(ptr.clone()),
}
}
}
impl<'a, T: Clone> std::ops::Deref for ReadOnlyOwnership<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::AfterEffects(ptr) => ptr,
Self::Rust(ptr) => ptr,
}
}
}
pub enum PointerOwnership<T> {
AfterEffects(*mut T),
Rust(T),
}
impl<T> std::ops::Deref for PointerOwnership<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::AfterEffects(ptr) => unsafe { &**ptr },
Self::Rust(ptr) => ptr,
}
}
}
impl<T> std::ops::DerefMut for PointerOwnership<T> {
fn deref_mut(&mut self) -> &mut T {
match self {
Self::AfterEffects(ptr) => unsafe { &mut **ptr },
Self::Rust(ptr) => ptr,
}
}
}
pub struct PicaBasicSuiteHandle {
pica_basic_suite_ptr: *const ae_sys::SPBasicSuite,
}
impl PicaBasicSuiteHandle {
#[inline]
pub fn from_raw(pica_basic_suite_ptr: *const ae_sys::SPBasicSuite) -> PicaBasicSuiteHandle {
PicaBasicSuiteHandle {
pica_basic_suite_ptr,
}
}
#[inline]
pub fn as_ptr(&self) -> *const ae_sys::SPBasicSuite {
self.pica_basic_suite_ptr
}
}
pub(crate) trait Suite {
fn new() -> Result<Self, Error>
where
Self: Sized;
}
pub trait AsPtr<T> {
fn as_ptr(&self) -> T
where
T: Sized;
}
pub trait AsMutPtr<T> {
fn as_mut_ptr(&mut self) -> T
where T: Sized;
}