after-effects 0.4.0

High level bindings for the Adobe After Effects® SDK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
use crate::*;
use crate::aegp::*;
use ae_sys::{AEGP_CompH, AEGP_LayerH};

define_suite!(
    /// This suite provides information about layers within a composition, and the relationship(s) between the source and layer times.
    ///
    /// As most After Effects usage boils down to layer manipulation, this is among the largest function suites in our API.
    ///
    /// ## Layer Creation Notes
    /// All layers created using AEGP calls will start at composition time 0, and have the duration of the composition.
    ///
    /// Use [`suites::Layer::set_layer_offset()`] and [`suites::Layer::set_layer_in_point_and_duration()`] to properly set the layer's time information.
    ///
    /// When the layer stretch factor (obtained using [`suites::Layer::layer_stretch()`], naturally) is not 100%, the following computation will be needed to yield the correct layer offset:
    /// ```ignore
    /// offset = compIn - stretch * layerIn;
    /// ```
    LayerSuite,
    AEGP_LayerSuite7,
    kAEGPLayerSuite,
    kAEGPLayerSuiteVersion7
);
define_suite!(
    LayerSuite8,
    AEGP_LayerSuite8,
    kAEGPLayerSuite,
    kAEGPLayerSuiteVersion8
);
define_suite!(
    LayerSuite9,
    AEGP_LayerSuite9,
    kAEGPLayerSuite,
    kAEGPLayerSuiteVersion9
);

impl LayerSuite {
    /// Acquire this suite from the host. Returns error if the suite is not available.
    /// Suite is released on drop.
    pub fn new() -> Result<Self, Error> {
        crate::Suite::new()
    }

