diff --git a/after-effects-sys/build.rs b/after-effects-sys/build.rs index 155cf94e..0c3261eb 100644 --- a/after-effects-sys/build.rs +++ b/after-effects-sys/build.rs @@ -15,12 +15,15 @@ fn main() { return; } - let ae_sdk_path = &env::var("AESDK_ROOT").ok().filter(|x| !x.is_empty()).expect( - "AESDK_ROOT environment variable not set – cannot find AfterEffects SDK.\n\ + let ae_sdk_path = &env::var("AESDK_ROOT") + .ok() + .filter(|x| !x.is_empty()) + .expect( + "AESDK_ROOT environment variable not set – cannot find AfterEffects SDK.\n\ Please set AESDK_ROOT to the root folder of your AfterEffects SDK\n\ installation (this folder contains the Examples folder & the SDK\n\ Guide PDF).", - ); + ); if !Path::new(ae_sdk_path).exists() { panic!("AESDK_ROOT environment variable points to non-existent path: {ae_sdk_path}"); @@ -109,9 +112,16 @@ fn main() { // Let's just rewrite the bindings here and pretend it never happened if let Ok(mut content) = std::fs::read_to_string(out_path.join("bindings.rs")) { if content.contains("pub __bindgen_anon_1: PF_Point__bindgen_ty_1,") { - content = content.replace("pub __bindgen_anon_1: PF_Point__bindgen_ty_1,", "pub h: A_long,"); - content = content.replace("pub __bindgen_anon_2: PF_Point__bindgen_ty_2,", "pub v: A_long,"); - std::fs::write(out_path.join("bindings.rs"), content).expect("Couldn't rewrite AfterEffects bindings!"); + content = content.replace( + "pub __bindgen_anon_1: PF_Point__bindgen_ty_1,", + "pub h: A_long,", + ); + content = content.replace( + "pub __bindgen_anon_2: PF_Point__bindgen_ty_2,", + "pub v: A_long,", + ); + std::fs::write(out_path.join("bindings.rs"), content) + .expect("Couldn't rewrite AfterEffects bindings!"); } } } diff --git a/after-effects-sys/src/lib.rs b/after-effects-sys/src/lib.rs index ecb0e5f6..76ffafa9 100644 --- a/after-effects-sys/src/lib.rs +++ b/after-effects-sys/src/lib.rs @@ -4,7 +4,6 @@ #![allow(non_upper_case_globals)] #![allow(non_snake_case)] #![allow(unused_attributes)] - #![doc = include_str!("../README.md")] // Included bindings are generated from After Effects SDK 25.6 dated Sep 2025 diff --git a/after-effects/src/aegp/mod.rs b/after-effects/src/aegp/mod.rs index 66cf1faa..4f96ce45 100644 --- a/after-effects/src/aegp/mod.rs +++ b/after-effects/src/aegp/mod.rs @@ -1,6 +1,6 @@ use crate::ae_sys; -use std::{ convert::TryFrom, ffi::CString, marker::PhantomData }; +use std::{convert::TryFrom, ffi::CString, marker::PhantomData}; use widestring::U16CString; #[cfg(feature = "artisan-2-api")] @@ -9,184 +9,123 @@ mod scene_3d; pub use scene_3d::*; pub mod suites { - pub(crate) mod camera; pub use camera ::CameraSuite as Camera; - pub(crate) mod canvas; pub use canvas ::CanvasSuite as Canvas; - pub(crate) mod color_settings; pub use color_settings ::ColorSettingsSuite as ColorSettings; - pub(crate) mod command; pub use command ::CommandSuite as Command; - pub(crate) mod comp; pub use comp ::CompSuite as Comp; - pub(crate) mod composite; pub use composite ::CompositeSuite as Composite; - pub(crate) mod effect; pub use effect ::EffectSuite as Effect; - pub(crate) mod footage; pub use footage ::FootageSuite as Footage; - pub(crate) mod io_in; pub use io_in ::IOInSuite as IOIn; - pub(crate) mod item; pub use item ::ItemSuite as Item; - pub(crate) mod keyframe; pub use keyframe ::KeyframeSuite as Keyframe; - pub(crate) mod layer_render_options; pub use layer_render_options::LayerRenderOptionsSuite as LayerRenderOptions; - pub(crate) mod layer; pub use layer ::LayerSuite as Layer; - pub(crate) mod light; pub use light ::LightSuite as Light; - pub(crate) mod mask; pub use mask ::{ MaskSuite as Mask, - MaskOutlineSuite as MaskOutline }; - pub(crate) mod memory; pub use memory ::MemorySuite as Memory; - pub(crate) mod persistent_data; pub use persistent_data ::PersistentDataSuite as PersistentData; - pub(crate) mod pf_interface; pub use pf_interface ::PFInterfaceSuite as PFInterface; - pub(crate) mod project; pub use project ::ProjectSuite as Project; - pub(crate) mod register; pub use register ::{ RegisterSuite as Register, - RegisterNonAegpSuite as RegisterNonAegp }; - pub(crate) mod render_async_manager; pub use render_async_manager::RenderAsyncManagerSuite as RenderAsyncManager; - pub(crate) mod render_options; pub use render_options ::RenderOptionsSuite as RenderOptions; - pub(crate) mod render; pub use render ::RenderSuite as Render; - pub(crate) mod output_module; pub use output_module ::OutputModuleSuite as OutputModule; - pub(crate) mod render_queue; pub use render_queue ::RenderQueueSuite as RenderQueue; - pub(crate) mod render_queue_item; pub use render_queue_item ::RenderQueueItemSuite as RenderQueueItem; - pub(crate) mod sound_data; pub use sound_data ::SoundDataSuite as SoundData; - pub(crate) mod stream; pub use stream ::{ StreamSuite as Stream, - DynamicStreamSuite as DynamicStream }; - pub(crate) mod utility; pub use utility ::UtilitySuite as Utility; - pub(crate) mod world; pub use world ::WorldSuite as World; - pub(crate) mod compute_cache; pub use compute_cache ::ComputeCacheSuite as ComputeCache; - pub(crate) mod hash; pub use hash ::HashSuite as Hash; + pub(crate) mod camera; + pub use camera::CameraSuite as Camera; + pub(crate) mod canvas; + pub use canvas::CanvasSuite as Canvas; + pub(crate) mod color_settings; + pub use color_settings::ColorSettingsSuite as ColorSettings; + pub(crate) mod command; + pub use command::CommandSuite as Command; + pub(crate) mod comp; + pub use comp::CompSuite as Comp; + pub(crate) mod composite; + pub use composite::CompositeSuite as Composite; + pub(crate) mod effect; + pub use effect::EffectSuite as Effect; + pub(crate) mod footage; + pub use footage::FootageSuite as Footage; + pub(crate) mod io_in; + pub use io_in::IOInSuite as IOIn; + pub(crate) mod item; + pub use item::ItemSuite as Item; + pub(crate) mod keyframe; + pub use keyframe::KeyframeSuite as Keyframe; + pub(crate) mod layer_render_options; + pub use layer_render_options::LayerRenderOptionsSuite as LayerRenderOptions; + pub(crate) mod layer; + pub use layer::LayerSuite as Layer; + pub(crate) mod light; + pub use light::LightSuite as Light; + pub(crate) mod mask; + pub use mask::{MaskOutlineSuite as MaskOutline, MaskSuite as Mask}; + pub(crate) mod memory; + pub use memory::MemorySuite as Memory; + pub(crate) mod persistent_data; + pub use persistent_data::PersistentDataSuite as PersistentData; + pub(crate) mod pf_interface; + pub use pf_interface::PFInterfaceSuite as PFInterface; + pub(crate) mod project; + pub use project::ProjectSuite as Project; + pub(crate) mod register; + pub use register::{RegisterNonAegpSuite as RegisterNonAegp, RegisterSuite as Register}; + pub(crate) mod render_async_manager; + pub use render_async_manager::RenderAsyncManagerSuite as RenderAsyncManager; + pub(crate) mod render_options; + pub use render_options::RenderOptionsSuite as RenderOptions; + pub(crate) mod render; + pub use render::RenderSuite as Render; + pub(crate) mod output_module; + pub use output_module::OutputModuleSuite as OutputModule; + pub(crate) mod render_queue; + pub use render_queue::RenderQueueSuite as RenderQueue; + pub(crate) mod render_queue_item; + pub use render_queue_item::RenderQueueItemSuite as RenderQueueItem; + pub(crate) mod sound_data; + pub use sound_data::SoundDataSuite as SoundData; + pub(crate) mod stream; + pub use stream::{DynamicStreamSuite as DynamicStream, StreamSuite as Stream}; + pub(crate) mod utility; + pub use utility::UtilitySuite as Utility; + pub(crate) mod world; + pub use world::WorldSuite as World; + pub(crate) mod compute_cache; + pub use compute_cache::ComputeCacheSuite as ComputeCache; + pub(crate) mod hash; + pub use hash::HashSuite as Hash; } pub type PluginId = ae_sys::AEGP_PluginID; pub type ItemId = i32; pub type LayerId = u32; -pub use suites::command::{MenuId, MenuOrder}; -pub use suites::register::{HookPriority, CommandHookStatus}; -pub use suites::project::{ - ProjectHandle, - ProjectBitDepth, -}; -pub use suites::camera::{ - Camera, - CameraType, - FilmSizeUnits, -}; +pub use suites::camera::{Camera, CameraType, FilmSizeUnits}; pub use suites::canvas::{ - Canvas, - BinType, - DisplayChannel, - RenderHints, - RenderLayerContextHandle, - RenderNumEffects, - RenderReceiptHandle, - RenderReceiptStatus, -}; -pub use suites::color_settings::{ - ColorProfileHandle, - ConstColorProfileHandle, - ItemViewHandle, -}; -pub use suites::comp::{ - Composition, - Collection2Handle, - CompFlags, - CompHandle, -}; -pub use suites::compute_cache:: { - ComputeClassId -}; -pub use suites::effect::{ - Effect, - EffectFlags, - EffectRefHandle, - InstalledEffectKey, + BinType, Canvas, DisplayChannel, RenderHints, RenderLayerContextHandle, RenderNumEffects, + RenderReceiptHandle, RenderReceiptStatus, }; +pub use suites::color_settings::{ColorProfileHandle, ConstColorProfileHandle, ItemViewHandle}; +pub use suites::command::{MenuId, MenuOrder}; +pub use suites::comp::{Collection2Handle, CompFlags, CompHandle, Composition}; +pub use suites::compute_cache::ComputeClassId; +pub use suites::effect::{Effect, EffectFlags, EffectRefHandle, InstalledEffectKey}; pub use suites::footage::{ - Footage, - FootageHandle, - FootageSignature, - InterpretationStyle, - Platform, + Footage, FootageHandle, FootageSignature, InterpretationStyle, Platform, }; +pub use suites::hash::Guid; pub use suites::io_in::InputSpecification; -pub use suites::item::{ - Item, - ItemFlags, - ItemHandle, - ItemType, - LabelId, -}; +pub use suites::item::{Item, ItemFlags, ItemHandle, ItemType, LabelId}; pub use suites::keyframe::{ + AddKeyframesInfoHandle, KeyframeFlags, KeyframeInterpolation, KeyframeInterpolationMask, Keyframes, - AddKeyframesInfoHandle, - KeyframeFlags, - KeyframeInterpolation, - KeyframeInterpolationMask, -}; -pub use suites::layer_render_options::{ - LayerRenderOptions, - LayerRenderOptionsHandle, - MatteMode }; pub use suites::layer::{ - Layer, - LayerFlags, - LayerHandle, - LayerQuality, - LayerSamplingQuality, - ObjectType, - TimeMode, + Layer, LayerFlags, LayerHandle, LayerQuality, LayerSamplingQuality, ObjectType, TimeMode, TrackMatte, }; +pub use suites::layer_render_options::{LayerRenderOptions, LayerRenderOptionsHandle, MatteMode}; pub use suites::light::LightType; pub use suites::mask::{ - Mask, - MaskOutline, - MaskFeatherFalloff, - MaskFeatherInterp, - MaskFeatherType, - MaskMBlur, - MaskMode, - MaskOutlineHandle, - MaskRefHandle, -}; -pub use suites::memory::{ - MemHandle, - MemHandleLock, -}; -pub use suites::persistent_data::{ - PersistentType, - PersistentBlobHandle -}; -pub use suites::render_async_manager::AsyncManager; -pub use suites::render_options::{ - RenderOptions, - RenderOptionsHandle, - ItemQuality, - ChannelOrder + Mask, MaskFeatherFalloff, MaskFeatherInterp, MaskFeatherType, MaskMBlur, MaskMode, MaskOutline, + MaskOutlineHandle, MaskRefHandle, }; +pub use suites::memory::{MemHandle, MemHandleLock}; pub use suites::output_module::{ - EmbeddingType, - OutputTypes, - PostRenderAction, - StretchQuality, - VideoChannels, + EmbeddingType, OutputTypes, PostRenderAction, StretchQuality, VideoChannels, }; +pub use suites::persistent_data::{PersistentBlobHandle, PersistentType}; +pub use suites::project::{ProjectBitDepth, ProjectHandle}; +pub use suites::register::{CommandHookStatus, HookPriority}; +pub use suites::render_async_manager::AsyncManager; +pub use suites::render_options::{ChannelOrder, ItemQuality, RenderOptions, RenderOptionsHandle}; pub use suites::render_queue::RenderQueueState; pub use suites::render_queue_item::{ - LogType, - OutputModuleRefHandle, - RenderItemStatus, - RQItemRefHandle, + LogType, OutputModuleRefHandle, RQItemRefHandle, RenderItemStatus, }; pub use suites::sound_data::SoundDataHandle; pub use suites::stream::{ - Stream, - DynamicStreamFlags, - LayerStream, - MaskStream, - StreamFlags, - StreamGroupingType, - StreamReferenceHandle, - StreamType, - StreamValue, - TextDocumentHandle, + DynamicStreamFlags, LayerStream, MaskStream, Stream, StreamFlags, StreamGroupingType, + StreamReferenceHandle, StreamType, StreamValue, TextDocumentHandle, }; pub use suites::utility::GetPathTypes; -pub use suites::world::{ - PlatformWorldHandle, - World, - WorldHandle, - WorldType, -}; -pub use suites::hash::Guid; +pub use suites::world::{PlatformWorldHandle, World, WorldHandle, WorldType}; diff --git a/after-effects/src/aegp/scene_3d.rs b/after-effects/src/aegp/scene_3d.rs index 63c55c6a..86e61d30 100644 --- a/after-effects/src/aegp/scene_3d.rs +++ b/after-effects/src/aegp/scene_3d.rs @@ -1,4 +1,4 @@ -use crate::{ae_sys, borrow_pica_basic_as_ptr, pr, Error, Matrix4, Suite, Time, WorldHandle}; +use crate::{Error, Matrix4, Suite, Time, WorldHandle, ae_sys, borrow_pica_basic_as_ptr, pr}; // TDOO: finish wrapping the entire scene 3d @@ -62,14 +62,10 @@ impl Scene3D { } #[inline] - pub fn scene3d_ptr(&self) -> *mut ae_sys::AEGP_Scene3D { - self.scene3d_ptr - } + pub fn scene3d_ptr(&self) -> *mut ae_sys::AEGP_Scene3D { self.scene3d_ptr } #[inline] - pub fn scene3d_suite_ptr(&self) -> *const ae_sys::AEGP_Scene3DSuite2 { - self - } + pub fn scene3d_suite_ptr(&self) -> *const ae_sys::AEGP_Scene3DSuite2 { self } #[inline] pub fn setup_motion_blur_samples( @@ -243,9 +239,7 @@ impl Scene3DLayerHandle { } #[inline] - pub fn as_ptr(&self) -> *const ae_sys::AEGP_Scene3DLayer { - self.scene3d_layer_ptr - } + pub fn as_ptr(&self) -> *const ae_sys::AEGP_Scene3DLayer { self.scene3d_layer_ptr } } pub struct Scene3DTextureCacheHandle { diff --git a/after-effects/src/aegp/suites/camera.rs b/after-effects/src/aegp/suites/camera.rs index ac103f31..fb2c7c75 100644 --- a/after-effects/src/aegp/suites/camera.rs +++ b/after-effects/src/aegp/suites/camera.rs @@ -1,153 +1,184 @@ -use crate::*; -use crate::aegp::*; -use ae_sys::{ AEGP_LayerH, PR_RenderContextH }; - -define_suite!( - /// Obtains the camera geometry, including camera properties (type, lens, depth of field, focal distance, aperture, et cetera). - /// - /// # Notes Regarding Camera Behavior - /// Camera orientation is in composition coordinates, and the rotations are in layer (the camera's layer) coordinates. - /// - /// If the camera layer has a parent, the position is in a coordinate space relative to the parent. - /// - /// # Orthographic Camera Matrix - /// Internally, we use composition width and height to set the matrix described by the OpenGL specification as - /// ```ignore - /// glOrtho(-width/2, width/2, -height/2, height/2, -1, 100); - /// ``` - /// The orthographic matrix describes the projection. The position of the camera is described by another, scaled matrix. The inverse of the camera position matrix provides the "eye" coordinates. - /// - /// # Focus On Focal - /// Remember, focal length affects field of view; focal distance only affects depth of field. - /// - /// # Film Size - /// In the real world, film size is measured in millimeters. In After Effects, it's measured in pixels. Multiply by 72 and divide by 25.4 to move from millimeters to pixels. - /// - /// Field of view is more complex; - /// - /// * ϴ = 1/2 field of view - /// * tan(ϴ) = 1/2 composition height / focal length - /// * focal length = 2 tan(ϴ) / composition height - CameraSuite, - AEGP_CameraSuite2, - kAEGPCameraSuite, - kAEGPCameraSuiteVersion2 -); - -impl CameraSuite { - /// Acquire this suite from the host. Returns error if the suite is not available. - /// Suite is released on drop. - pub fn new() -> Result { - crate::Suite::new() - } - - /// Given a layer handle and time, returns the current camera layer handle. - pub fn camera(&self, render_context_handle: impl AsPtr, time: Time) -> Result { - let camera_layer_handle = call_suite_fn_single!(self, AEGP_GetCamera -> ae_sys::AEGP_LayerH, render_context_handle.as_ptr(), &time as *const _ as *const ae_sys::A_Time)?; - if camera_layer_handle.is_null() { - Err(Error::Generic) - } else { - Ok(LayerHandle::from_raw(camera_layer_handle)) - } - } - - /// Given a layer, returns the camera type of the layer. - pub fn camera_type(&self, camera_layer_handle: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetCameraType -> ae_sys::AEGP_CameraType, camera_layer_handle.as_ptr())?.into()) - } - - /// Retrieves the size (and units used to measure that size) of the film used by the designated camera. - pub fn camera_film_size(&self, camera_layer_handle: impl AsPtr) -> Result<(FilmSizeUnits, f64), Error> { - let mut film_size_units: ae_sys::AEGP_FilmSizeUnits = 0; - let mut film_size: ae_sys::A_FpLong = 0.0; - - call_suite_fn!(self, AEGP_GetCameraFilmSize, camera_layer_handle.as_ptr(), &mut film_size_units, &mut film_size)?; - - Ok((film_size_units.into(), film_size)) - } - - /// Sets the size (and unites used to measure that size) of the film used by the designated camera. - pub fn set_camera_film_size(&self, camera_layer_handle: impl AsPtr, film_size_units: FilmSizeUnits, mut film_size: f64) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCameraFilmSize, camera_layer_handle.as_ptr(), film_size_units.into(), &mut film_size) - } - - /// Given a composition handle, returns the camera distance to the image plane. - pub fn default_camera_distance_to_image_plane(&self, comp_handle: &CompHandle) -> Result { - call_suite_fn_single!(self, AEGP_GetDefaultCameraDistanceToImagePlane -> f64, comp_handle.as_ptr()) - } -} - -// ――――――――――――――――――――――――――――――――――――――― Types ―――――――――――――――――――――――――――――――――――――――― - -define_enum! { - ae_sys::AEGP_FilmSizeUnits, - FilmSizeUnits { - None = ae_sys::AEGP_FilmSizeUnits_NONE, - Horizontal = ae_sys::AEGP_FilmSizeUnits_HORIZONTAL, - Vertical = ae_sys::AEGP_FilmSizeUnits_VERTICAL, - Diagonal = ae_sys::AEGP_FilmSizeUnits_DIAGONAL, - } -} - -define_enum! { - ae_sys::AEGP_CameraType, - CameraType { - None = ae_sys::AEGP_CameraType_NONE, - Perspective = ae_sys::AEGP_CameraType_PERSPECTIVE, - Orthographic = ae_sys::AEGP_CameraType_ORTHOGRAPHIC, - } -} - -define_suite_item_wrapper!( - ae_sys::AEGP_LayerH, LayerHandle, - suite: CameraSuite, - /// Obtains the camera geometry, including camera properties (type, lens, depth of field, focal distance, aperture, et cetera). - /// - /// # Notes Regarding Camera Behavior - /// Camera orientation is in composition coordinates, and the rotations are in layer (the camera's layer) coordinates. - /// - /// If the camera layer has a parent, the position is in a coordinate space relative to the parent. - /// - /// # Orthographic Camera Matrix - /// Internally, we use composition width and height to set the matrix described by the OpenGL specification as - /// ```ignore - /// glOrtho(-width/2, width/2, -height/2, height/2, -1, 100); - /// ``` - /// The orthographic matrix describes the projection. The position of the camera is described by another, scaled matrix. The inverse of the camera position matrix provides the "eye" coordinates. - /// - /// # Focus On Focal - /// Remember, focal length affects field of view; focal distance only affects depth of field. - /// - /// # Film Size - /// In the real world, film size is measured in millimeters. In After Effects, it's measured in pixels. Multiply by 72 and divide by 25.4 to move from millimeters to pixels. - /// - /// Field of view is more complex; - /// - /// * ϴ = 1/2 field of view - /// * tan(ϴ) = 1/2 composition height / focal length - /// * focal length = 2 tan(ϴ) / composition height - Camera { - dispose: ; - - /// Returns the camera type - camera_type() -> CameraType => suite.camera_type, - - /// Retrieves the size (and units used to measure that size) of the film used by the camera. - film_size() -> (FilmSizeUnits, f64) => suite.camera_film_size, - - /// Sets the size (and unites used to measure that size) of the film used by the designated camera. - set_film_size(film_size_units: FilmSizeUnits, film_size: f64) -> () => suite.set_camera_film_size, - } -); - -impl Camera { - pub fn from_render_context(render_context_handle: impl AsPtr, time: Time) -> Result { - let suite = CameraSuite::new()?; - let handle = suite.camera(render_context_handle, time)?; - Ok(Self { - suite: once_cell::sync::Lazy::new(|| CameraSuite::new()), - handle, - is_owned: false - }) - } -} +use crate::aegp::*; +use crate::*; +use ae_sys::{AEGP_LayerH, PR_RenderContextH}; + +define_suite!( + /// Obtains the camera geometry, including camera properties (type, lens, depth of field, focal distance, aperture, et cetera). + /// + /// # Notes Regarding Camera Behavior + /// Camera orientation is in composition coordinates, and the rotations are in layer (the camera's layer) coordinates. + /// + /// If the camera layer has a parent, the position is in a coordinate space relative to the parent. + /// + /// # Orthographic Camera Matrix + /// Internally, we use composition width and height to set the matrix described by the OpenGL specification as + /// ```ignore + /// glOrtho(-width/2, width/2, -height/2, height/2, -1, 100); + /// ``` + /// The orthographic matrix describes the projection. The position of the camera is described by another, scaled matrix. The inverse of the camera position matrix provides the "eye" coordinates. + /// + /// # Focus On Focal + /// Remember, focal length affects field of view; focal distance only affects depth of field. + /// + /// # Film Size + /// In the real world, film size is measured in millimeters. In After Effects, it's measured in pixels. Multiply by 72 and divide by 25.4 to move from millimeters to pixels. + /// + /// Field of view is more complex; + /// + /// * ϴ = 1/2 field of view + /// * tan(ϴ) = 1/2 composition height / focal length + /// * focal length = 2 tan(ϴ) / composition height + CameraSuite, + AEGP_CameraSuite2, + kAEGPCameraSuite, + kAEGPCameraSuiteVersion2 +); + +impl CameraSuite { + /// Acquire this suite from the host. Returns error if the suite is not available. + /// Suite is released on drop. + pub fn new() -> Result { crate::Suite::new() } + + /// Given a layer handle and time, returns the current camera layer handle. + pub fn camera( + &self, + render_context_handle: impl AsPtr, + time: Time, + ) -> Result { + let camera_layer_handle = call_suite_fn_single!(self, AEGP_GetCamera -> ae_sys::AEGP_LayerH, render_context_handle.as_ptr(), &time as *const _ as *const ae_sys::A_Time)?; + if camera_layer_handle.is_null() { + Err(Error::Generic) + } else { + Ok(LayerHandle::from_raw(camera_layer_handle)) + } + } + + /// Given a layer, returns the camera type of the layer. + pub fn camera_type( + &self, + camera_layer_handle: impl AsPtr, + ) -> Result { + Ok(call_suite_fn_single!(self, AEGP_GetCameraType -> ae_sys::AEGP_CameraType, camera_layer_handle.as_ptr())?.into()) + } + + /// Retrieves the size (and units used to measure that size) of the film used by the designated camera. + pub fn camera_film_size( + &self, + camera_layer_handle: impl AsPtr, + ) -> Result<(FilmSizeUnits, f64), Error> { + let mut film_size_units: ae_sys::AEGP_FilmSizeUnits = 0; + let mut film_size: ae_sys::A_FpLong = 0.0; + + call_suite_fn!( + self, + AEGP_GetCameraFilmSize, + camera_layer_handle.as_ptr(), + &mut film_size_units, + &mut film_size + )?; + + Ok((film_size_units.into(), film_size)) + } + + /// Sets the size (and unites used to measure that size) of the film used by the designated camera. + pub fn set_camera_film_size( + &self, + camera_layer_handle: impl AsPtr, + film_size_units: FilmSizeUnits, + mut film_size: f64, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_SetCameraFilmSize, + camera_layer_handle.as_ptr(), + film_size_units.into(), + &mut film_size + ) + } + + /// Given a composition handle, returns the camera distance to the image plane. + pub fn default_camera_distance_to_image_plane( + &self, + comp_handle: &CompHandle, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetDefaultCameraDistanceToImagePlane -> f64, comp_handle.as_ptr()) + } +} + +// ――――――――――――――――――――――――――――――――――――――― Types ―――――――――――――――――――――――――――――――――――――――― + +define_enum! { + ae_sys::AEGP_FilmSizeUnits, + FilmSizeUnits { + None = ae_sys::AEGP_FilmSizeUnits_NONE, + Horizontal = ae_sys::AEGP_FilmSizeUnits_HORIZONTAL, + Vertical = ae_sys::AEGP_FilmSizeUnits_VERTICAL, + Diagonal = ae_sys::AEGP_FilmSizeUnits_DIAGONAL, + } +} + +define_enum! { + ae_sys::AEGP_CameraType, + CameraType { + None = ae_sys::AEGP_CameraType_NONE, + Perspective = ae_sys::AEGP_CameraType_PERSPECTIVE, + Orthographic = ae_sys::AEGP_CameraType_ORTHOGRAPHIC, + } +} + +define_suite_item_wrapper!( + ae_sys::AEGP_LayerH, LayerHandle, + suite: CameraSuite, + /// Obtains the camera geometry, including camera properties (type, lens, depth of field, focal distance, aperture, et cetera). + /// + /// # Notes Regarding Camera Behavior + /// Camera orientation is in composition coordinates, and the rotations are in layer (the camera's layer) coordinates. + /// + /// If the camera layer has a parent, the position is in a coordinate space relative to the parent. + /// + /// # Orthographic Camera Matrix + /// Internally, we use composition width and height to set the matrix described by the OpenGL specification as + /// ```ignore + /// glOrtho(-width/2, width/2, -height/2, height/2, -1, 100); + /// ``` + /// The orthographic matrix describes the projection. The position of the camera is described by another, scaled matrix. The inverse of the camera position matrix provides the "eye" coordinates. + /// + /// # Focus On Focal + /// Remember, focal length affects field of view; focal distance only affects depth of field. + /// + /// # Film Size + /// In the real world, film size is measured in millimeters. In After Effects, it's measured in pixels. Multiply by 72 and divide by 25.4 to move from millimeters to pixels. + /// + /// Field of view is more complex; + /// + /// * ϴ = 1/2 field of view + /// * tan(ϴ) = 1/2 composition height / focal length + /// * focal length = 2 tan(ϴ) / composition height + Camera { + dispose: ; + + /// Returns the camera type + camera_type() -> CameraType => suite.camera_type, + + /// Retrieves the size (and units used to measure that size) of the film used by the camera. + film_size() -> (FilmSizeUnits, f64) => suite.camera_film_size, + + /// Sets the size (and unites used to measure that size) of the film used by the designated camera. + set_film_size(film_size_units: FilmSizeUnits, film_size: f64) -> () => suite.set_camera_film_size, + } +); + +impl Camera { + pub fn from_render_context( + render_context_handle: impl AsPtr, + time: Time, + ) -> Result { + let suite = CameraSuite::new()?; + let handle = suite.camera(render_context_handle, time)?; + Ok(Self { + suite: once_cell::sync::Lazy::new(|| CameraSuite::new()), + handle, + is_owned: false, + }) + } +} diff --git a/after-effects/src/aegp/suites/canvas.rs b/after-effects/src/aegp/suites/canvas.rs index 826043d3..f3783d93 100644 --- a/after-effects/src/aegp/suites/canvas.rs +++ b/after-effects/src/aegp/suites/canvas.rs @@ -1,712 +1,958 @@ -use crate::*; -use crate::aegp::*; -use ae_sys::{ PR_RenderContextH, AEGP_RenderLayerContextH }; - -define_suite!( - /// [`render_texture()`](Self::render_texture) supplies the raw pixels of a layer, untransformed, into an arbitrarily-sized buffer. - /// - /// [`render_layer_plus()`](Self::render_layer_plus) invokes the entire After Effects render pipeline, including transforms, masking, et cetera, providing the layer as it appears in its composition, in a composition-sized buffer. - /// - /// If the layer being rendered is 3D, the default (Standard 3D) Artisan is invoked to perform any 3D geometrics. - /// - /// Your Artisan can use this to render track matte layers, and apply them only in a strictly 2D sense, to the transformed 3D layer. - /// - /// Before rendering, the Artisans that ship with After Effects apply an inverse transform to get square pixels, then re-apply the transform before display. - /// - /// For example, if the pixel aspect ratio is 10/11 (DV NTSC), we multiply by 11/10 to get square pixels. We process and composite 3D layers, then re-divide to get back to the original pixel aspect ratio. - /// - /// The following suite supplies the layers, compositions, texture and destination buffers. This is a vital suite for all artisans. - CanvasSuite, - AEGP_CanvasSuite8, - kAEGPCanvasSuite, - kAEGPCanvasSuiteVersion8 -); - -impl CanvasSuite { - /// Acquire this suite from the host. Returns error if the suite is not available. - /// Suite is released on drop. - pub fn new() -> Result { - crate::Suite::new() - } - - /// Given the render context provided to the Artisan at render time, returns a handle to the composition. - pub fn comp_to_render(&self, render_ctx: impl AsPtr) -> Result { - Ok(CompHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetCompToRender -> ae_sys::AEGP_CompH, render_ctx.as_ptr())? - )) - } - - /// Given the render context, returns the number of layers the Artisan needs to render. - pub fn num_layers_to_render(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetNumLayersToRender -> i32, render_ctx.as_ptr())? as u32) - } - - /// Used to build a list of layers to render after determining the total number of layers that need rendering by the Artisan. - pub fn nth_layer_context_to_render(&self, render_ctx: impl AsPtr, n: u32) -> Result { - Ok(RenderLayerContextHandle::from_raw( - call_suite_fn_single!(self, - AEGP_GetNthLayerContextToRender -> ae_sys::AEGP_RenderLayerContextH, - render_ctx.as_ptr(), - n as i32 - )?, - )) - } - - /// Given a [`RenderContextHandle`](pr::RenderContextHandle), retrieves the associated [`LayerHandle`] (required by many suite functions). - pub fn layer_from_layer_context(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr) -> Result { - Ok(LayerHandle::from_raw( - call_suite_fn_single!(self, - AEGP_GetLayerFromLayerContext -> ae_sys::AEGP_LayerH, - render_ctx.as_ptr(), - layer_ctx.as_ptr() - )? - )) - } - - /// Allows for rendering of sub-layers (as within a Photoshop file). - pub fn layer_and_sub_layer_from_layer_context(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr) -> Result<(LayerHandle, u32), Error> { - let (layer_handle, sub_layer) = call_suite_fn_double!(self, - AEGP_GetLayerAndSubLayerFromLayerContext -> ae_sys::AEGP_LayerH, ae_sys::AEGP_SubLayerIndex, - render_ctx.as_ptr(), - layer_ctx.as_ptr() - )?; - Ok(( - LayerHandle::from_raw(layer_handle), - sub_layer as u32 - )) - } - - /// With collapsed geometrics "on" this gives the layer in the root composition containing the layer context. - /// - /// With collapsed geometrics off this is the same as [`layer_from_layer_context()`](Self::layer_from_layer_context). - pub fn top_layer_from_layer_context(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr) -> Result { - Ok(LayerHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetTopLayerFromLayerContext -> ae_sys::AEGP_LayerH, render_ctx.as_ptr(), layer_ctx.as_ptr())? - )) - } - - /// Given the render context, returns the current point in (composition) time to render. - pub fn comp_render_time(&self, render_ctx: impl AsPtr) -> Result<(Time, Time), Error> { - let (shutter_frame_start, shutter_frame_duration) = - call_suite_fn_double!(self, AEGP_GetCompRenderTime -> ae_sys::A_Time, ae_sys::A_Time, render_ctx.as_ptr())?; - - Ok((shutter_frame_start.into(), shutter_frame_duration.into())) - } - - /// Given the render context, returns a buffer in which to place the final rendered output. - pub fn comp_destination_buffer(&self, render_ctx: impl AsPtr, comp_handle: CompHandle) -> Result { - Ok(WorldHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetCompDestinationBuffer -> ae_sys::AEGP_WorldH, render_ctx.as_ptr(), comp_handle.as_ptr())? - )) - } - - /// Given the render context provided to the Artisan at render time, returns a handle to the composition. - pub fn region_of_interest(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetROI -> ae_sys::A_LegacyRect, render_ctx.as_ptr())?.into()) - } - - /// Given the render context and layer, returns the layer texture. - /// - /// The returned [`WorldHandle`] can be null. - /// - /// [`RenderHints::NoTransferMode`] prevents application of opacity & transfer mode; for use with `RenderLayer` calls. - pub fn render_texture(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, render_hints: RenderHints, suggested_scale: Option, suggested_src_rect: Option, src_matrix: Option) -> Result { - Ok(WorldHandle::from_raw( - call_suite_fn_single!(self, - AEGP_RenderTexture -> ae_sys::AEGP_WorldH, - render_ctx.as_ptr(), - layer_ctx.as_ptr(), - render_hints.into(), - suggested_scale .map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x), - suggested_src_rect.map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x), - src_matrix .map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x) - )? - )) - } - - /// Disposes of an acquired layer texture. - pub fn dispose_texture(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, world_handle: WorldHandle) -> Result<(), Error> { - call_suite_fn!(self, AEGP_DisposeTexture, render_ctx.as_ptr(), layer_ctx.as_ptr(), world_handle.as_ptr()) - } - - /// Returns the field settings of the given [`RenderContextHandle`](pr::RenderContextHandle). - pub fn field_render(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetFieldRender -> ae_sys::PF_Field, render_ctx.as_ptr())?) - } - - /// Given the render context provided to the Artisan at render time, returns a handle to the composition. - /// - /// Note: this is NOT thread-safe on macOS; only use this function when the current thread ID is 0. - pub fn report_artisan_progress(&self, render_ctx: impl AsPtr, count: i32, total: i32) -> Result<(), Error> { - call_suite_fn!(self, AEGP_ReportArtisanProgress, render_ctx.as_ptr(), count, total) - } - - /// Returns the downsample factor of the [`RenderContextHandle`](pr::RenderContextHandle). - pub fn render_downsample_factor(&self, render_ctx: impl AsPtr) -> Result { - let dsf = call_suite_fn_single!(self, AEGP_GetRenderDownsampleFactor -> ae_sys::AEGP_DownsampleFactor, render_ctx.as_ptr())?; - Ok(dsf.into()) - } - pub fn set_render_downsample_factor(&self, render_ctx: impl AsPtr, mut dsf: ae_sys::AEGP_DownsampleFactor) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetRenderDownsampleFactor, render_ctx.as_ptr(), &mut dsf as *mut _) - } - - /// Determines whether the [`RenderContextHandle`](pr::RenderContextHandle) is blank (empty). - pub fn is_blank_canvas(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_IsBlankCanvas -> ae_sys::A_Boolean, render_ctx.as_ptr())? != 0) - } - - /// Given a render context and a layer (at a given time), retrieves the 4 by 4 transform to move between their coordinate spaces. - pub fn render_layer_to_world_xform(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, comp_time: Time) -> Result { - let matrix = call_suite_fn_single!(self, AEGP_GetRenderLayerToWorldXform -> ae_sys::A_Matrix4, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)?; - Ok(matrix.into()) - } - - /// Retrieves the bounding rectangle of the layer_contextH (at a given time) within the [`RenderContextHandle`](pr::RenderContextHandle). - pub fn render_layer_bounds(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, comp_time: Time) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetRenderLayerBounds -> ae_sys::A_LegacyRect, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)?.into()) - } - - /// Returns the opacity of the given layer context at the given time, within the render context. - pub fn render_opacity(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, comp_time: Time) -> Result { - call_suite_fn_single!(self, AEGP_GetRenderOpacity -> f64, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _) - } - - /// Returns whether or not a given layer context is active within the render context, at the given time. - pub fn is_render_layer_active(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, comp_time: Time) -> Result { - Ok(call_suite_fn_single!(self, AEGP_IsRenderLayerActive -> ae_sys::A_Boolean, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)? != 0) - } - - /// Sets the progress information for a rendering Artisan. - /// - /// * `count` is the number of layers completed - /// * `num_layers` is the total number of layers the Artisan is rendering - pub fn set_artisan_layer_progress(&self, render_ctx: impl AsPtr, count: i32, num_layers: i32) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetArtisanLayerProgress, render_ctx.as_ptr(), count, num_layers) - } - - /// Invokes the entire After Effects render pipeline, including transforms, masking, et cetera, - /// providing the layer as it appears in its composition, in a composition-sized buffer. - pub fn render_layer_plus(&self, render_ctx: impl AsPtr, layer_handle: LayerHandle, layer_ctx: impl AsPtr, render_hints: RenderHints) -> Result { - Ok(WorldHandle::from_raw( - call_suite_fn_single!(self, AEGP_RenderLayerPlus -> ae_sys::AEGP_WorldH, render_ctx.as_ptr(), layer_handle.as_ptr(), layer_ctx.as_ptr(), render_hints.into())? - )) - } - - /// Retrieves the [`RenderLayerContextHandle`] for the specified render and fill contexts. - pub fn track_matte_context(&self, render_ctx: impl AsPtr, fill_ctx: RenderLayerContextHandle) -> Result { - Ok(RenderLayerContextHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetTrackMatteContext -> ae_sys::AEGP_RenderLayerContextH, render_ctx.as_ptr(), fill_ctx.as_ptr())? - )) - } - - /// Renders a texture into an [`WorldHandle`], and provides an [`RenderReceiptHandle`] for the operation. - pub fn render_texture_with_receipt(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, render_hints: RenderHints, num_effects: RenderNumEffects, suggested_scale: Option, suggested_src_rect: Option, src_matrix: Option) -> Result<(RenderReceiptHandle, WorldHandle), Error> { - let suggested_scale = suggested_scale .map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x); - let suggested_src_rect = suggested_src_rect.map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x); - let src_matrix = src_matrix .map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x); - let (receipt, world) = call_suite_fn_double!(self, - AEGP_RenderTextureWithReceipt -> ae_sys::AEGP_RenderReceiptH, ae_sys::AEGP_WorldH, - render_ctx.as_ptr(), - layer_ctx.as_ptr(), - render_hints.into(), - num_effects.into(), - suggested_scale, - suggested_src_rect, - src_matrix - )?; - Ok(( - RenderReceiptHandle::from_raw(receipt), - WorldHandle::from_raw(world) - )) - } - - /// Returns the number of software effects applied in the given [`RenderLayerContextHandle`]. - pub fn number_of_software_effects(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetNumberOfSoftwareEffects -> i16, render_ctx.as_ptr(), layer_ctx.as_ptr())? as i32) - } - - /// An improvement over [`render_layer_plus()`](Self::render_layer_plus), this function also provides an [`RenderReceiptHandle`] for caching purposes. - pub fn render_layer_plus_with_receipt(&self, render_ctx: impl AsPtr, layer_handle: LayerHandle, layer_ctx: impl AsPtr, render_hints: RenderHints) -> Result<(RenderReceiptHandle, WorldHandle), Error> { - let (receipt, world) = call_suite_fn_double!(self, - AEGP_RenderLayerPlusWithReceipt -> ae_sys::AEGP_RenderReceiptH, ae_sys::AEGP_WorldH, - render_ctx.as_ptr(), - layer_handle.as_ptr(), - layer_ctx.as_ptr(), - render_hints.into() - )?; - - Ok(( - RenderReceiptHandle::from_raw(receipt), - WorldHandle::from_raw(world)) - ) - } - - /// Frees an [`ae_sys::AEGP_RenderReceiptH`] - /// - /// This is called automatically on [`RenderReceiptHandle::drop()`] - pub fn dispose_render_receipt(&self, render_receipt_handle: ae_sys::AEGP_RenderReceiptH) -> Result<(), Error> { - call_suite_fn!(self, AEGP_DisposeRenderReceipt, render_receipt_handle) - } - - /// Checks with After Effects' internal caching to determine whether a given [`RenderReceiptHandle`] is still valid. - pub fn check_render_receipt(&self, current_render_ctx: impl AsPtr, current_layer_ctx: impl AsPtr, old_render_receipt_handle: RenderReceiptHandle, check_geometrics: bool, num_effects: RenderNumEffects) -> Result { - Ok(call_suite_fn_single!(self, - AEGP_CheckRenderReceipt -> ae_sys::AEGP_RenderReceiptStatus, - current_render_ctx.as_ptr(), - current_layer_ctx.as_ptr(), - old_render_receipt_handle.as_ptr(), - check_geometrics as i32 as _, - num_effects.into() - )?.into()) - } - - /// Generates a [`RenderReceiptHandle`] for a layer as if the first `num_effects` have been rendered. - pub fn generate_render_receipt(&self, current_render_ctx: impl AsPtr, current_layer_ctx: impl AsPtr, num_effects: RenderNumEffects) -> Result { - Ok(RenderReceiptHandle::from_raw( - call_suite_fn_single!(self, - AEGP_GenerateRenderReceipt -> ae_sys::AEGP_RenderReceiptH, - current_render_ctx.as_ptr(), - current_layer_ctx.as_ptr(), - num_effects.into() - )? - )) - } - - /// Returns the number of bins After Effects wants the artisan to render. - pub fn num_bins_to_render(&self, render_ctx: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetNumBinsToRender -> i32, render_ctx.as_ptr()) - } - - /// Sets the given render context to be the n-th bin to be rendered by After Effects. - pub fn set_nth_bin(&self, render_ctx: impl AsPtr, n: i32) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetNthBin, render_ctx.as_ptr(), n) - } - - /// Retrieves the type of the given bin. - pub fn bin_type(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, - AEGP_GetBinType -> ae_sys::AEGP_BinType, - render_ctx.as_ptr() - )?.into()) - } - - /// Retrieves the transform to correctly orient the layer being rendered with the output world. - /// - /// Pass `true` for `only_2dB` to constrain the transform to two dimensions. - pub fn render_layer_to_world_xform_2d_3d(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, comp_time: Time, only_2d: bool) -> Result { - Ok(call_suite_fn_single!(self, - AEGP_GetRenderLayerToWorldXform2D3D -> ae_sys::A_Matrix4, - render_ctx.as_ptr(), - layer_ctx.as_ptr(), - &comp_time.into() as *const _, - if only_2d { 1 } else { 0 } - )?.into()) - } - - /// Retrieves the platform-specific window context into which to draw the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn platform_window_ref(&self, render_ctx: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetPlatformWindowRef -> ae_sys::AEGP_PlatformWindowRef, render_ctx.as_ptr()) - } - - /// Retrieves the source-to-frame downsample factor for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn viewport_scale(&self, render_ctx: impl AsPtr) -> Result<(f64, f64), Error> { - call_suite_fn_double!(self, AEGP_GetViewportScale -> f64, f64, render_ctx.as_ptr()) - } - - /// Retrieves to origin of the source, within the frame (necessary to translate between the two), for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn viewport_origin(&self, render_ctx: impl AsPtr) -> Result<(i32, i32), Error> { - call_suite_fn_double!(self, AEGP_GetViewportOrigin -> i32, i32, render_ctx.as_ptr()) - } - - /// Retrieves the bounding rectangle for the area to be drawn, for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn viewport_rect(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetViewportRect -> ae_sys::A_LegacyRect, render_ctx.as_ptr())?.into()) - } - - /// Retrieves the color used for the fallow regions in the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn fallow_color(&self, render_ctx: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetFallowColor -> ae_sys::PF_Pixel8, render_ctx.as_ptr()) - } - - pub fn interactive_buffer(&self, render_ctx: impl AsPtr) -> Result { - Ok(WorldHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetInteractiveBuffer -> ae_sys::AEGP_WorldH, render_ctx.as_ptr())? - )) - } - - /// Retrieves whether or not the checkerboard is currently active for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn interactive_checkerboard(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetInteractiveCheckerboard -> ae_sys::A_Boolean, render_ctx.as_ptr())? != 0) - } - - /// Retrieves the colors used in the checkerboard. - /// - /// This function is valid for interactive artisans only. - pub fn interactive_checkerboard_colors(&self, render_ctx: impl AsPtr) -> Result<(Pixel8, Pixel8), Error> { - let (px1, px2) = call_suite_fn_double!(self, AEGP_GetInteractiveCheckerboardColors -> ae_sys::PF_Pixel, ae_sys::PF_Pixel, render_ctx.as_ptr())?; - Ok(( - px1.into(), - px2.into() - )) - } - - /// Retrieves the width and height of one checkerboard square. - /// - /// This function is valid for interactive artisans only. - pub fn interactive_checkerboard_size(&self, render_ctx: impl AsPtr) -> Result<(u32, u32), Error> { - call_suite_fn_double!(self, AEGP_GetInteractiveCheckerboardSize -> u32, u32, render_ctx.as_ptr()) - } - - /// Retrieves the cached AEGP_WorldH last used for the [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn interactive_cached_buffer(&self, render_ctx: impl AsPtr) -> Result { - Ok(WorldHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetInteractiveCachedBuffer -> ae_sys::AEGP_WorldH, render_ctx.as_ptr())? - )) - } - - /// Determines whether or not the artisan must render the current [`RenderLayerContextHandle`] as a layer. - /// - /// This function is valid for interactive artisans only. - pub fn artisan_must_render_as_layer(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_ArtisanMustRenderAsLayer -> ae_sys::A_Boolean, render_ctx.as_ptr(), layer_ctx.as_ptr())? != 0) - } - - /// Returns which channels should be displayed by the interactive artisan. - /// - /// This function is valid for interactive artisans only. - pub fn interactive_display_channel(&self, render_ctx: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetInteractiveDisplayChannel -> ae_sys::AEGP_DisplayChannelType, render_ctx.as_ptr())?.into()) - } - - /// Returns the exposure for the given [`RenderContextHandle`](pr::RenderContextHandle), expressed as a floating point number. - /// - /// This function is valid for interactive artisans only. - pub fn interactive_exposure(&self, render_ctx: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetInteractiveExposure -> f64, render_ctx.as_ptr()) - } - - // TODO: what's xform? - /// Returns the color transform for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn color_transform(&self, render_ctx: impl AsPtr, xform: *mut std::ffi::c_void) -> Result<(bool, u32), Error> { - let mut cms_on = 0; - let mut xform_key = 0; - call_suite_fn!(self, AEGP_GetColorTransform, render_ctx.as_ptr(), &mut cms_on, &mut xform_key, xform)?; - Ok((cms_on != 0, xform_key)) - } - - /// Returns the shutter angle for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - pub fn comp_shutter_time(&self, render_ctx: impl AsPtr) -> Result<(Time, Time), Error> { - let (shutter_time, shutter_dur) = call_suite_fn_double!(self, - AEGP_GetCompShutterTime -> ae_sys::A_Time, ae_sys::A_Time, - render_ctx.as_ptr() - )?; - Ok((shutter_time.into(), shutter_dur.into())) - } - - /// New in CC. Unlike [`suites::Layer::convert_comp_to_layer_time()`](aegp::suites::Layer::convert_comp_to_layer_time), this handles time remapping with collapsed or nested comps. - /// - /// This function is valid for interactive artisans only. - pub fn map_comp_to_layer_time(&self, render_ctx: impl AsPtr, layer_ctx: impl AsPtr, comp_time: Time) -> Result { - Ok(call_suite_fn_single!(self, AEGP_MapCompToLayerTime -> ae_sys::A_Time, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)?.into()) - } -} - -// ――――――――――――――――――――――――――――――――――――――― Types ―――――――――――――――――――――――――――――――――――――――― - -register_handle!(AEGP_RenderLayerContextH); -define_handle_wrapper!(RenderLayerContextHandle, AEGP_RenderLayerContextH); - -define_enum! { - ae_sys::AEGP_RenderHints, - RenderHints { - None = ae_sys::AEGP_RenderHints_NONE, - IgnoreExtents = ae_sys::AEGP_RenderHints_IGNORE_EXTENTS, - NoTransferMode = ae_sys::AEGP_RenderHints_NO_TRANSFER_MODE, - } -} - -define_enum! { - ae_sys::AEGP_BinType, - BinType { - None = ae_sys::AEGP_BinType_NONE, - TwoD = ae_sys::AEGP_BinType_2D, - ThreeD = ae_sys::AEGP_BinType_3D, - } -} - -define_enum! { - ae_sys::AEGP_DisplayChannelType, - DisplayChannel { - None = ae_sys::AEGP_DisplayChannel_NONE, - Red = ae_sys::AEGP_DisplayChannel_RED, - Green = ae_sys::AEGP_DisplayChannel_GREEN, - Blue = ae_sys::AEGP_DisplayChannel_BLUE, - Alpha = ae_sys::AEGP_DisplayChannel_ALPHA, - RedAlt = ae_sys::AEGP_DisplayChannel_RED_ALT, - GreenAlt = ae_sys::AEGP_DisplayChannel_GREEN_ALT, - BlueAlt = ae_sys::AEGP_DisplayChannel_BLUE_ALT, - AlphaAlt = ae_sys::AEGP_DisplayChannel_ALPHA_ALT, - } -} - -pub enum RenderNumEffects { - AllEffects, - NumEffects(u16) -} -impl Into for RenderNumEffects { - fn into(self) -> i16 { - match self { - RenderNumEffects::AllEffects => -1, - RenderNumEffects::NumEffects(x) => x as i16 - } - } -} - -define_enum! { - ae_sys::AEGP_RenderReceiptStatus, - RenderReceiptStatus { - Invalid = ae_sys::AEGP_RenderReceiptStatus_INVALID, - Valid = ae_sys::AEGP_RenderReceiptStatus_VALID, - ValidButIncomplete = ae_sys::AEGP_RenderReceiptStatus_VALID_BUT_INCOMPLETE, - } -} - -#[derive(Clone, Debug, Hash)] -pub struct RenderReceiptHandle(after_effects_sys::AEGP_RenderReceiptH); -impl RenderReceiptHandle { - pub fn from_raw(raw_handle: after_effects_sys::AEGP_RenderReceiptH) -> Self { Self(raw_handle) } - pub fn as_ptr(&self) -> after_effects_sys::AEGP_RenderReceiptH { self.0 } -} -impl Drop for RenderReceiptHandle { - fn drop(&mut self) { - if let Ok(s) = CanvasSuite::new() { - let _ = s.dispose_render_receipt(self.0); - } - } -} - -define_suite_item_wrapper!( - ae_sys::PR_RenderContextH, pr::RenderContextHandle, - suite: CanvasSuite, - /// [`render_texture()`](Self::render_texture) supplies the raw pixels of a layer, untransformed, into an arbitrarily-sized buffer. - /// - /// [`render_layer_plus()`](Self::render_layer_plus) invokes the entire After Effects render pipeline, including transforms, masking, et cetera, providing the layer as it appears in its composition, in a composition-sized buffer. - /// - /// If the layer being rendered is 3D, the default (Standard 3D) Artisan is invoked to perform any 3D geometrics. - /// - /// Your Artisan can use this to render track matte layers, and apply them only in a strictly 2D sense, to the transformed 3D layer. - /// - /// Before rendering, the Artisans that ship with After Effects apply an inverse transform to get square pixels, then re-apply the transform before display. - /// - /// For example, if the pixel aspect ratio is 10/11 (DV NTSC), we multiply by 11/10 to get square pixels. We process and composite 3D layers, then re-divide to get back to the original pixel aspect ratio. - /// - /// The following suite supplies the layers, compositions, texture and destination buffers. This is a vital suite for all artisans. - Canvas { - dispose: ; - - /// Given the render context provided to the Artisan at render time, returns a handle to the composition. - comp_to_render() -> CompHandle => suite.comp_to_render, - - /// Given the render context, returns the number of layers the Artisan needs to render. - num_layers_to_render() -> u32 => suite.num_layers_to_render, - - /// Used to build a list of layers to render after determining the total number of layers that need rendering by the Artisan. - nth_layer_context_to_render(n: u32) -> RenderLayerContextHandle => suite.nth_layer_context_to_render, - - /// Given a [`RenderContextHandle`](pr::RenderContextHandle), retrieves the associated [`LayerHandle`] (required by many suite functions). - layer_from_layer_context(layer_ctx: impl AsPtr) -> LayerHandle => suite.layer_from_layer_context, - - /// Allows for rendering of sub-layers (as within a Photoshop file). - layer_and_sub_layer_from_layer_context(layer_ctx: impl AsPtr) -> (LayerHandle, u32) => suite.layer_and_sub_layer_from_layer_context, - - /// With collapsed geometrics "on" this gives the layer in the root composition containing the layer context. - /// - /// With collapsed geometrics off this is the same as [`layer_from_layer_context()`](Self::layer_from_layer_context). - top_layer_from_layer_context(layer_ctx: impl AsPtr) -> LayerHandle => suite.top_layer_from_layer_context, - - /// Given the render context, returns the current point in (composition) time to render. - comp_render_time() -> (Time, Time) => suite.comp_render_time, - - /// Given the render context, returns a buffer in which to place the final rendered output. - comp_destination_buffer(comp_handle: CompHandle) -> WorldHandle => suite.comp_destination_buffer, - - /// Given the render context provided to the Artisan at render time, returns a handle to the composition. - region_of_interest() -> Rect => suite.region_of_interest, - - /// Given the render context and layer, returns the layer texture. - /// - /// The returned [`WorldHandle`] can be null. - /// - /// [`RenderHints::NoTransferMode`] prevents application of opacity & transfer mode; for use with `RenderLayer` calls. - render_texture(layer_ctx: impl AsPtr, render_hints: RenderHints, suggested_scale: Option, suggested_src_rect: Option, src_matrix: Option) -> WorldHandle => suite.render_texture, - - /// Disposes of an acquired layer texture. - dispose_texture(layer_ctx: impl AsPtr, world_handle: WorldHandle) -> () => suite.dispose_texture, - - /// Returns the field settings of the given [`RenderContextHandle`](pr::RenderContextHandle). - field_render() -> ae_sys::PF_Field => suite.field_render, - - /// Given the render context provided to the Artisan at render time, returns a handle to the composition. - /// - /// Note: this is NOT thread-safe on macOS; only use this function when the current thread ID is 0. - report_artisan_progress(count: i32, total: i32) -> () => suite.report_artisan_progress, - - /// Returns the downsample factor of the [`RenderContextHandle`](pr::RenderContextHandle). - render_downsample_factor() -> ae_sys::AEGP_DownsampleFactor => suite.render_downsample_factor, - - set_render_downsample_factor(dsf: ae_sys::AEGP_DownsampleFactor) -> () => suite.set_render_downsample_factor, - - /// Determines whether the [`RenderContextHandle`](pr::RenderContextHandle) is blank (empty). - is_blank_canvas() -> bool => suite.is_blank_canvas, - - /// Given a render context and a layer (at a given time), retrieves the 4 by 4 transform to move between their coordinate spaces. - render_layer_to_world_xform(layer_ctx: impl AsPtr, comp_time: Time) -> Matrix4 => suite.render_layer_to_world_xform, - - /// Retrieves the bounding rectangle of the layer_contextH (at a given time) within the [`RenderContextHandle`](pr::RenderContextHandle). - render_layer_bounds(layer_ctx: impl AsPtr, comp_time: Time) -> Rect => suite.render_layer_bounds, - - /// Returns the opacity of the given layer context at the given time, within the render context. - render_opacity(layer_ctx: impl AsPtr, comp_time: Time) -> f64 => suite.render_opacity, - - /// Returns whether or not a given layer context is active within the render context, at the given time. - is_render_layer_active(layer_ctx: impl AsPtr, comp_time: Time) -> bool => suite.is_render_layer_active, - - /// Sets the progress information for a rendering Artisan. - /// - /// * `count` is the number of layers completed - /// * `num_layers` is the total number of layers the Artisan is rendering - set_artisan_layer_progress(count: i32, num_layers: i32) -> () => suite.set_artisan_layer_progress, - - /// Invokes the entire After Effects render pipeline, including transforms, masking, et cetera, - /// providing the layer as it appears in its composition, in a composition-sized buffer. - render_layer_plus(layer_handle: LayerHandle, layer_ctx: impl AsPtr, render_hints: RenderHints) -> WorldHandle => suite.render_layer_plus, - - /// Retrieves the [`RenderLayerContextHandle`] for the specified render and fill contexts. - track_matte_context(fill_ctx: RenderLayerContextHandle) -> RenderLayerContextHandle => suite.track_matte_context, - - /// Renders a texture into an [`WorldHandle`], and provides an [`RenderReceiptHandle`] for the operation. - render_texture_with_receipt(layer_ctx: impl AsPtr, render_hints: RenderHints, num_effects: RenderNumEffects, suggested_scale: Option, suggested_src_rect: Option, src_matrix: Option) -> (RenderReceiptHandle, WorldHandle) => suite.render_texture_with_receipt, - - /// Returns the number of software effects applied in the given [`RenderLayerContextHandle`]. - number_of_software_effects(layer_ctx: impl AsPtr) -> i32 => suite.number_of_software_effects, - - /// An improvement over [`render_layer_plus()`](Self::render_layer_plus), this function also provides an [`RenderReceiptHandle`] for caching purposes. - render_layer_plus_with_receipt(layer_handle: LayerHandle, layer_ctx: impl AsPtr, render_hints: RenderHints) -> (RenderReceiptHandle, WorldHandle) => suite.render_layer_plus_with_receipt, - - /// Returns the number of bins After Effects wants the artisan to render. - num_bins_to_render() -> i32 => suite.num_bins_to_render, - - /// Sets the given render context to be the n-th bin to be rendered by After Effects. - set_nth_bin(n: i32) -> () => suite.set_nth_bin, - - /// Retrieves the type of the given bin. - bin_type() -> BinType => suite.bin_type, - - /// Retrieves the transform to correctly orient the layer being rendered with the output world. - /// - /// Pass `true` for `only_2dB` to constrain the transform to two dimensions. - render_layer_to_world_xform_2d_3d(layer_ctx: impl AsPtr, comp_time: Time, only_2d: bool) -> Matrix4 => suite.render_layer_to_world_xform_2d_3d, - - /// Retrieves the platform-specific window context into which to draw the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - platform_window_ref() -> ae_sys::AEGP_PlatformWindowRef => suite.platform_window_ref, - - /// Retrieves the source-to-frame downsample factor for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - viewport_scale() -> (f64, f64) => suite.viewport_scale, - - /// Retrieves to origin of the source, within the frame (necessary to translate between the two), for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - viewport_origin() -> (i32, i32) => suite.viewport_origin, - - /// Retrieves the bounding rectangle for the area to be drawn, for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - viewport_rect() -> Rect => suite.viewport_rect, - - /// Retrieves the color used for the fallow regions in the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - fallow_color() -> ae_sys::PF_Pixel8 => suite.fallow_color, - - interactive_buffer() -> WorldHandle => suite.interactive_buffer, - - /// Retrieves whether or not the checkerboard is currently active for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - interactive_checkerboard() -> bool => suite.interactive_checkerboard, - - /// Retrieves the colors used in the checkerboard. - /// - /// This function is valid for interactive artisans only. - interactive_checkerboard_colors() -> (Pixel8, Pixel8) => suite.interactive_checkerboard_colors, - - /// Retrieves the width and height of one checkerboard square. - /// - /// This function is valid for interactive artisans only. - interactive_checkerboard_size() -> (u32, u32) => suite.interactive_checkerboard_size, - - /// Retrieves the cached AEGP_WorldH last used for the [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - interactive_cached_buffer() -> WorldHandle => suite.interactive_cached_buffer, - - /// Determines whether or not the artisan must render the current [`RenderLayerContextHandle`] as a layer. - /// - /// This function is valid for interactive artisans only. - artisan_must_render_as_layer(layer_ctx: impl AsPtr) -> bool => suite.artisan_must_render_as_layer, - - /// Returns which channels should be displayed by the interactive artisan. - /// - /// This function is valid for interactive artisans only. - interactive_display_channel() -> DisplayChannel => suite.interactive_display_channel, - - /// Returns the exposure for the given [`RenderContextHandle`](pr::RenderContextHandle), expressed as a floating point number. - /// - /// This function is valid for interactive artisans only. - interactive_exposure() -> f64 => suite.interactive_exposure, - - // TODO: what's xform? - /// Returns the color transform for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - color_transform(xform: *mut std::ffi::c_void) -> (bool, u32) => suite.color_transform, - - /// Returns the shutter angle for the given [`RenderContextHandle`](pr::RenderContextHandle). - /// - /// This function is valid for interactive artisans only. - comp_shutter_time() -> (Time, Time) => suite.comp_shutter_time, - - /// New in CC. Unlike [`suites::Layer::convert_comp_to_layer_time()`](aegp::suites::Layer::convert_comp_to_layer_time), this handles time remapping with collapsed or nested comps. - /// - /// This function is valid for interactive artisans only. - map_comp_to_layer_time(layer_ctx: impl AsPtr, comp_time: Time) -> Time => suite.map_comp_to_layer_time, - } -); - -impl Canvas { - // dispose_render_receipt(&self, render_receipt_handle: ae_sys::AEGP_RenderReceiptH) -> () => suite. - // check_render_receipt(&self, current_render_ctx: impl AsPtr, current_layer_ctx: impl AsPtr, old_render_receipt_handle: RenderReceiptHandle, check_geometrics: bool, num_effects: RenderNumEffects) -> RenderReceiptStatus => suite. - // generate_render_receipt(&self, current_render_ctx: impl AsPtr, current_layer_ctx: impl AsPtr, num_effects: RenderNumEffects) -> RenderReceiptHandle => suite. -} +use crate::aegp::*; +use crate::*; +use ae_sys::{AEGP_RenderLayerContextH, PR_RenderContextH}; + +define_suite!( + /// [`render_texture()`](Self::render_texture) supplies the raw pixels of a layer, untransformed, into an arbitrarily-sized buffer. + /// + /// [`render_layer_plus()`](Self::render_layer_plus) invokes the entire After Effects render pipeline, including transforms, masking, et cetera, providing the layer as it appears in its composition, in a composition-sized buffer. + /// + /// If the layer being rendered is 3D, the default (Standard 3D) Artisan is invoked to perform any 3D geometrics. + /// + /// Your Artisan can use this to render track matte layers, and apply them only in a strictly 2D sense, to the transformed 3D layer. + /// + /// Before rendering, the Artisans that ship with After Effects apply an inverse transform to get square pixels, then re-apply the transform before display. + /// + /// For example, if the pixel aspect ratio is 10/11 (DV NTSC), we multiply by 11/10 to get square pixels. We process and composite 3D layers, then re-divide to get back to the original pixel aspect ratio. + /// + /// The following suite supplies the layers, compositions, texture and destination buffers. This is a vital suite for all artisans. + CanvasSuite, + AEGP_CanvasSuite8, + kAEGPCanvasSuite, + kAEGPCanvasSuiteVersion8 +); + +impl CanvasSuite { + /// Acquire this suite from the host. Returns error if the suite is not available. + /// Suite is released on drop. + pub fn new() -> Result { crate::Suite::new() } + + /// Given the render context provided to the Artisan at render time, returns a handle to the composition. + pub fn comp_to_render( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok(CompHandle::from_raw( + call_suite_fn_single!(self, AEGP_GetCompToRender -> ae_sys::AEGP_CompH, render_ctx.as_ptr())?, + )) + } + + /// Given the render context, returns the number of layers the Artisan needs to render. + pub fn num_layers_to_render( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_GetNumLayersToRender -> i32, render_ctx.as_ptr())? + as u32, + ) + } + + /// Used to build a list of layers to render after determining the total number of layers that need rendering by the Artisan. + pub fn nth_layer_context_to_render( + &self, + render_ctx: impl AsPtr, + n: u32, + ) -> Result { + Ok(RenderLayerContextHandle::from_raw( + call_suite_fn_single!(self, + AEGP_GetNthLayerContextToRender -> ae_sys::AEGP_RenderLayerContextH, + render_ctx.as_ptr(), + n as i32 + )?, + )) + } + + /// Given a [`RenderContextHandle`](pr::RenderContextHandle), retrieves the associated [`LayerHandle`] (required by many suite functions). + pub fn layer_from_layer_context( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + ) -> Result { + Ok(LayerHandle::from_raw(call_suite_fn_single!(self, + AEGP_GetLayerFromLayerContext -> ae_sys::AEGP_LayerH, + render_ctx.as_ptr(), + layer_ctx.as_ptr() + )?)) + } + + /// Allows for rendering of sub-layers (as within a Photoshop file). + pub fn layer_and_sub_layer_from_layer_context( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + ) -> Result<(LayerHandle, u32), Error> { + let (layer_handle, sub_layer) = call_suite_fn_double!(self, + AEGP_GetLayerAndSubLayerFromLayerContext -> ae_sys::AEGP_LayerH, ae_sys::AEGP_SubLayerIndex, + render_ctx.as_ptr(), + layer_ctx.as_ptr() + )?; + Ok((LayerHandle::from_raw(layer_handle), sub_layer as u32)) + } + + /// With collapsed geometrics "on" this gives the layer in the root composition containing the layer context. + /// + /// With collapsed geometrics off this is the same as [`layer_from_layer_context()`](Self::layer_from_layer_context). + pub fn top_layer_from_layer_context( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + ) -> Result { + Ok(LayerHandle::from_raw( + call_suite_fn_single!(self, AEGP_GetTopLayerFromLayerContext -> ae_sys::AEGP_LayerH, render_ctx.as_ptr(), layer_ctx.as_ptr())?, + )) + } + + /// Given the render context, returns the current point in (composition) time to render. + pub fn comp_render_time( + &self, + render_ctx: impl AsPtr, + ) -> Result<(Time, Time), Error> { + let (shutter_frame_start, shutter_frame_duration) = call_suite_fn_double!(self, AEGP_GetCompRenderTime -> ae_sys::A_Time, ae_sys::A_Time, render_ctx.as_ptr())?; + + Ok((shutter_frame_start.into(), shutter_frame_duration.into())) + } + + /// Given the render context, returns a buffer in which to place the final rendered output. + pub fn comp_destination_buffer( + &self, + render_ctx: impl AsPtr, + comp_handle: CompHandle, + ) -> Result { + Ok(WorldHandle::from_raw( + call_suite_fn_single!(self, AEGP_GetCompDestinationBuffer -> ae_sys::AEGP_WorldH, render_ctx.as_ptr(), comp_handle.as_ptr())?, + )) + } + + /// Given the render context provided to the Artisan at render time, returns a handle to the composition. + pub fn region_of_interest( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_GetROI -> ae_sys::A_LegacyRect, render_ctx.as_ptr())? + .into(), + ) + } + + /// Given the render context and layer, returns the layer texture. + /// + /// The returned [`WorldHandle`] can be null. + /// + /// [`RenderHints::NoTransferMode`] prevents application of opacity & transfer mode; for use with `RenderLayer` calls. + pub fn render_texture( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + render_hints: RenderHints, + suggested_scale: Option, + suggested_src_rect: Option, + src_matrix: Option, + ) -> Result { + Ok(WorldHandle::from_raw(call_suite_fn_single!(self, + AEGP_RenderTexture -> ae_sys::AEGP_WorldH, + render_ctx.as_ptr(), + layer_ctx.as_ptr(), + render_hints.into(), + suggested_scale .map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x), + suggested_src_rect.map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x), + src_matrix .map(Into::into).as_mut().map_or(std::ptr::null_mut(), |x| x) + )?)) + } + + /// Disposes of an acquired layer texture. + pub fn dispose_texture( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + world_handle: WorldHandle, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_DisposeTexture, + render_ctx.as_ptr(), + layer_ctx.as_ptr(), + world_handle.as_ptr() + ) + } + + /// Returns the field settings of the given [`RenderContextHandle`](pr::RenderContextHandle). + pub fn field_render( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_GetFieldRender -> ae_sys::PF_Field, render_ctx.as_ptr())?, + ) + } + + /// Given the render context provided to the Artisan at render time, returns a handle to the composition. + /// + /// Note: this is NOT thread-safe on macOS; only use this function when the current thread ID is 0. + pub fn report_artisan_progress( + &self, + render_ctx: impl AsPtr, + count: i32, + total: i32, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_ReportArtisanProgress, + render_ctx.as_ptr(), + count, + total + ) + } + + /// Returns the downsample factor of the [`RenderContextHandle`](pr::RenderContextHandle). + pub fn render_downsample_factor( + &self, + render_ctx: impl AsPtr, + ) -> Result { + let dsf = call_suite_fn_single!(self, AEGP_GetRenderDownsampleFactor -> ae_sys::AEGP_DownsampleFactor, render_ctx.as_ptr())?; + Ok(dsf.into()) + } + + pub fn set_render_downsample_factor( + &self, + render_ctx: impl AsPtr, + mut dsf: ae_sys::AEGP_DownsampleFactor, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_SetRenderDownsampleFactor, + render_ctx.as_ptr(), + &mut dsf as *mut _ + ) + } + + /// Determines whether the [`RenderContextHandle`](pr::RenderContextHandle) is blank (empty). + pub fn is_blank_canvas( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_IsBlankCanvas -> ae_sys::A_Boolean, render_ctx.as_ptr())? + != 0, + ) + } + + /// Given a render context and a layer (at a given time), retrieves the 4 by 4 transform to move between their coordinate spaces. + pub fn render_layer_to_world_xform( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + comp_time: Time, + ) -> Result { + let matrix = call_suite_fn_single!(self, AEGP_GetRenderLayerToWorldXform -> ae_sys::A_Matrix4, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)?; + Ok(matrix.into()) + } + + /// Retrieves the bounding rectangle of the layer_contextH (at a given time) within the [`RenderContextHandle`](pr::RenderContextHandle). + pub fn render_layer_bounds( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + comp_time: Time, + ) -> Result { + Ok(call_suite_fn_single!(self, AEGP_GetRenderLayerBounds -> ae_sys::A_LegacyRect, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)?.into()) + } + + /// Returns the opacity of the given layer context at the given time, within the render context. + pub fn render_opacity( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + comp_time: Time, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetRenderOpacity -> f64, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _) + } + + /// Returns whether or not a given layer context is active within the render context, at the given time. + pub fn is_render_layer_active( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + comp_time: Time, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_IsRenderLayerActive -> ae_sys::A_Boolean, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)? + != 0, + ) + } + + /// Sets the progress information for a rendering Artisan. + /// + /// * `count` is the number of layers completed + /// * `num_layers` is the total number of layers the Artisan is rendering + pub fn set_artisan_layer_progress( + &self, + render_ctx: impl AsPtr, + count: i32, + num_layers: i32, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_SetArtisanLayerProgress, + render_ctx.as_ptr(), + count, + num_layers + ) + } + + /// Invokes the entire After Effects render pipeline, including transforms, masking, et cetera, + /// providing the layer as it appears in its composition, in a composition-sized buffer. + pub fn render_layer_plus( + &self, + render_ctx: impl AsPtr, + layer_handle: LayerHandle, + layer_ctx: impl AsPtr, + render_hints: RenderHints, + ) -> Result { + Ok(WorldHandle::from_raw( + call_suite_fn_single!(self, AEGP_RenderLayerPlus -> ae_sys::AEGP_WorldH, render_ctx.as_ptr(), layer_handle.as_ptr(), layer_ctx.as_ptr(), render_hints.into())?, + )) + } + + /// Retrieves the [`RenderLayerContextHandle`] for the specified render and fill contexts. + pub fn track_matte_context( + &self, + render_ctx: impl AsPtr, + fill_ctx: RenderLayerContextHandle, + ) -> Result { + Ok(RenderLayerContextHandle::from_raw( + call_suite_fn_single!(self, AEGP_GetTrackMatteContext -> ae_sys::AEGP_RenderLayerContextH, render_ctx.as_ptr(), fill_ctx.as_ptr())?, + )) + } + + /// Renders a texture into an [`WorldHandle`], and provides an [`RenderReceiptHandle`] for the operation. + pub fn render_texture_with_receipt( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + render_hints: RenderHints, + num_effects: RenderNumEffects, + suggested_scale: Option, + suggested_src_rect: Option, + src_matrix: Option, + ) -> Result<(RenderReceiptHandle, WorldHandle), Error> { + let suggested_scale = suggested_scale + .map(Into::into) + .as_mut() + .map_or(std::ptr::null_mut(), |x| x); + let suggested_src_rect = suggested_src_rect + .map(Into::into) + .as_mut() + .map_or(std::ptr::null_mut(), |x| x); + let src_matrix = src_matrix + .map(Into::into) + .as_mut() + .map_or(std::ptr::null_mut(), |x| x); + let (receipt, world) = call_suite_fn_double!(self, + AEGP_RenderTextureWithReceipt -> ae_sys::AEGP_RenderReceiptH, ae_sys::AEGP_WorldH, + render_ctx.as_ptr(), + layer_ctx.as_ptr(), + render_hints.into(), + num_effects.into(), + suggested_scale, + suggested_src_rect, + src_matrix + )?; + Ok(( + RenderReceiptHandle::from_raw(receipt), + WorldHandle::from_raw(world), + )) + } + + /// Returns the number of software effects applied in the given [`RenderLayerContextHandle`]. + pub fn number_of_software_effects( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_GetNumberOfSoftwareEffects -> i16, render_ctx.as_ptr(), layer_ctx.as_ptr())? + as i32, + ) + } + + /// An improvement over [`render_layer_plus()`](Self::render_layer_plus), this function also provides an [`RenderReceiptHandle`] for caching purposes. + pub fn render_layer_plus_with_receipt( + &self, + render_ctx: impl AsPtr, + layer_handle: LayerHandle, + layer_ctx: impl AsPtr, + render_hints: RenderHints, + ) -> Result<(RenderReceiptHandle, WorldHandle), Error> { + let (receipt, world) = call_suite_fn_double!(self, + AEGP_RenderLayerPlusWithReceipt -> ae_sys::AEGP_RenderReceiptH, ae_sys::AEGP_WorldH, + render_ctx.as_ptr(), + layer_handle.as_ptr(), + layer_ctx.as_ptr(), + render_hints.into() + )?; + + Ok(( + RenderReceiptHandle::from_raw(receipt), + WorldHandle::from_raw(world), + )) + } + + /// Frees an [`ae_sys::AEGP_RenderReceiptH`] + /// + /// This is called automatically on [`RenderReceiptHandle::drop()`] + pub fn dispose_render_receipt( + &self, + render_receipt_handle: ae_sys::AEGP_RenderReceiptH, + ) -> Result<(), Error> { + call_suite_fn!(self, AEGP_DisposeRenderReceipt, render_receipt_handle) + } + + /// Checks with After Effects' internal caching to determine whether a given [`RenderReceiptHandle`] is still valid. + pub fn check_render_receipt( + &self, + current_render_ctx: impl AsPtr, + current_layer_ctx: impl AsPtr, + old_render_receipt_handle: RenderReceiptHandle, + check_geometrics: bool, + num_effects: RenderNumEffects, + ) -> Result { + Ok(call_suite_fn_single!(self, + AEGP_CheckRenderReceipt -> ae_sys::AEGP_RenderReceiptStatus, + current_render_ctx.as_ptr(), + current_layer_ctx.as_ptr(), + old_render_receipt_handle.as_ptr(), + check_geometrics as i32 as _, + num_effects.into() + )? + .into()) + } + + /// Generates a [`RenderReceiptHandle`] for a layer as if the first `num_effects` have been rendered. + pub fn generate_render_receipt( + &self, + current_render_ctx: impl AsPtr, + current_layer_ctx: impl AsPtr, + num_effects: RenderNumEffects, + ) -> Result { + Ok(RenderReceiptHandle::from_raw(call_suite_fn_single!(self, + AEGP_GenerateRenderReceipt -> ae_sys::AEGP_RenderReceiptH, + current_render_ctx.as_ptr(), + current_layer_ctx.as_ptr(), + num_effects.into() + )?)) + } + + /// Returns the number of bins After Effects wants the artisan to render. + pub fn num_bins_to_render( + &self, + render_ctx: impl AsPtr, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetNumBinsToRender -> i32, render_ctx.as_ptr()) + } + + /// Sets the given render context to be the n-th bin to be rendered by After Effects. + pub fn set_nth_bin( + &self, + render_ctx: impl AsPtr, + n: i32, + ) -> Result<(), Error> { + call_suite_fn!(self, AEGP_SetNthBin, render_ctx.as_ptr(), n) + } + + /// Retrieves the type of the given bin. + pub fn bin_type(&self, render_ctx: impl AsPtr) -> Result { + Ok(call_suite_fn_single!(self, + AEGP_GetBinType -> ae_sys::AEGP_BinType, + render_ctx.as_ptr() + )? + .into()) + } + + /// Retrieves the transform to correctly orient the layer being rendered with the output world. + /// + /// Pass `true` for `only_2dB` to constrain the transform to two dimensions. + pub fn render_layer_to_world_xform_2d_3d( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + comp_time: Time, + only_2d: bool, + ) -> Result { + Ok(call_suite_fn_single!(self, + AEGP_GetRenderLayerToWorldXform2D3D -> ae_sys::A_Matrix4, + render_ctx.as_ptr(), + layer_ctx.as_ptr(), + &comp_time.into() as *const _, + if only_2d { 1 } else { 0 } + )? + .into()) + } + + /// Retrieves the platform-specific window context into which to draw the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn platform_window_ref( + &self, + render_ctx: impl AsPtr, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetPlatformWindowRef -> ae_sys::AEGP_PlatformWindowRef, render_ctx.as_ptr()) + } + + /// Retrieves the source-to-frame downsample factor for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn viewport_scale( + &self, + render_ctx: impl AsPtr, + ) -> Result<(f64, f64), Error> { + call_suite_fn_double!(self, AEGP_GetViewportScale -> f64, f64, render_ctx.as_ptr()) + } + + /// Retrieves to origin of the source, within the frame (necessary to translate between the two), for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn viewport_origin( + &self, + render_ctx: impl AsPtr, + ) -> Result<(i32, i32), Error> { + call_suite_fn_double!(self, AEGP_GetViewportOrigin -> i32, i32, render_ctx.as_ptr()) + } + + /// Retrieves the bounding rectangle for the area to be drawn, for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn viewport_rect(&self, render_ctx: impl AsPtr) -> Result { + Ok(call_suite_fn_single!(self, AEGP_GetViewportRect -> ae_sys::A_LegacyRect, render_ctx.as_ptr())?.into()) + } + + /// Retrieves the color used for the fallow regions in the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn fallow_color( + &self, + render_ctx: impl AsPtr, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetFallowColor -> ae_sys::PF_Pixel8, render_ctx.as_ptr()) + } + + pub fn interactive_buffer( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok(WorldHandle::from_raw( + call_suite_fn_single!(self, AEGP_GetInteractiveBuffer -> ae_sys::AEGP_WorldH, render_ctx.as_ptr())?, + )) + } + + /// Retrieves whether or not the checkerboard is currently active for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn interactive_checkerboard( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_GetInteractiveCheckerboard -> ae_sys::A_Boolean, render_ctx.as_ptr())? + != 0, + ) + } + + /// Retrieves the colors used in the checkerboard. + /// + /// This function is valid for interactive artisans only. + pub fn interactive_checkerboard_colors( + &self, + render_ctx: impl AsPtr, + ) -> Result<(Pixel8, Pixel8), Error> { + let (px1, px2) = call_suite_fn_double!(self, AEGP_GetInteractiveCheckerboardColors -> ae_sys::PF_Pixel, ae_sys::PF_Pixel, render_ctx.as_ptr())?; + Ok((px1.into(), px2.into())) + } + + /// Retrieves the width and height of one checkerboard square. + /// + /// This function is valid for interactive artisans only. + pub fn interactive_checkerboard_size( + &self, + render_ctx: impl AsPtr, + ) -> Result<(u32, u32), Error> { + call_suite_fn_double!(self, AEGP_GetInteractiveCheckerboardSize -> u32, u32, render_ctx.as_ptr()) + } + + /// Retrieves the cached AEGP_WorldH last used for the [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn interactive_cached_buffer( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok(WorldHandle::from_raw( + call_suite_fn_single!(self, AEGP_GetInteractiveCachedBuffer -> ae_sys::AEGP_WorldH, render_ctx.as_ptr())?, + )) + } + + /// Determines whether or not the artisan must render the current [`RenderLayerContextHandle`] as a layer. + /// + /// This function is valid for interactive artisans only. + pub fn artisan_must_render_as_layer( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_ArtisanMustRenderAsLayer -> ae_sys::A_Boolean, render_ctx.as_ptr(), layer_ctx.as_ptr())? + != 0, + ) + } + + /// Returns which channels should be displayed by the interactive artisan. + /// + /// This function is valid for interactive artisans only. + pub fn interactive_display_channel( + &self, + render_ctx: impl AsPtr, + ) -> Result { + Ok(call_suite_fn_single!(self, AEGP_GetInteractiveDisplayChannel -> ae_sys::AEGP_DisplayChannelType, render_ctx.as_ptr())?.into()) + } + + /// Returns the exposure for the given [`RenderContextHandle`](pr::RenderContextHandle), expressed as a floating point number. + /// + /// This function is valid for interactive artisans only. + pub fn interactive_exposure( + &self, + render_ctx: impl AsPtr, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetInteractiveExposure -> f64, render_ctx.as_ptr()) + } + + // TODO: what's xform? + /// Returns the color transform for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn color_transform( + &self, + render_ctx: impl AsPtr, + xform: *mut std::ffi::c_void, + ) -> Result<(bool, u32), Error> { + let mut cms_on = 0; + let mut xform_key = 0; + call_suite_fn!( + self, + AEGP_GetColorTransform, + render_ctx.as_ptr(), + &mut cms_on, + &mut xform_key, + xform + )?; + Ok((cms_on != 0, xform_key)) + } + + /// Returns the shutter angle for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + pub fn comp_shutter_time( + &self, + render_ctx: impl AsPtr, + ) -> Result<(Time, Time), Error> { + let (shutter_time, shutter_dur) = call_suite_fn_double!(self, + AEGP_GetCompShutterTime -> ae_sys::A_Time, ae_sys::A_Time, + render_ctx.as_ptr() + )?; + Ok((shutter_time.into(), shutter_dur.into())) + } + + /// New in CC. Unlike [`suites::Layer::convert_comp_to_layer_time()`](aegp::suites::Layer::convert_comp_to_layer_time), this handles time remapping with collapsed or nested comps. + /// + /// This function is valid for interactive artisans only. + pub fn map_comp_to_layer_time( + &self, + render_ctx: impl AsPtr, + layer_ctx: impl AsPtr, + comp_time: Time, + ) -> Result { + Ok(call_suite_fn_single!(self, AEGP_MapCompToLayerTime -> ae_sys::A_Time, render_ctx.as_ptr(), layer_ctx.as_ptr(), &comp_time.into() as *const _)?.into()) + } +} + +// ――――――――――――――――――――――――――――――――――――――― Types ―――――――――――――――――――――――――――――――――――――――― + +register_handle!(AEGP_RenderLayerContextH); +define_handle_wrapper!(RenderLayerContextHandle, AEGP_RenderLayerContextH); + +define_enum! { + ae_sys::AEGP_RenderHints, + RenderHints { + None = ae_sys::AEGP_RenderHints_NONE, + IgnoreExtents = ae_sys::AEGP_RenderHints_IGNORE_EXTENTS, + NoTransferMode = ae_sys::AEGP_RenderHints_NO_TRANSFER_MODE, + } +} + +define_enum! { + ae_sys::AEGP_BinType, + BinType { + None = ae_sys::AEGP_BinType_NONE, + TwoD = ae_sys::AEGP_BinType_2D, + ThreeD = ae_sys::AEGP_BinType_3D, + } +} + +define_enum! { + ae_sys::AEGP_DisplayChannelType, + DisplayChannel { + None = ae_sys::AEGP_DisplayChannel_NONE, + Red = ae_sys::AEGP_DisplayChannel_RED, + Green = ae_sys::AEGP_DisplayChannel_GREEN, + Blue = ae_sys::AEGP_DisplayChannel_BLUE, + Alpha = ae_sys::AEGP_DisplayChannel_ALPHA, + RedAlt = ae_sys::AEGP_DisplayChannel_RED_ALT, + GreenAlt = ae_sys::AEGP_DisplayChannel_GREEN_ALT, + BlueAlt = ae_sys::AEGP_DisplayChannel_BLUE_ALT, + AlphaAlt = ae_sys::AEGP_DisplayChannel_ALPHA_ALT, + } +} + +pub enum RenderNumEffects { + AllEffects, + NumEffects(u16), +} +impl Into for RenderNumEffects { + fn into(self) -> i16 { + match self { + RenderNumEffects::AllEffects => -1, + RenderNumEffects::NumEffects(x) => x as i16, + } + } +} + +define_enum! { + ae_sys::AEGP_RenderReceiptStatus, + RenderReceiptStatus { + Invalid = ae_sys::AEGP_RenderReceiptStatus_INVALID, + Valid = ae_sys::AEGP_RenderReceiptStatus_VALID, + ValidButIncomplete = ae_sys::AEGP_RenderReceiptStatus_VALID_BUT_INCOMPLETE, + } +} + +#[derive(Clone, Debug, Hash)] +pub struct RenderReceiptHandle(after_effects_sys::AEGP_RenderReceiptH); +impl RenderReceiptHandle { + pub fn from_raw(raw_handle: after_effects_sys::AEGP_RenderReceiptH) -> Self { Self(raw_handle) } + + pub fn as_ptr(&self) -> after_effects_sys::AEGP_RenderReceiptH { self.0 } +} +impl Drop for RenderReceiptHandle { + fn drop(&mut self) { + if let Ok(s) = CanvasSuite::new() { + let _ = s.dispose_render_receipt(self.0); + } + } +} + +define_suite_item_wrapper!( + ae_sys::PR_RenderContextH, pr::RenderContextHandle, + suite: CanvasSuite, + /// [`render_texture()`](Self::render_texture) supplies the raw pixels of a layer, untransformed, into an arbitrarily-sized buffer. + /// + /// [`render_layer_plus()`](Self::render_layer_plus) invokes the entire After Effects render pipeline, including transforms, masking, et cetera, providing the layer as it appears in its composition, in a composition-sized buffer. + /// + /// If the layer being rendered is 3D, the default (Standard 3D) Artisan is invoked to perform any 3D geometrics. + /// + /// Your Artisan can use this to render track matte layers, and apply them only in a strictly 2D sense, to the transformed 3D layer. + /// + /// Before rendering, the Artisans that ship with After Effects apply an inverse transform to get square pixels, then re-apply the transform before display. + /// + /// For example, if the pixel aspect ratio is 10/11 (DV NTSC), we multiply by 11/10 to get square pixels. We process and composite 3D layers, then re-divide to get back to the original pixel aspect ratio. + /// + /// The following suite supplies the layers, compositions, texture and destination buffers. This is a vital suite for all artisans. + Canvas { + dispose: ; + + /// Given the render context provided to the Artisan at render time, returns a handle to the composition. + comp_to_render() -> CompHandle => suite.comp_to_render, + + /// Given the render context, returns the number of layers the Artisan needs to render. + num_layers_to_render() -> u32 => suite.num_layers_to_render, + + /// Used to build a list of layers to render after determining the total number of layers that need rendering by the Artisan. + nth_layer_context_to_render(n: u32) -> RenderLayerContextHandle => suite.nth_layer_context_to_render, + + /// Given a [`RenderContextHandle`](pr::RenderContextHandle), retrieves the associated [`LayerHandle`] (required by many suite functions). + layer_from_layer_context(layer_ctx: impl AsPtr) -> LayerHandle => suite.layer_from_layer_context, + + /// Allows for rendering of sub-layers (as within a Photoshop file). + layer_and_sub_layer_from_layer_context(layer_ctx: impl AsPtr) -> (LayerHandle, u32) => suite.layer_and_sub_layer_from_layer_context, + + /// With collapsed geometrics "on" this gives the layer in the root composition containing the layer context. + /// + /// With collapsed geometrics off this is the same as [`layer_from_layer_context()`](Self::layer_from_layer_context). + top_layer_from_layer_context(layer_ctx: impl AsPtr) -> LayerHandle => suite.top_layer_from_layer_context, + + /// Given the render context, returns the current point in (composition) time to render. + comp_render_time() -> (Time, Time) => suite.comp_render_time, + + /// Given the render context, returns a buffer in which to place the final rendered output. + comp_destination_buffer(comp_handle: CompHandle) -> WorldHandle => suite.comp_destination_buffer, + + /// Given the render context provided to the Artisan at render time, returns a handle to the composition. + region_of_interest() -> Rect => suite.region_of_interest, + + /// Given the render context and layer, returns the layer texture. + /// + /// The returned [`WorldHandle`] can be null. + /// + /// [`RenderHints::NoTransferMode`] prevents application of opacity & transfer mode; for use with `RenderLayer` calls. + render_texture(layer_ctx: impl AsPtr, render_hints: RenderHints, suggested_scale: Option, suggested_src_rect: Option, src_matrix: Option) -> WorldHandle => suite.render_texture, + + /// Disposes of an acquired layer texture. + dispose_texture(layer_ctx: impl AsPtr, world_handle: WorldHandle) -> () => suite.dispose_texture, + + /// Returns the field settings of the given [`RenderContextHandle`](pr::RenderContextHandle). + field_render() -> ae_sys::PF_Field => suite.field_render, + + /// Given the render context provided to the Artisan at render time, returns a handle to the composition. + /// + /// Note: this is NOT thread-safe on macOS; only use this function when the current thread ID is 0. + report_artisan_progress(count: i32, total: i32) -> () => suite.report_artisan_progress, + + /// Returns the downsample factor of the [`RenderContextHandle`](pr::RenderContextHandle). + render_downsample_factor() -> ae_sys::AEGP_DownsampleFactor => suite.render_downsample_factor, + + set_render_downsample_factor(dsf: ae_sys::AEGP_DownsampleFactor) -> () => suite.set_render_downsample_factor, + + /// Determines whether the [`RenderContextHandle`](pr::RenderContextHandle) is blank (empty). + is_blank_canvas() -> bool => suite.is_blank_canvas, + + /// Given a render context and a layer (at a given time), retrieves the 4 by 4 transform to move between their coordinate spaces. + render_layer_to_world_xform(layer_ctx: impl AsPtr, comp_time: Time) -> Matrix4 => suite.render_layer_to_world_xform, + + /// Retrieves the bounding rectangle of the layer_contextH (at a given time) within the [`RenderContextHandle`](pr::RenderContextHandle). + render_layer_bounds(layer_ctx: impl AsPtr, comp_time: Time) -> Rect => suite.render_layer_bounds, + + /// Returns the opacity of the given layer context at the given time, within the render context. + render_opacity(layer_ctx: impl AsPtr, comp_time: Time) -> f64 => suite.render_opacity, + + /// Returns whether or not a given layer context is active within the render context, at the given time. + is_render_layer_active(layer_ctx: impl AsPtr, comp_time: Time) -> bool => suite.is_render_layer_active, + + /// Sets the progress information for a rendering Artisan. + /// + /// * `count` is the number of layers completed + /// * `num_layers` is the total number of layers the Artisan is rendering + set_artisan_layer_progress(count: i32, num_layers: i32) -> () => suite.set_artisan_layer_progress, + + /// Invokes the entire After Effects render pipeline, including transforms, masking, et cetera, + /// providing the layer as it appears in its composition, in a composition-sized buffer. + render_layer_plus(layer_handle: LayerHandle, layer_ctx: impl AsPtr, render_hints: RenderHints) -> WorldHandle => suite.render_layer_plus, + + /// Retrieves the [`RenderLayerContextHandle`] for the specified render and fill contexts. + track_matte_context(fill_ctx: RenderLayerContextHandle) -> RenderLayerContextHandle => suite.track_matte_context, + + /// Renders a texture into an [`WorldHandle`], and provides an [`RenderReceiptHandle`] for the operation. + render_texture_with_receipt(layer_ctx: impl AsPtr, render_hints: RenderHints, num_effects: RenderNumEffects, suggested_scale: Option, suggested_src_rect: Option, src_matrix: Option) -> (RenderReceiptHandle, WorldHandle) => suite.render_texture_with_receipt, + + /// Returns the number of software effects applied in the given [`RenderLayerContextHandle`]. + number_of_software_effects(layer_ctx: impl AsPtr) -> i32 => suite.number_of_software_effects, + + /// An improvement over [`render_layer_plus()`](Self::render_layer_plus), this function also provides an [`RenderReceiptHandle`] for caching purposes. + render_layer_plus_with_receipt(layer_handle: LayerHandle, layer_ctx: impl AsPtr, render_hints: RenderHints) -> (RenderReceiptHandle, WorldHandle) => suite.render_layer_plus_with_receipt, + + /// Returns the number of bins After Effects wants the artisan to render. + num_bins_to_render() -> i32 => suite.num_bins_to_render, + + /// Sets the given render context to be the n-th bin to be rendered by After Effects. + set_nth_bin(n: i32) -> () => suite.set_nth_bin, + + /// Retrieves the type of the given bin. + bin_type() -> BinType => suite.bin_type, + + /// Retrieves the transform to correctly orient the layer being rendered with the output world. + /// + /// Pass `true` for `only_2dB` to constrain the transform to two dimensions. + render_layer_to_world_xform_2d_3d(layer_ctx: impl AsPtr, comp_time: Time, only_2d: bool) -> Matrix4 => suite.render_layer_to_world_xform_2d_3d, + + /// Retrieves the platform-specific window context into which to draw the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + platform_window_ref() -> ae_sys::AEGP_PlatformWindowRef => suite.platform_window_ref, + + /// Retrieves the source-to-frame downsample factor for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + viewport_scale() -> (f64, f64) => suite.viewport_scale, + + /// Retrieves to origin of the source, within the frame (necessary to translate between the two), for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + viewport_origin() -> (i32, i32) => suite.viewport_origin, + + /// Retrieves the bounding rectangle for the area to be drawn, for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + viewport_rect() -> Rect => suite.viewport_rect, + + /// Retrieves the color used for the fallow regions in the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + fallow_color() -> ae_sys::PF_Pixel8 => suite.fallow_color, + + interactive_buffer() -> WorldHandle => suite.interactive_buffer, + + /// Retrieves whether or not the checkerboard is currently active for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + interactive_checkerboard() -> bool => suite.interactive_checkerboard, + + /// Retrieves the colors used in the checkerboard. + /// + /// This function is valid for interactive artisans only. + interactive_checkerboard_colors() -> (Pixel8, Pixel8) => suite.interactive_checkerboard_colors, + + /// Retrieves the width and height of one checkerboard square. + /// + /// This function is valid for interactive artisans only. + interactive_checkerboard_size() -> (u32, u32) => suite.interactive_checkerboard_size, + + /// Retrieves the cached AEGP_WorldH last used for the [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + interactive_cached_buffer() -> WorldHandle => suite.interactive_cached_buffer, + + /// Determines whether or not the artisan must render the current [`RenderLayerContextHandle`] as a layer. + /// + /// This function is valid for interactive artisans only. + artisan_must_render_as_layer(layer_ctx: impl AsPtr) -> bool => suite.artisan_must_render_as_layer, + + /// Returns which channels should be displayed by the interactive artisan. + /// + /// This function is valid for interactive artisans only. + interactive_display_channel() -> DisplayChannel => suite.interactive_display_channel, + + /// Returns the exposure for the given [`RenderContextHandle`](pr::RenderContextHandle), expressed as a floating point number. + /// + /// This function is valid for interactive artisans only. + interactive_exposure() -> f64 => suite.interactive_exposure, + + // TODO: what's xform? + /// Returns the color transform for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + color_transform(xform: *mut std::ffi::c_void) -> (bool, u32) => suite.color_transform, + + /// Returns the shutter angle for the given [`RenderContextHandle`](pr::RenderContextHandle). + /// + /// This function is valid for interactive artisans only. + comp_shutter_time() -> (Time, Time) => suite.comp_shutter_time, + + /// New in CC. Unlike [`suites::Layer::convert_comp_to_layer_time()`](aegp::suites::Layer::convert_comp_to_layer_time), this handles time remapping with collapsed or nested comps. + /// + /// This function is valid for interactive artisans only. + map_comp_to_layer_time(layer_ctx: impl AsPtr, comp_time: Time) -> Time => suite.map_comp_to_layer_time, + } +); + +impl Canvas { + // dispose_render_receipt(&self, render_receipt_handle: ae_sys::AEGP_RenderReceiptH) -> () => suite. + // check_render_receipt(&self, current_render_ctx: impl AsPtr, current_layer_ctx: impl AsPtr, old_render_receipt_handle: RenderReceiptHandle, check_geometrics: bool, num_effects: RenderNumEffects) -> RenderReceiptStatus => suite. + // generate_render_receipt(&self, current_render_ctx: impl AsPtr, current_layer_ctx: impl AsPtr, num_effects: RenderNumEffects) -> RenderReceiptHandle => suite. +} diff --git a/after-effects/src/aegp/suites/color_settings.rs b/after-effects/src/aegp/suites/color_settings.rs index 6b5d9c37..f0fc222f 100644 --- a/after-effects/src/aegp/suites/color_settings.rs +++ b/after-effects/src/aegp/suites/color_settings.rs @@ -1,174 +1,247 @@ -use crate::*; -use crate::aegp::*; -use pr::RenderContextHandle; - -define_suite!( - /// We've provided a function so AEGPs can obtain information on After Effects' current color management settings. - ColorSettingsSuite, - AEGP_ColorSettingsSuite6, - kAEGPColorSettingsSuite, - kAEGPColorSettingsSuiteVersion6 -); - -impl ColorSettingsSuite { - /// Acquire this suite from the host. Returns error if the suite is not available. - /// Suite is released on drop. - pub fn new() -> Result { - crate::Suite::new() - } - - /// Retrieves the current opaque `PF_EffectBlendingTables`, for use with `AEGP_TransferRect`. - pub fn blending_tables(&self, render_context: RenderContextHandle) -> Result { - call_suite_fn_single!(self, AEGP_GetBlendingTables -> ae_sys::PF_EffectBlendingTables, render_context.as_ptr()) - } - - /// Returns whether there is a colorspace transform applied to the current item view. - pub fn does_view_have_color_space_xform(&self, view: ItemViewHandle) -> Result { - Ok(call_suite_fn_single!(self, AEGP_DoesViewHaveColorSpaceXform -> ae_sys::A_Boolean, view.as_ptr())? != 0) - } - - /// Changes the view colorspace of the source to be the working colorspace of the destination. - /// Source and destination can be the same. - pub fn xform_working_to_view_color_space(&self, view: ItemViewHandle, src: WorldHandle, dst: &mut WorldHandle) -> Result<(), Error> { - call_suite_fn!(self, AEGP_XformWorkingToViewColorSpace, view.as_ptr(), src.as_ptr(), dst.as_ptr()) - } - - /// Retrieves the opaque current working space ICC profile. - /// The "New" in the name does not indicate that you're making up a new profile; rather, it's part of our function naming standard; anything with "New" in the name allocates something which the caller must dispose. - pub fn new_working_space_color_profile(&self, plugin_id: PluginId, comp: CompHandle) -> Result { - Ok(ColorProfileHandle::from_raw_owned( - call_suite_fn_single!(self, AEGP_GetNewWorkingSpaceColorProfile -> ae_sys::AEGP_ColorProfileP, plugin_id, comp.as_ptr())? - )) - } - - /// Retrieves a new [`ColorProfileHandle`] from After Effects, representing the specified ICC profile. - pub fn new_color_profile_from_icc_profile(&self, plugin_id: PluginId, icc_size: i32, icc_data: *const std::ffi::c_void) -> Result { - Ok(ColorProfileHandle::from_raw_owned( - call_suite_fn_single!(self, AEGP_GetNewColorProfileFromICCProfile -> ae_sys::AEGP_ColorProfileP, plugin_id, icc_size, icc_data)? - )) - } - - /// Retrieves a new ICC profile representing the specified color profile. - /// - /// Use [`MemHandle::to_bytes()`] to convert into `Vec`. - pub fn new_icc_profile_from_color_profile(&self, plugin_id: PluginId, color_profile: ConstColorProfileHandle) -> Result, Error> { - let handle = call_suite_fn_single!(self, AEGP_GetNewICCProfileFromColorProfile -> ae_sys::AEGP_MemHandle, plugin_id, color_profile.as_ptr())?; - Ok(MemHandle::from_raw(handle)?) - } - - /// Returns a textual description of the specified color profile. - pub fn new_color_profile_description(&self, plugin_id: PluginId, color_profile: ConstColorProfileHandle) -> Result { - let mem_handle = call_suite_fn_single!(self, AEGP_GetNewColorProfileDescription -> ae_sys::AEGP_MemHandle, plugin_id, color_profile.as_ptr())?; - Ok(unsafe { - U16CString::from_ptr_str( - MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr(), - ).to_string_lossy() - }) - } - - /// Disposes of a color profile, obtained using other functions in this suite. - pub fn dispose_color_profile(&self, color_profile: ae_sys::AEGP_ColorProfileP) -> Result<(), Error> { - call_suite_fn!(self, AEGP_DisposeColorProfile, color_profile) - } - - /// Returns a floating point number approximating the gamma setting used by the specified color profile. - pub fn color_profile_approximate_gamma(&self, color_profile: ConstColorProfileHandle) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetColorProfileApproximateGamma -> ae_sys::A_FpShort, color_profile.as_ptr())?) - } - - /// Returns whether the specified color profile is RGB. - pub fn is_rgb_color_profile(&self, color_profile: ConstColorProfileHandle) -> Result { - Ok(call_suite_fn_single!(self, AEGP_IsRGBColorProfile -> ae_sys::A_Boolean, color_profile.as_ptr())? != 0) - } - - /// Sets the working space to the passed color profile. - pub fn set_working_color_space(&self, plugin_id: PluginId, comp: CompHandle, color_profile: ConstColorProfileHandle) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetWorkingColorSpace, plugin_id, comp.as_ptr(), color_profile.as_ptr()) - } - - /// Check if the current project is using the OCIO color engine or not. - /// Returns true if current project uses OCIO color managed mode. - pub fn is_ocio_color_management_used(&self, plugin_id: PluginId) -> Result { - Ok(call_suite_fn_single!(self, AEGP_IsOCIOColorManagementUsed -> ae_sys::A_Boolean, plugin_id)? != 0) - } - - /// Returns the OCIO configuration file used by the project. - /// - /// Returned string is the OCIO Configuration file. - pub fn ocio_configuration_file(&self, plugin_id: PluginId) -> Result { - let mem_handle = call_suite_fn_single!(self, AEGP_GetOCIOConfigurationFile -> ae_sys::AEGP_MemHandle, plugin_id)?; - Ok(unsafe { - U16CString::from_ptr_str( - MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr(), - ).to_string_lossy() - }) - } - - /// Returns the absolute file path to the OCIO configuration used by the project - /// - /// The returned string is an absolute path to OCIO Configuration file. - pub fn ocio_configuration_file_path(&self, plugin_id: PluginId) -> Result { - let mem_handle = call_suite_fn_single!(self, AEGP_GetOCIOConfigurationFilePath -> ae_sys::AEGP_MemHandle, plugin_id)?; - Ok(unsafe { - U16CString::from_ptr_str( - MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr(), - ).to_string_lossy() - }) - } - - /// Returns the working color space of the project in OCIO mode. - /// - /// The returned string specifies the working color space. - pub fn ocio_working_color_space(&self, plugin_id: PluginId) -> Result { - let mem_handle = call_suite_fn_single!(self, AEGPD_GetOCIOWorkingColorSpace -> ae_sys::AEGP_MemHandle, plugin_id)?; - Ok(unsafe { - U16CString::from_ptr_str( - MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr(), - ).to_string_lossy() - }) - } - - /// Returns the Display and View transforms used by the project. - /// - /// The returned strings specify the Display and View transforms used at project level. - pub fn ocio_display_color_space(&self, plugin_id: PluginId) -> Result<(String, String), Error> { - let (display, view) = call_suite_fn_double!(self, AEGPD_GetOCIODisplayColorSpace -> ae_sys::AEGP_MemHandle, ae_sys::AEGP_MemHandle, plugin_id)?; - Ok(unsafe {( - U16CString::from_ptr_str(MemHandle::::from_raw(display)?.lock()?.as_ptr()).to_string_lossy(), - U16CString::from_ptr_str(MemHandle::::from_raw(view) ?.lock()?.as_ptr()).to_string_lossy() - )}) - } - pub fn is_color_space_aware_effects_enabled(&self, plugin_id: PluginId) -> Result { - Ok(call_suite_fn_single!(self, AEGPD_IsColorSpaceAwareEffectsEnabled -> ae_sys::A_Boolean, plugin_id)? != 0) - } - pub fn lut_interpolation_method(&self, plugin_id: PluginId) -> Result { - Ok(call_suite_fn_single!(self, AEGPD_GetLUTInterpolationMethod -> ae_sys::A_u_short, plugin_id)?) - } - pub fn graphics_white_luminance(&self, plugin_id: PluginId) -> Result { - Ok(call_suite_fn_single!(self, AEGPD_GetGraphicsWhiteLuminance -> ae_sys::A_u_short, plugin_id)?) - } - pub fn working_color_space_id(&self, plugin_id: PluginId) -> Result { - let val: ae_sys::AEGP_GuidP = unsafe { std::mem::zeroed() }; - let err = unsafe { ae_get_suite_fn!(self.suite_ptr, AEGPD_GetWorkingColorSpaceId)(plugin_id, val) }; - match err { - 0 => Ok(val), - _ => Err(Error::from(err)) - } - } -} - -// ――――――――――――――――――――――――――――――――――――――― Types ―――――――――――――――――――――――――――――――――――――――― - -define_handle_wrapper!(ItemViewHandle, AEGP_ItemViewP); -define_handle_wrapper!(ConstColorProfileHandle, AEGP_ConstColorProfileP); - -define_owned_handle_wrapper!(ColorProfileHandle, AEGP_ColorProfileP); -impl Drop for ColorProfileHandle { - fn drop(&mut self) { - if let Ok(suite) = ColorSettingsSuite::new() { - if suite.dispose_color_profile(self.as_ptr()).is_ok() { - self.0 = std::ptr::null_mut(); - } - } - } -} +use crate::aegp::*; +use crate::*; +use pr::RenderContextHandle; + +define_suite!( + /// We've provided a function so AEGPs can obtain information on After Effects' current color management settings. + ColorSettingsSuite, + AEGP_ColorSettingsSuite6, + kAEGPColorSettingsSuite, + kAEGPColorSettingsSuiteVersion6 +); + +impl ColorSettingsSuite { + /// Acquire this suite from the host. Returns error if the suite is not available. + /// Suite is released on drop. + pub fn new() -> Result { crate::Suite::new() } + + /// Retrieves the current opaque `PF_EffectBlendingTables`, for use with `AEGP_TransferRect`. + pub fn blending_tables( + &self, + render_context: RenderContextHandle, + ) -> Result { + call_suite_fn_single!(self, AEGP_GetBlendingTables -> ae_sys::PF_EffectBlendingTables, render_context.as_ptr()) + } + + /// Returns whether there is a colorspace transform applied to the current item view. + pub fn does_view_have_color_space_xform(&self, view: ItemViewHandle) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_DoesViewHaveColorSpaceXform -> ae_sys::A_Boolean, view.as_ptr())? + != 0, + ) + } + + /// Changes the view colorspace of the source to be the working colorspace of the destination. + /// Source and destination can be the same. + pub fn xform_working_to_view_color_space( + &self, + view: ItemViewHandle, + src: WorldHandle, + dst: &mut WorldHandle, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_XformWorkingToViewColorSpace, + view.as_ptr(), + src.as_ptr(), + dst.as_ptr() + ) + } + + /// Retrieves the opaque current working space ICC profile. + /// The "New" in the name does not indicate that you're making up a new profile; rather, it's part of our function naming standard; anything with "New" in the name allocates something which the caller must dispose. + pub fn new_working_space_color_profile( + &self, + plugin_id: PluginId, + comp: CompHandle, + ) -> Result { + Ok(ColorProfileHandle::from_raw_owned( + call_suite_fn_single!(self, AEGP_GetNewWorkingSpaceColorProfile -> ae_sys::AEGP_ColorProfileP, plugin_id, comp.as_ptr())?, + )) + } + + /// Retrieves a new [`ColorProfileHandle`] from After Effects, representing the specified ICC profile. + pub fn new_color_profile_from_icc_profile( + &self, + plugin_id: PluginId, + icc_size: i32, + icc_data: *const std::ffi::c_void, + ) -> Result { + Ok(ColorProfileHandle::from_raw_owned( + call_suite_fn_single!(self, AEGP_GetNewColorProfileFromICCProfile -> ae_sys::AEGP_ColorProfileP, plugin_id, icc_size, icc_data)?, + )) + } + + /// Retrieves a new ICC profile representing the specified color profile. + /// + /// Use [`MemHandle::to_bytes()`] to convert into `Vec`. + pub fn new_icc_profile_from_color_profile( + &self, + plugin_id: PluginId, + color_profile: ConstColorProfileHandle, + ) -> Result, Error> { + let handle = call_suite_fn_single!(self, AEGP_GetNewICCProfileFromColorProfile -> ae_sys::AEGP_MemHandle, plugin_id, color_profile.as_ptr())?; + Ok(MemHandle::from_raw(handle)?) + } + + /// Returns a textual description of the specified color profile. + pub fn new_color_profile_description( + &self, + plugin_id: PluginId, + color_profile: ConstColorProfileHandle, + ) -> Result { + let mem_handle = call_suite_fn_single!(self, AEGP_GetNewColorProfileDescription -> ae_sys::AEGP_MemHandle, plugin_id, color_profile.as_ptr())?; + Ok(unsafe { + U16CString::from_ptr_str(MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr()) + .to_string_lossy() + }) + } + + /// Disposes of a color profile, obtained using other functions in this suite. + pub fn dispose_color_profile( + &self, + color_profile: ae_sys::AEGP_ColorProfileP, + ) -> Result<(), Error> { + call_suite_fn!(self, AEGP_DisposeColorProfile, color_profile) + } + + /// Returns a floating point number approximating the gamma setting used by the specified color profile. + pub fn color_profile_approximate_gamma( + &self, + color_profile: ConstColorProfileHandle, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_GetColorProfileApproximateGamma -> ae_sys::A_FpShort, color_profile.as_ptr())?, + ) + } + + /// Returns whether the specified color profile is RGB. + pub fn is_rgb_color_profile( + &self, + color_profile: ConstColorProfileHandle, + ) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_IsRGBColorProfile -> ae_sys::A_Boolean, color_profile.as_ptr())? + != 0, + ) + } + + /// Sets the working space to the passed color profile. + pub fn set_working_color_space( + &self, + plugin_id: PluginId, + comp: CompHandle, + color_profile: ConstColorProfileHandle, + ) -> Result<(), Error> { + call_suite_fn!( + self, + AEGP_SetWorkingColorSpace, + plugin_id, + comp.as_ptr(), + color_profile.as_ptr() + ) + } + + /// Check if the current project is using the OCIO color engine or not. + /// Returns true if current project uses OCIO color managed mode. + pub fn is_ocio_color_management_used(&self, plugin_id: PluginId) -> Result { + Ok( + call_suite_fn_single!(self, AEGP_IsOCIOColorManagementUsed -> ae_sys::A_Boolean, plugin_id)? + != 0, + ) + } + + /// Returns the OCIO configuration file used by the project. + /// + /// Returned string is the OCIO Configuration file. + pub fn ocio_configuration_file(&self, plugin_id: PluginId) -> Result { + let mem_handle = call_suite_fn_single!(self, AEGP_GetOCIOConfigurationFile -> ae_sys::AEGP_MemHandle, plugin_id)?; + Ok(unsafe { + U16CString::from_ptr_str(MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr()) + .to_string_lossy() + }) + } + + /// Returns the absolute file path to the OCIO configuration used by the project + /// + /// The returned string is an absolute path to OCIO Configuration file. + pub fn ocio_configuration_file_path(&self, plugin_id: PluginId) -> Result { + let mem_handle = call_suite_fn_single!(self, AEGP_GetOCIOConfigurationFilePath -> ae_sys::AEGP_MemHandle, plugin_id)?; + Ok(unsafe { + U16CString::from_ptr_str(MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr()) + .to_string_lossy() + }) + } + + /// Returns the working color space of the project in OCIO mode. + /// + /// The returned string specifies the working color space. + pub fn ocio_working_color_space(&self, plugin_id: PluginId) -> Result { + let mem_handle = call_suite_fn_single!(self, AEGPD_GetOCIOWorkingColorSpace -> ae_sys::AEGP_MemHandle, plugin_id)?; + Ok(unsafe { + U16CString::from_ptr_str(MemHandle::::from_raw(mem_handle)?.lock()?.as_ptr()) + .to_string_lossy() + }) + } + + /// Returns the Display and View transforms used by the project. + /// + /// The returned strings specify the Display and View transforms used at project level. + pub fn ocio_display_color_space(&self, plugin_id: PluginId) -> Result<(String, String), Error> { + let (display, view) = call_suite_fn_double!(self, AEGPD_GetOCIODisplayColorSpace -> ae_sys::AEGP_MemHandle, ae_sys::AEGP_MemHandle, plugin_id)?; + Ok(unsafe { + ( + U16CString::from_ptr_str(MemHandle::::from_raw(display)?.lock()?.as_ptr()) + .to_string_lossy(), + U16CString::from_ptr_str(MemHandle::::from_raw(view)?.lock()?.as_ptr()) + .to_string_lossy(), + ) + }) + } + + pub fn is_color_space_aware_effects_enabled(&self, plugin_id: PluginId) -> Result { + Ok( + call_suite_fn_single!(self, AEGPD_IsColorSpaceAwareEffectsEnabled -> ae_sys::A_Boolean, plugin_id)? + != 0, + ) + } + + pub fn lut_interpolation_method(&self, plugin_id: PluginId) -> Result { + Ok( + call_suite_fn_single!(self, AEGPD_GetLUTInterpolationMethod -> ae_sys::A_u_short, plugin_id)?, + ) + } + + pub fn graphics_white_luminance(&self, plugin_id: PluginId) -> Result { + Ok( + call_suite_fn_single!(self, AEGPD_GetGraphicsWhiteLuminance -> ae_sys::A_u_short, plugin_id)?, + ) + } + + pub fn working_color_space_id(&self, plugin_id: PluginId) -> Result { + let val: ae_sys::AEGP_GuidP = unsafe { std::mem::zeroed() }; + let err = unsafe { + ae_get_suite_fn!(self.suite_ptr, AEGPD_GetWorkingColorSpaceId)(plugin_id, val) + }; + match err { + 0 => Ok(val), + _ => Err(Error::from(err)), + } + } +} + +// ――――――――――――――――――――――――――――――――――――――― Types ―――――――――――――――――――――――――――――――――――――――― + +define_handle_wrapper!(ItemViewHandle, AEGP_ItemViewP); +define_handle_wrapper!(ConstColorProfileHandle, AEGP_ConstColorProfileP); + +define_owned_handle_wrapper!(ColorProfileHandle, AEGP_ColorProfileP); +impl Drop for ColorProfileHandle { + fn drop(&mut self) { + if let Ok(suite) = ColorSettingsSuite::new() { + if suite.dispose_color_profile(self.as_ptr()).is_ok() { + self.0 = std::ptr::null_mut(); + } + } + } +} diff --git a/after-effects/src/aegp/suites/command.rs b/after-effects/src/aegp/suites/command.rs index 70fc7458..cd0f47db 100644 --- a/after-effects/src/aegp/suites/command.rs +++ b/after-effects/src/aegp/suites/command.rs @@ -67,9 +67,7 @@ impl CommandSuite { /// /// Note: On occasion After Effects will send command 0 (zero), so don't use that as part of your command handling logic. #[deprecated(since = "0.5.0", note = "renamed to `unique_command`")] - pub fn get_unique_command(&self) -> Result { - self.unique_command() - } + pub fn get_unique_command(&self) -> Result { self.unique_command() } /// Set menu name of a command. pub fn set_command_name(&self, command_name: &str, command: AEGP_Command) -> Result<(), Error> { diff --git a/after-effects/src/aegp/suites/comp.rs b/after-effects/src/aegp/suites/comp.rs index fb0814ae..a3875331 100644 --- a/after-effects/src/aegp/suites/comp.rs +++ b/after-effects/src/aegp/suites/comp.rs @@ -1,588 +1,816 @@ -use crate::*; -use crate::aegp::*; -use ae_sys::{ AEGP_CompH, AEGP_ItemH }; - -define_suite!( - /// Provide information about the compositions in a project, and create cameras, lights, and solids. - CompSuite, - AEGP_CompSuite11, - kAEGPCompSuite, - kAEGPCompSuiteVersion11 -); - -impl CompSuite { - /// Acquire this suite from the host. Returns error if the suite is not available. - /// Suite is released on drop. - pub fn new() -> Result { - crate::Suite::new() - } - - /// Retrieves the handle to the composition, given an item handle. - /// - /// Returns `None` if `item_handle` is not an `AEGP_CompH`. - pub fn comp_from_item(&self, item_handle: impl AsPtr) -> Result, Error> { - let ptr = call_suite_fn_single!(self, AEGP_GetCompFromItem -> ae_sys::AEGP_CompH, item_handle.as_ptr())?; - Ok(if ptr.is_null() { - None - } else { - Some(CompHandle::from_raw(ptr)) - }) - } - - /// Used to get the item handle, given a composition handle. - pub fn item_from_comp(&self, comp_handle: impl AsPtr) -> Result { - Ok(ItemHandle::from_raw( - call_suite_fn_single!(self, AEGP_GetItemFromComp -> ae_sys::AEGP_ItemH, comp_handle.as_ptr())? - )) - } - - /// Returns current downsample factor. Measured in pixels X by Y. - /// - /// Users can choose a custom downsample factor with independent X and Y. - pub fn comp_downsample_factor(&self, comp_handle: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetCompDownsampleFactor -> ae_sys::AEGP_DownsampleFactor, comp_handle.as_ptr())?.into()) - } - - /// Sets the composition's downsample factor. - pub fn set_comp_downsample_factor(&self, comp_handle: impl AsPtr, downsample_factor: &ae_sys::AEGP_DownsampleFactor) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCompDownsampleFactor, comp_handle.as_ptr(), downsample_factor) - } - - /// Returns the composition background color. - pub fn comp_bg_color(&self, comp_handle: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetCompBGColor -> ae_sys::AEGP_ColorVal, comp_handle.as_ptr()) - } - - /// Sets a composition's background color. - pub fn set_comp_bg_color(&self, comp_handle: impl AsPtr, color: ae_sys::AEGP_ColorVal) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCompBGColor, comp_handle.as_ptr(), &color) - } - - /// Returns composition flags, or'd together. - pub fn comp_flags(&self, comp_handle: impl AsPtr) -> Result { - CompFlags::from_bits(call_suite_fn_single!(self, AEGP_GetCompFlags -> ae_sys::A_long, comp_handle.as_ptr())?) - .ok_or(Error::InvalidParms) - } - - /// New in CC. Passes back true if the Comp's timeline shows layer names, false if source names. - /// - /// This will open the comp as a side effect. - pub fn show_layer_name_or_source_name(&self, comp_handle: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetShowLayerNameOrSourceName -> ae_sys::A_Boolean, comp_handle.as_ptr())? != 0) - } - - /// New in CC. Pass in true to have the Comp's timeline show layer names, false for source names. - /// - /// This will open the comp as a side effect. - pub fn set_show_layer_name_or_source_name(&self, comp_handle: impl AsPtr, show_layer_names: bool) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetShowLayerNameOrSourceName, comp_handle.as_ptr(), if show_layer_names { 1 } else { 0 }) - } - - - /// New in CC. Passes back true if the Comp's timeline shows blend modes column, false if hidden. - /// - /// This will open the comp as a side effect. - pub fn show_blend_modes(&self, comp_handle: impl AsPtr) -> Result { - Ok(call_suite_fn_single!(self, AEGP_GetShowBlendModes -> ae_sys::A_Boolean, comp_handle.as_ptr())? != 0) - } - - /// New in CC. Pass in true to have the Comp's timeline show the blend modes column, false to hide it. - /// - /// This will open the comp as a side effect. - pub fn set_show_blend_modes(&self, comp_handle: impl AsPtr, show_blend_modes: bool) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetShowBlendModes, comp_handle.as_ptr(), if show_blend_modes { 1 } else { 0 }) - } - - /// Returns the composition's frames per second. - pub fn comp_framerate(&self, comp_handle: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetCompFramerate -> f64, comp_handle.as_ptr()) - } - - /// Sets the composition's frames per second. - pub fn set_comp_framerate(&self, comp_handle: impl AsPtr, framerate: f64) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCompFrameRate, comp_handle.as_ptr(), &framerate) - } - - /// The composition shutter angle and phase. - pub fn comp_shutter_angle_phase(&self, comp_handle: impl AsPtr) -> Result<(Ratio, Ratio), Error> { - let (angle, phase) = call_suite_fn_double!(self, AEGP_GetCompShutterAnglePhase -> ae_sys::A_Ratio, ae_sys::A_Ratio, comp_handle.as_ptr())?; - Ok(( - angle.into(), - phase.into() - )) - } - - /// The duration of the shutter frame, in seconds. - pub fn comp_shutter_frame_range(&self, comp_handle: impl AsPtr, comp_time: Time) -> Result<(Time, Time), Error> { - let (start, duration) = call_suite_fn_double!(self, AEGP_GetCompShutterFrameRange -> ae_sys::A_Time, ae_sys::A_Time, comp_handle.as_ptr(), &comp_time.into() as *const _)?; - Ok(( - start.into(), - duration.into() - )) - } - - /// Retrieves the number of motion blur samples After Effects will perform in the given composition. - pub fn comp_suggested_motion_blur_samples(&self, comp_handle: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetCompSuggestedMotionBlurSamples -> i32, comp_handle.as_ptr()) - } - - /// Specifies the number of motion blur samples After Effects will perform in the given composition. Undoable. - pub fn set_comp_suggested_motion_blur_samples(&self, comp_handle: impl AsPtr, samples: i32) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCompSuggestedMotionBlurSamples, comp_handle.as_ptr(), samples) - } - - /// New in CC. Retrieves the motion blur adaptive sample limit for the given composition. - /// - /// As of CC, a new comp defaults to 128. - pub fn comp_motion_blur_adaptive_sample_limit(&self, comp_handle: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetCompMotionBlurAdaptiveSampleLimit -> i32, comp_handle.as_ptr()) - } - - /// New in CC. Specifies the motion blur adaptive sample limit for the given composition. - /// - /// As of CC, both the limit and the suggested values are clamped to \[2,256\] range and the limit value will not be allowed less than the suggested value. - /// - /// Undoable. - pub fn set_comp_motion_blur_adaptive_sample_limit(&self, comp_handle: impl AsPtr, limit: i32) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCompMotionBlurAdaptiveSampleLimit, comp_handle.as_ptr(), limit) - } - - /// Get the time where the current work area starts. - pub fn comp_work_area_start(&self, comp_handle: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetCompWorkAreaStart -> ae_sys::A_Time, comp_handle.as_ptr()).map(|t| t.into()) - } - - /// Get the duration of a composition's current work area, in seconds. - pub fn comp_work_area_duration(&self, comp_handle: impl AsPtr) -> Result { - call_suite_fn_single!(self, AEGP_GetCompWorkAreaDuration -> ae_sys::A_Time, comp_handle.as_ptr()).map(|t| t.into()) - } - - /// Set the work area start and duration, in seconds. Undo-able. - /// - /// One call to this function is sufficient to set the layer's in point and duration; - /// it's not necessary to call it twice, once for each timespace. - pub fn set_comp_work_area_start_and_duration(&self, comp_handle: impl AsPtr, start: Time, duration: Time) -> Result<(), Error> { - call_suite_fn!(self, AEGP_SetCompWorkAreaStartAndDuration, comp_handle.as_ptr(), &start.into() as *const _ as *const ae_sys::A_Time, &duration.into() as *const _ as *const ae_sys::A_Time) - } - - /// Creates a new solid with a specified width, height, color, and duration in the composition. Undo-able. - /// - /// If you pass `None` for the duration, After Effects uses its preference for the duration of a new still. - /// If you pass `None`, or an invalid time scale, duration is set to the length of the composition. - pub fn create_solid_in_comp(&self, comp_handle: impl AsPtr, name: &str, width: i32, height: i32, color: ae_sys::AEGP_ColorVal, duration: Option