    /// Obtains the number of layers in a composition.
    pub fn comp_num_layers(&self, comp_handle: impl AsPtr<AEGP_CompH>) -> Result<usize, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetCompNumLayers -> i32, comp_handle.as_ptr())? as usize)
    }

    /// Get a [`LayerHandle`] from a composition. Zero is the foremost layer.
    pub fn comp_layer_by_index(&self, comp_handle: impl AsPtr<AEGP_CompH>, layer_index: usize) -> Result<LayerHandle, Error> {
        Ok(LayerHandle::from_raw(
            call_suite_fn_single!(self, AEGP_GetCompLayerByIndex -> ae_sys::AEGP_LayerH, comp_handle.as_ptr(), layer_index as _)?
        ))
    }

    /// Get the active layer. If a Layer or effect controls palette is active, the active layer is that associated with the front-most tab in the window.
    ///
    /// If a composition or timeline window is active, the active layer is the selected layer (if only one is selected; otherwise `None` is returned).
    pub fn active_layer(&self) -> Result<Option<LayerHandle>, Error> {
        let layer_handle = call_suite_fn_single!(self, AEGP_GetActiveLayer -> ae_sys::AEGP_LayerH)?;
        if layer_handle.is_null() {
            Ok(None)
        } else {
            Ok(Some(LayerHandle::from_raw(layer_handle)))
        }
    }

    /// Get the index of the layer (0 is the topmost layer in the composition).
    pub fn layer_index(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<usize, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerIndex -> i32, layer_handle.as_ptr())? as usize)
    }

    /// Get the [`ItemHandle`] of the layer's source item.
    pub fn layer_source_item(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<ItemHandle, Error> {
        Ok(ItemHandle::from_raw(
            call_suite_fn_single!(self, AEGP_GetLayerSourceItem -> ae_sys::AEGP_ItemH, layer_handle.as_ptr())?
        ))
    }

    /// Retrieves the ID of the given [`LayerHandle`].
    ///
    /// This is useful when hunting for a specific layer's ID in an [`StreamValue`].
    pub fn layer_source_item_id(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<i32, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerSourceItemID -> i32, layer_handle.as_ptr())?)
    }

    /// Get the AEGP_CompH of the composition containing the layer.
    pub fn layer_parent_comp(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<CompHandle, Error> {
        Ok(CompHandle::from_raw(
            call_suite_fn_single!(self, AEGP_GetLayerParentComp -> ae_sys::AEGP_CompH, layer_handle.as_ptr())?
        ))
    }

    /// Get the name of a layer.
    pub fn layer_name(&self, layer_handle: impl AsPtr<AEGP_LayerH>, plugin_id: PluginId) -> Result<(String, String), Error> {
        let (layer_name, source_name) = call_suite_fn_double!(self, AEGP_GetLayerName ->ae_sys::AEGP_MemHandle, ae_sys::AEGP_MemHandle, plugin_id, layer_handle.as_ptr())?;
        unsafe {
            Ok((
                // Create a mem handle each and lock it.
                // When the lock goes out of scope it unlocks and when the handle goes out of scope it gives the memory back to Ae.
                U16CString::from_ptr_str(MemHandle::<u16>::from_raw(layer_name)?.lock()?.as_ptr()).to_string_lossy(),
                U16CString::from_ptr_str(MemHandle::<u16>::from_raw(source_name)?.lock()?.as_ptr()).to_string_lossy()
            ))
        }
    }

    /// Get the quality of a layer.
    pub fn layer_quality(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<LayerQuality, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerQuality -> ae_sys::AEGP_LayerQuality, layer_handle.as_ptr())?.into())
    }

    /// Sets the quality of a layer. Undoable.
    pub fn set_layer_quality(&self, layer_handle: impl AsPtr<AEGP_LayerH>, quality: LayerQuality) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerQuality, layer_handle.as_ptr(), quality.into())
    }

    /// Get flags for a layer.
    pub fn layer_flags(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<LayerFlags, Error> {
        Ok(LayerFlags::from_bits_truncate(
            call_suite_fn_single!(self, AEGP_GetLayerFlags -> ae_sys::AEGP_LayerFlags, layer_handle.as_ptr())?
        ))
    }

    /// Sets one layer flag at a time. Undoable.
    pub fn set_layer_flag(&self, layer_handle: impl AsPtr<AEGP_LayerH>, single_flag: LayerFlags, value: bool) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerFlag, layer_handle.as_ptr(), single_flag.bits(), value as _)
    }

    /// Determines whether the layer's video is visible.
    ///
    /// This is necessary to account for 'solo' status of other layers in the composition; non-solo'd layers are still on.
    pub fn is_layer_video_really_on(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsLayerVideoReallyOn -> ae_sys::A_Boolean, layer_handle.as_ptr())? != 0)
    }

    /// Accounts for solo status of other layers in the composition.
    pub fn is_layer_audio_really_on(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsLayerAudioReallyOn -> ae_sys::A_Boolean, layer_handle.as_ptr())? != 0)
    }

    /// Get current time, in layer or composition timespace. This value is not updated during rendering.
    ///
    /// NOTE: If a layer starts at other than time 0 or is time-stretched other than 100%, layer time and composition time are distinct.
    pub fn layer_current_time(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time_mode: TimeMode) -> Result<Time, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerCurrentTime -> ae_sys::A_Time, layer_handle.as_ptr(), time_mode.into())?.into())
    }

    /// Get time of first visible frame in composition or layer time.
    ///
    /// In layer time, the `in_point` is always 0.
    pub fn layer_in_point(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time_mode: TimeMode) -> Result<Time, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerInPoint -> ae_sys::A_Time, layer_handle.as_ptr(), time_mode.into())?.into())
    }

    /// Get duration of layer, in composition or layer time, in seconds.
    pub fn layer_duration(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time_mode: TimeMode) -> Result<Time, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerDuration -> ae_sys::A_Time, layer_handle.as_ptr(), time_mode.into())?.into())
    }

    /// Set duration and in point of layer in composition or layer time. Undo-able.
    pub fn set_layer_in_point_and_duration(&self, layer_handle: impl AsPtr<AEGP_LayerH>, in_point: Time, duration: Time, time_mode: TimeMode) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerInPointAndDuration, layer_handle.as_ptr(), time_mode.into(), &in_point.into() as *const _, &duration.into() as *const _)
    }

    /// Get the offset from the start of the composition to layer time 0, in composition time.
    pub fn layer_offset(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<Time, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerOffset -> ae_sys::A_Time, layer_handle.as_ptr())?.into())
    }

    /// Set the offset from the start of the composition to the first frame of the layer, in composition time. Undoable.
    pub fn set_layer_offset(&self, layer_handle: impl AsPtr<AEGP_LayerH>, offset: Time) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerOffset, layer_handle.as_ptr(), &offset.into() as *const _)
    }

    /// Get stretch factor of a layer.
    pub fn layer_stretch(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<Ratio, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerStretch -> ae_sys::A_Ratio, layer_handle.as_ptr())?.into())
    }

    /// Set stretch factor of a layer.
    pub fn set_layer_stretch(&self, layer_handle: impl AsPtr<AEGP_LayerH>, stretch: Ratio) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerStretch, layer_handle.as_ptr(), &stretch.into() as *const _)
    }

    /// Get transfer mode of a layer.
    pub fn layer_transfer_mode(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<ae_sys::AEGP_LayerTransferMode, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerTransferMode -> ae_sys::AEGP_LayerTransferMode, layer_handle.as_ptr())?.into())
    }

    /// Set transfer mode of a layer. Undoable.
    ///
    /// As of 23.0, when you make a layer a track matte, the layer being matted will be disabled,
    /// as when you do this via the interface.
    pub fn set_layer_transfer_mode(&self, layer_handle: impl AsPtr<AEGP_LayerH>, transfer_mode: &ae_sys::AEGP_LayerTransferMode) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerTransferMode, layer_handle.as_ptr(), transfer_mode)
    }

    /// Tests whether it's currently valid to add a given item to a composition.
    ///
    /// A composition cannot be added to itself, or to any compositions which it contains; other conditions can preclude successful adding too.
    ///
    /// Adding a layer without first using this function will produce undefined results.
    pub fn is_add_layer_valid(&self, item_handle: &ItemHandle, comp_handle: impl AsPtr<AEGP_CompH>) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsAddLayerValid -> ae_sys::A_Boolean, item_handle.as_ptr(), comp_handle.as_ptr())? != 0)
    }

    /// Add an item to the composition, above all other layers. Undo-able.
    ///
    /// Use [`Self::is_add_layer_valid()`] first, to confirm that it's possible.
    pub fn add_layer(&self, item_handle: &ItemHandle, comp_handle: impl AsPtr<AEGP_CompH>) -> Result<LayerHandle, Error> {
        Ok(LayerHandle::from_raw(
            call_suite_fn_single!(self, AEGP_AddLayer -> ae_sys::AEGP_LayerH, item_handle.as_ptr(), comp_handle.as_ptr())?
        ))
    }

    /// Change the order of layers. Undoable.
    ///
    /// To add a layer to the end of the composition, to use `layer_index = -1`
    pub fn reorder_layer(&self, layer_handle: impl AsPtr<AEGP_LayerH>, layer_index: i32) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_ReorderLayer, layer_handle.as_ptr(), layer_index)
    }

    /// Given a layer's handle and a time, returns the bounds of area visible with masks applied.
    pub fn layer_masked_bounds(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time_mode: TimeMode, time: Time) -> Result<FloatRect, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerMaskedBounds -> ae_sys::A_FloatRect, layer_handle.as_ptr(), time_mode.into(), &time.into() as *const _)?.into())
    }

    /// Returns a layer's object type.
    pub fn layer_object_type(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<ObjectType, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerObjectType -> ae_sys::AEGP_ObjectType, layer_handle.as_ptr())?.into())
    }

    /// Is the footage item a 3D layer. All AV layers are either 2D or 3D.
    pub fn is_layer_3d(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsLayer3D -> ae_sys::A_Boolean, layer_handle.as_ptr())? != 0)
    }

    /// Is the footage item a 2D layer. All AV layers are either 2D or 3D.
    pub fn is_layer_2d(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsLayer2D -> ae_sys::A_Boolean, layer_handle.as_ptr())? != 0)
    }

    /// Given composition time and a layer, see if the layer will render.
    ///
    /// Time mode is either [`TimeMode::LayerTime`] or [`TimeMode::CompTime`].
    pub fn is_video_active(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time_mode: TimeMode, time: Time) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsVideoActive -> ae_sys::A_Boolean, layer_handle.as_ptr(), time_mode.into(), &time.into() as *const _)? != 0)
    }

    /// Is the layer used as a track matte?
    pub fn is_layer_used_as_track_matte(&self, layer_handle: impl AsPtr<AEGP_LayerH>, fill_must_be_active: bool) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_IsLayerUsedAsTrackMatte -> ae_sys::A_Boolean, layer_handle.as_ptr(), fill_must_be_active as _)? != 0)
    }

    /// Does this layer have a Track Matte?
    pub fn does_layer_have_track_matte(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<bool, Error> {
        Ok(call_suite_fn_single!(self, AEGP_DoesLayerHaveTrackMatte -> ae_sys::A_Boolean, layer_handle.as_ptr())? != 0)
    }

    /// Given a time in composition space, returns the time relative to the layer source footage.
    pub fn convert_comp_to_layer_time(&self, layer_handle: impl AsPtr<AEGP_LayerH>, comp_time: Time) -> Result<Time, Error> {
        Ok(call_suite_fn_single!(self, AEGP_ConvertCompToLayerTime -> ae_sys::A_Time, layer_handle.as_ptr(), &comp_time.into() as *const _)?.into())
    }

    /// Given a time in layer space, find the corresponding time in composition space.
    pub fn convert_layer_to_comp_time(&self, layer_handle: impl AsPtr<AEGP_LayerH>, layer_time: Time) -> Result<Time, Error> {
        Ok(call_suite_fn_single!(self, AEGP_ConvertLayerToCompTime -> ae_sys::A_Time, layer_handle.as_ptr(), &layer_time.into() as *const _)?.into())
    }

    /// Used by the dancing dissolve transfer function.
    pub fn layer_dancing_rand_value(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time: Time) -> Result<i32, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerDancingRandValue -> ae_sys::A_long, layer_handle.as_ptr(), &time.into() as *const _)?.into())
    }

    /// Supplies the layer's unique ID. This ID never changes during the lifetime of the project.
    pub fn layer_id(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<LayerId, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerID -> ae_sys::AEGP_LayerIDVal, layer_handle.as_ptr())? as LayerId)
    }

    /// Given a layer handle and time, returns the layer-to-world transformation matrix.
    pub fn layer_to_world_xform(&self, layer_handle: impl AsPtr<AEGP_LayerH>, time: Time) -> Result<Matrix4, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerToWorldXform -> ae_sys::A_Matrix4, layer_handle.as_ptr(), &time.into() as *const _)?.into())
    }

    /// Given a layer handle, the current (composition) time, and the requested view time, returns the translation between the user's view and the layer, corrected for the composition's current aspect ratio.
    pub fn layer_to_world_xform_from_view(&self, layer_handle: impl AsPtr<AEGP_LayerH>, comp_time: Time, view_time: Time) -> Result<Matrix4, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerToWorldXformFromView -> ae_sys::A_Matrix4, layer_handle.as_ptr(), &comp_time.into() as *const _, &view_time.into() as *const _)?.into())
    }

    /// Sets the name of a layer. Undo-able.
    pub fn set_layer_name(&self, layer_handle: impl AsPtr<AEGP_LayerH>, new_name: &str) -> Result<(), Error> {
        let new_name = U16CString::from_str(new_name).map_err(|_| Error::InvalidParms)?;
        call_suite_fn!(self, AEGP_SetLayerName, layer_handle.as_ptr(), new_name.as_ptr())
    }

    /// Retrieves the handle to a layer's parent (none if not parented).
    pub fn layer_parent(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<Option<LayerHandle>, Error> {
        let parent_handle = call_suite_fn_single!(self, AEGP_GetLayerParent -> ae_sys::AEGP_LayerH, layer_handle.as_ptr())?;
        if parent_handle.is_null() {
            Ok(None)
        } else {
            Ok(Some(LayerHandle::from_raw(parent_handle)))
        }
    }

    /// Sets a layer's parent layer.
    pub fn set_layer_parent(&self, layer_handle: impl AsPtr<AEGP_LayerH>, parent_handle: LayerHandle) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerParent, layer_handle.as_ptr(), parent_handle.as_ptr())
    }

    /// Deletes a layer. Can you believe it took us three suite versions to add a delete function? Neither can we.
    pub fn delete_layer(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_DeleteLayer, layer_handle.as_ptr())
    }

    /// Duplicates the layer. Undoable.
    pub fn duplicate_layer(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<LayerHandle, Error> {
        Ok(LayerHandle::from_raw(
            call_suite_fn_single!(self, AEGP_DuplicateLayer -> ae_sys::AEGP_LayerH, layer_handle.as_ptr())?
        ))
    }

    /// Retrieves the [`LayerHandle`] associated with a given [`LayerId`] (which is what you get when accessing an effect's layer parameter stream).
    pub fn layer_from_layer_id(&self, parent: &CompHandle, layer_id: LayerId) -> Result<LayerHandle, Error> {
        Ok(LayerHandle::from_raw(
            call_suite_fn_single!(self, AEGP_GetLayerFromLayerID -> ae_sys::AEGP_LayerH, parent.as_ptr(), layer_id as _)?
        ))
    }

    /// Gets a layer's [`LabelId`].
    pub fn layer_label(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<LabelId, Error> {
        Ok(call_suite_fn_single!(self, AEGP_GetLayerLabel -> ae_sys::AEGP_LabelID, layer_handle.as_ptr())?.into())
    }

    /// Sets a layer's [`LabelId`]. Undoable.
    pub fn set_layer_label(&self, layer_handle: impl AsPtr<AEGP_LayerH>, label_id: LabelId) -> Result<(), Error> {
        call_suite_fn!(self, AEGP_SetLayerLabel, layer_handle.as_ptr(), label_id.into())
    }

    /// New in CC. Get the sampling quality of a layer.
    ///
    /// Layer sampling quality is one of the following flags:
    ///
    /// - [`LayerSamplingQuality::Bilinear`]
    /// - [`LayerSamplingQuality::Bicubic`]
    pub fn layer_sampling_quality(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<LayerSamplingQuality, Error> {
        let v8 = LayerSuite8::new()?;
        Ok(call_suite_fn_single!(v8, AEGP_GetLayerSamplingQuality -> ae_sys::AEGP_LayerSamplingQuality, layer_handle.as_ptr())?.into())
    }

    /// New in CC. Sets the sampling quality of a layer (see flag values above).
    ///
    /// Option is explicitly set on the layer independent of layer quality.
    ///
    /// If you want to force it on you must also set the layer quality to [`LayerQuality::Best`] with [`Self::set_layer_quality`].
    /// Otherwise it will only be using the specified layer sampling quality whenever the layer quality is set to [`LayerQuality::Best`].
    pub fn set_layer_sampling_quality(&self, layer_handle: impl AsPtr<AEGP_LayerH>, quality: LayerSamplingQuality) -> Result<(), Error> {
        let v8 = LayerSuite8::new()?;
        call_suite_fn!(v8, AEGP_SetLayerSamplingQuality, layer_handle.as_ptr(), quality.into())
    }

    /// New in 23.0. Returns the track matte layer of [`LayerHandle`]. Returns `None` if there is no track matte layer.
    pub fn track_matte_layer(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<Option<LayerHandle>, Error> {
        let v9 = LayerSuite9::new()?;
        let track_matte_handle = call_suite_fn_single!(v9, AEGP_GetTrackMatteLayer -> ae_sys::AEGP_LayerH, layer_handle.as_ptr())?;
        if track_matte_handle.is_null() {
            Ok(None)
        } else {
            Ok(Some(LayerHandle::from_raw(track_matte_handle)))
        }
    }

    /// New in 23.0. Sets the track matte layer and track matte type of [`LayerHandle`].
    ///
    /// Setting the track matte type as [`TrackMatte::NoTrackMatte`] removes track matte.
    pub fn set_track_matte(&self, layer_handle: impl AsPtr<AEGP_LayerH>, track_matte_layer: Option<LayerHandle>, track_matte_type: TrackMatte) -> Result<(), Error> {
        let v9 = LayerSuite9::new()?;
        call_suite_fn!(v9, AEGP_SetTrackMatte, layer_handle.as_ptr(), track_matte_layer.map_or(std::ptr::null_mut(), |h| h.as_ptr()), track_matte_type.into())
    }

    /// New in 23.0. Removes the track matte layer of [`LayerHandle`].
    pub fn remove_track_matte(&self, layer_handle: impl AsPtr<AEGP_LayerH>) -> Result<(), Error> {
        let v9 = LayerSuite9::new()?;
        call_suite_fn!(v9, AEGP_RemoveTrackMatte, layer_handle.as_ptr())
    }
}

// ――――――――――――――――――――――――――――――――――――――― Types ――――――――――――――――――――――――――――――――――――――――

register_handle!(AEGP_LayerH);
define_handle_wrapper!(LayerHandle, AEGP_LayerH);

define_enum! {
    ae_sys::AEGP_ObjectType,
    ObjectType {
        None        = ae_sys::AEGP_ObjectType_NONE,
        AudioVideo  = ae_sys::AEGP_ObjectType_AV,
        Light       = ae_sys::AEGP_ObjectType_LIGHT,
        Camera      = ae_sys::AEGP_ObjectType_CAMERA,
        Text        = ae_sys::AEGP_ObjectType_TEXT,
        Vector      = ae_sys::AEGP_ObjectType_VECTOR,
        ThreeDModel = ae_sys::AEGP_ObjectType_3D_MODEL,
    }
}

define_enum! {
    ae_sys::AEGP_LayerQuality,
    LayerQuality {
        None      = ae_sys::AEGP_LayerQual_NONE,
        Wireframe = ae_sys::AEGP_LayerQual_WIREFRAME,
        Draft     = ae_sys::AEGP_LayerQual_DRAFT,
        Best      = ae_sys::AEGP_LayerQual_BEST,
    }
}

define_enum! {
    ae_sys::AEGP_LayerSamplingQuality,
    LayerSamplingQuality {
        Bilinear = ae_sys::AEGP_LayerSamplingQual_BILINEAR,
        Bicubic  = ae_sys::AEGP_LayerSamplingQual_BICUBIC,
    }
}

define_enum! {
    ae_sys::AEGP_TrackMatte,
    TrackMatte {
        NoTrackMatte = ae_sys::AEGP_TrackMatte_NO_TRACK_MATTE,
        Alpha        = ae_sys::AEGP_TrackMatte_ALPHA,
        NotAlpha     = ae_sys::AEGP_TrackMatte_NOT_ALPHA,
        Luma         = ae_sys::AEGP_TrackMatte_LUMA,
        NotLuma      = ae_sys::AEGP_TrackMatte_NOT_LUMA,
    }
}

define_enum! {
    ae_sys::AEGP_LTimeMode,
    TimeMode {
        LayerTime = ae_sys::AEGP_LTimeMode_LayerTime,
        CompTime  = ae_sys::AEGP_LTimeMode_CompTime,
    }
}

bitflags::bitflags! {
    pub struct LayerFlags: ae_sys::A_long {
        const NONE                        = ae_sys::AEGP_LayerFlag_NONE                        as ae_sys::A_long;
        const VIDEO_ACTIVE                = ae_sys::AEGP_LayerFlag_VIDEO_ACTIVE                as ae_sys::A_long;
        const AUDIO_ACTIVE                = ae_sys::AEGP_LayerFlag_AUDIO_ACTIVE                as ae_sys::A_long;
        const EFFECTS_ACTIVE              = ae_sys::AEGP_LayerFlag_EFFECTS_ACTIVE              as ae_sys::A_long;
        const MOTION_BLUR                 = ae_sys::AEGP_LayerFlag_MOTION_BLUR                 as ae_sys::A_long;
        const FRAME_BLENDING              = ae_sys::AEGP_LayerFlag_FRAME_BLENDING              as ae_sys::A_long;
        const LOCKED                      = ae_sys::AEGP_LayerFlag_LOCKED                      as ae_sys::A_long;
        const SHY                         = ae_sys::AEGP_LayerFlag_SHY                         as ae_sys::A_long;
        const COLLAPSE                    = ae_sys::AEGP_LayerFlag_COLLAPSE                    as ae_sys::A_long;
        const AUTO_ORIENT_ROTATION        = ae_sys::AEGP_LayerFlag_AUTO_ORIENT_ROTATION        as ae_sys::A_long;
        const ADJUSTMENT_LAYER            = ae_sys::AEGP_LayerFlag_ADJUSTMENT_LAYER            as ae_sys::A_long;
        const TIME_REMAPPING              = ae_sys::AEGP_LayerFlag_TIME_REMAPPING              as ae_sys::A_long;
        const LAYER_IS_3D                 = ae_sys::AEGP_LayerFlag_LAYER_IS_3D                 as ae_sys::A_long;
        const LOOK_AT_CAMERA              = ae_sys::AEGP_LayerFlag_LOOK_AT_CAMERA              as ae_sys::A_long;
        const LOOK_AT_POI                 = ae_sys::AEGP_LayerFlag_LOOK_AT_POI                 as ae_sys::A_long;
        const SOLO                        = ae_sys::AEGP_LayerFlag_SOLO                        as ae_sys::A_long;
        const MARKERS_LOCKED              = ae_sys::AEGP_LayerFlag_MARKERS_LOCKED              as ae_sys::A_long;
        const NULL_LAYER                  = ae_sys::AEGP_LayerFlag_NULL_LAYER                  as ae_sys::A_long;
        const HIDE_LOCKED_MASKS           = ae_sys::AEGP_LayerFlag_HIDE_LOCKED_MASKS           as ae_sys::A_long;
        const GUIDE_LAYER                 = ae_sys::AEGP_LayerFlag_GUIDE_LAYER                 as ae_sys::A_long;
        /// `true` only if pixel motion frame blending is on for the layer.
        const ADVANCED_FRAME_BLENDING     = ae_sys::AEGP_LayerFlag_ADVANCED_FRAME_BLENDING     as ae_sys::A_long;
        /// Used to get/set the state of per-character 3D enablement on a text layer.
        const SUBLAYERS_RENDER_SEPARATELY = ae_sys::AEGP_LayerFlag_SUBLAYERS_RENDER_SEPARATELY as ae_sys::A_long;
        const ENVIRONMENT_LAYER           = ae_sys::AEGP_LayerFlag_ENVIRONMENT_LAYER           as ae_sys::A_long;
    }
}

define_suite_item_wrapper!(
    ae_sys::AEGP_LayerH, LayerHandle,
    suite: LayerSuite,
    effect: aegp::suites::Effect,
    light:  aegp::suites::Light,
    mask:   aegp::suites::Mask,
    stream: aegp::suites::Stream,
    dynstream: aegp::suites::DynamicStream,
    /// A layer
    Layer {
        dispose: ;

        /// Get the index of the layer (0 is the topmost layer in the composition).
        index() -> usize => suite.layer_index,

        /// Get the [`ItemHandle`] of the layer's source item.
        source_item() -> Item => suite.layer_source_item,

        /// Retrieves the ID of the given [`LayerHandle`].
        ///
        /// This is useful when hunting for a specific layer's ID in an [`StreamValue`].
        source_item_id() -> i32 => suite.layer_source_item_id,

        /// Get the AEGP_CompH of the composition containing the layer.
        parent_comp() -> Composition => suite.layer_parent_comp,

        /// Get the name of a layer.
        name(plugin_id: PluginId) -> (String, String) => suite.layer_name,

        /// Get the quality of a layer.
        quality() -> LayerQuality => suite.layer_quality,

        /// Sets the quality of a layer. Undoable.
        set_quality(quality: LayerQuality) -> () => suite.set_layer_quality,

        /// Get flags for a layer.
        flags() -> LayerFlags => suite.layer_flags,

        /// Sets one layer flag at a time. Undoable.
        set_flag(single_flag: LayerFlags, value: bool) -> () => suite.set_layer_flag,

        /// Determines whether the layer's video is visible.
        ///
        /// This is necessary to account for 'solo' status of other layers in the composition; non-solo'd layers are still on.
        is_video_really_on() -> bool => suite.is_layer_video_really_on,

        /// Accounts for solo status of other layers in the composition.
        is_audio_really_on() -> bool => suite.is_layer_audio_really_on,

        /// Get current time, in layer or composition timespace. This value is not updated during rendering.
        ///
        /// NOTE: If a layer starts at other than time 0 or is time-stretched other than 100%, layer time and composition time are distinct.
        current_time(time_mode: TimeMode) -> Time => suite.layer_current_time,

        /// Get time of first visible frame in composition or layer time.
        ///
        /// In layer time, the `in_point` is always 0.
        in_point(time_mode: TimeMode) -> Time => suite.layer_in_point,

        /// Get duration of layer, in composition or layer time, in seconds.
        duration(time_mode: TimeMode) -> Time => suite.layer_duration,

        /// Set duration and in point of layer in composition or layer time. Undo-able.
        set_in_point_and_duration(in_point: Time, duration: Time, time_mode: TimeMode) -> () => suite.set_layer_in_point_and_duration,

        /// Get the offset from the start of the composition to layer time 0, in composition time.
        offset() -> Time => suite.layer_offset,

        /// Set the offset from the start of the composition to the first frame of the layer, in composition time. Undoable.
        set_offset(offset: Time) -> () => suite.set_layer_offset,

        /// Get stretch factor of a layer.
        stretch() -> Ratio => suite.layer_stretch,

        /// Set stretch factor of a layer.
        set_stretch(stretch: Ratio) -> () => suite.set_layer_stretch,

        /// Get transfer mode of a layer.
        transfer_mode() -> ae_sys::AEGP_LayerTransferMode => suite.layer_transfer_mode,

        /// Set transfer mode of a layer. Undoable.
        ///
        /// As of 23.0, when you make a layer a track matte, the layer being matted will be disabled,
        /// as when you do this via the interface.
        set_transfer_mode(transfer_mode: &ae_sys::AEGP_LayerTransferMode) -> () => suite.set_layer_transfer_mode,

        /// Change the order of layers. Undoable.
        ///
        /// To add a layer to the end of the composition, to use `layer_index = -1`
        reorder(layer_index: i32) -> () => suite.reorder_layer,

        /// Given a layer's handle and a time, returns the bounds of area visible with masks applied.
        masked_bounds(time_mode: TimeMode, time: Time) -> FloatRect => suite.layer_masked_bounds,

        /// Returns a layer's object type.
        object_type() -> ObjectType => suite.layer_object_type,

        /// Is the footage item a 3D layer. All AV layers are either 2D or 3D.
        is_3d() -> bool => suite.is_layer_3d,

        /// Is the footage item a 2D layer. All AV layers are either 2D or 3D.
        is_2d() -> bool => suite.is_layer_2d,

        /// Given composition time and a layer, see if the layer will render.
        ///
        /// Time mode is either [`TimeMode::LayerTime`] or [`TimeMode::CompTime`].
        is_video_active(time_mode: TimeMode, time: Time) -> bool => suite.is_video_active,

        /// Is the layer used as a track matte?
        is_used_as_track_matte(fill_must_be_active: bool) -> bool => suite.is_layer_used_as_track_matte,

        /// Does this layer have a Track Matte?
        does_layer_have_track_matte() -> bool => suite.does_layer_have_track_matte,

        /// Given a time in composition space, returns the time relative to the layer source footage.
        convert_comp_to_layer_time(comp_time: Time) -> Time => suite.convert_comp_to_layer_time,

        /// Given a time in layer space, find the corresponding time in composition space.
        convert_layer_to_comp_time(layer_time: Time) -> Time => suite.convert_layer_to_comp_time,

        /// Used by the dancing dissolve transfer function.
        dancing_rand_value(time: Time) -> i32 => suite.layer_dancing_rand_value,

        /// Supplies the layer's unique ID. This ID never changes during the lifetime of the project.
        id() -> LayerId => suite.layer_id,

        /// Given a layer handle and time, returns the layer-to-world transformation matrix.
        to_world_xform(time: Time) -> Matrix4 => suite.layer_to_world_xform,

        /// Given a layer handle, the current (composition) time, and the requested view time, returns the translation between the user's view and the layer, corrected for the composition's current aspect ratio.
        to_world_xform_from_view(comp_time: Time, view_time: Time) -> Matrix4 => suite.layer_to_world_xform_from_view,

        /// Sets the name of a layer. Undo-able.
        set_name(new_name: &str) -> () => suite.set_layer_name,

        /// Retrieves the handle to a layer's parent (none if not parented).
        parent() -> Option<LayerHandle> => suite.layer_parent,

        /// Sets a layer's parent layer.
        set_parent(parent_handle: LayerHandle) -> () => suite.set_layer_parent,

        /// Deletes a layer. Can you believe it took us three suite versions to add a delete function? Neither can we.
        delete() -> () => suite.delete_layer,

        /// Duplicates the layer. Undoable.
        duplicate() -> Layer => suite.duplicate_layer,

        /// Gets a layer's [`LabelId`].
        label() -> LabelId => suite.layer_label,

        /// Sets a layer's [`LabelId`]. Undoable.
        set_label(label_id: LabelId) -> () => suite.set_layer_label,

        /// New in CC. Get the sampling quality of a layer.
        ///
        /// Layer sampling quality is one of the following flags:
        ///
        /// - [`LayerSamplingQuality::Bilinear`]
        /// - [`LayerSamplingQuality::Bicubic`]
        sampling_quality() -> LayerSamplingQuality => suite.layer_sampling_quality,

        /// New in CC. Sets the sampling quality of a layer (see flag values above).
        ///
        /// Option is explicitly set on the layer independent of layer quality.
        ///
        /// If you want to force it on you must also set the layer quality to [`LayerQuality::Best`] with [`Self::set_quality`].
        /// Otherwise it will only be using the specified layer sampling quality whenever the layer quality is set to [`LayerQuality::Best`].
        set_sampling_quality(quality: LayerSamplingQuality) -> () => suite.set_layer_sampling_quality,

        /// New in 23.0. Returns the track matte layer of [`LayerHandle`]. Returns `None` if there is no track matte layer.
        track_matte() -> Option<LayerHandle> => suite.track_matte_layer,

        /// New in 23.0. Sets the track matte layer and track matte type of [`LayerHandle`].
        ///
        /// Setting the track matte type as [`TrackMatte::NoTrackMatte`] removes track matte.
        set_track_matte(track_matte_layer: Option<LayerHandle>, track_matte_type: TrackMatte) -> () => suite.set_track_matte,

        /// New in 23.0. Removes the track matte layer of [`LayerHandle`].
        remove_track_matte() -> () => suite.remove_track_matte,

        // ―――――――――――――――――――――――――――― Effect suite functions ――――――――――――――――――――――――――――

        /// Get the number of effects applied to this layer.
        num_effects() -> i32 => effect.layer_num_effects,

        /// Retrieves (by index) a reference to an effect applied to this layer.
        effect_by_index(plugin_id: PluginId, index: i32) -> aegp::Effect => effect.layer_effect_by_index,

        /// Apply an effect to a given layer. Returns the newly-created [`Effect`](aegp::Effect).
        apply_effect(plugin_id: PluginId, installed_effect_key: InstalledEffectKey) -> aegp::Effect => effect.apply_effect,

        // ―――――――――――――――――――――――――――― Light suite functions ――――――――――――――――――――――――――――

        /// Retrieves the [`LightType`] of the specified camera layer
        light_type() -> LightType => light.light_type,

        /// Sets the [`LightType`] for the specified camera layer.
        set_light_type(light_type: LightType) -> () => light.set_light_type,

        // ―――――――――――――――――――――――――――― Mask suite functions ――――――――――――――――――――――――――――

        /// Counts the masks applied to a layer.
        num_masks() -> i32 => mask.layer_num_masks,

        /// Given a layer handle and mask index, returns a pointer to the mask handle.
        mask_by_index(mask_index: i32) -> Mask => mask.layer_mask_by_index,

        // ―――――――――――――――――――――――――――― Stream suite functions ――――――――――――――――――――――――――――

        /// Determines if the given stream is appropriate for this layer.
        is_stream_legal(stream: LayerStream) -> bool => stream.is_stream_legal,

        /// Get layer's data stream.
        ///
        /// Note that this will not provide keyframe access; Use the [`KeyframeSuite`](aegp::suites::Keyframe) instead.
        new_layer_stream(plugin_id: PluginId, stream_name: LayerStream) -> aegp::Stream => stream.new_layer_stream,

        /// NOTE: This convenience function is only valid for streams with primitive data types, and not for `StreamType::ArbBlock`, `StreamType::Marker` or `StreamType::MaskOutline`.
        /// For these and other complex types, use [`Stream::new_value()`](aegp::Stream::new_value), described above.
        layer_stream_value(stream: LayerStream, time_mode: TimeMode, time: Time, pre_expression: bool) -> StreamValue => stream.layer_stream_value,

        /// Retrieves the [`Stream`] corresponding to the layer. This function is used to initiate a recursive walk of the layer's streams.
        new_stream_for_layer(plugin_id: PluginId) -> aegp::Stream => dynstream.new_stream_ref_for_layer,
    }
);

impl Layer {
    /// Get the active layer. If a Layer or effect controls palette is active, the active layer is that associated with the front-most tab in the window.
    ///
    /// If a composition or timeline window is active, the active layer is the selected layer (if only one is selected; otherwise `None` is returned).
    pub fn active() -> Result<Option<Layer>, Error> {
        LayerSuite::new()?.active_layer().map(|h| h.map(|x| Layer::from_handle(x, false)))
    }

    /// Creates a new mask on the referenced layer, with zero nodes. Returns new mask and its index.
    pub fn create_new_mask(&self) -> Result<(Mask, i32), Error> {
        let Ok(ref suite) = *self.mask else { return Err(Error::MissingSuite); };
        suite.create_new_mask(self.handle.as_ptr()).map(|(mask, idx)| (mask.into(), idx))
    }

    /// Creates a new [`aegp::LayerRenderOptions`] from this layer.
    pub fn layer_render_options(&self, plugin_id: PluginId) -> Result<aegp::LayerRenderOptions, Error> {
        aegp::LayerRenderOptions::from_layer(self.handle.as_ptr(), plugin_id)
    }

}