mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 10:03:51 +00:00
* Add craft-aep: After Effects project container and typed view from EffectCraft The .aep (RIFX 'Egg!') and .aepx container as a lossless chunk tree that writes back byte for byte, and the typed, read-only view over it: compositions, layers, properties, keyframes, effects and their settings data, masks, shapes, text documents, markers, cameras, track mattes, footage, project settings and the saving version. Moved from effectcraft crates/aep/src/container at f6b4da0a with its unit tests, the container integration tests, the aepdump and stubicc tools and docs/aep-format.md. The match-name tables (effect_map, style_map), the importer and the exporter are written against EffectCraft's model and stay there. ExtractorChannels::sources takes the caller's name for a copied channel instead of an EffectCraft constant. * craft-aep: read the Photoshop layer a footage item shows A footage item made from one layer of a .psd stores the layer in its `opti` (tag `8BPS`): the layer record's index (big-endian i32 at 14, -1 for the merged image), the document's two sides (little-endian u32 at 32 and 36), the layer's bounds (little-endian i32 at 78, 82, 86, 90) and its name (UTF-8 from 344). `Footage::psd_layer` reads them as `PsdLayer`. The format part of storytold/effectcraft#3449; the importer's use of it stays in EffectCraft. --------- Co-authored-by: arnav-hiwarkar <asharnav2008@gmail.com>
69 lines
2.6 KiB
Rust
69 lines
2.6 KiB
Rust
//! Track mattes: the matte mode and the matte layer, two fields of the layer header `ldta`.
|
|
//!
|
|
//! Cases 402-408 (a 960x540 grey solid `M` above the red solid `L`): setting a track matte on a
|
|
//! layer changes exactly two fields of that layer's `ldta` (and the video switch of the matte
|
|
//! layer, which After Effects turns off by itself):
|
|
//!
|
|
//! * byte 107 (`u8`; bytes 104..106 are 0 in every file of the corpus): the matte mode, 1 alpha
|
|
//! (403), 2 alpha inverted (404), 3 luma (405), 4 luma inverted (406), 0 no matte (every other
|
|
//! case);
|
|
//! * the `u32` at byte 160: the id of the matte layer (`ldta` byte 0 of that layer), `0x1d` = `M`
|
|
//! in 403-406 and 408, `0x1b` = `L` in 407, where the matte layer is BELOW the layer it mattes.
|
|
//! 0 in every file without a matte.
|
|
//!
|
|
//! A matte mode with a matte layer id of 0 (or an `ldta` too short to hold the id) has no case in
|
|
//! the corpus: After Effects 2026 writes the id. Before After Effects 23.0 a track matte always
|
|
//! used the layer directly above (the documented behaviour of those versions), so such a matte is
|
|
//! returned with [`TrackMatte::layer`] `None` and the caller decides.
|
|
|
|
use crate::Layer;
|
|
use crate::bytes::be_u32;
|
|
|
|
/// The matte mode (`ldta` byte 107).
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum MatteMode {
|
|
/// `1` (403)
|
|
Alpha,
|
|
/// `2` (404)
|
|
AlphaInverted,
|
|
/// `3` (405)
|
|
Luma,
|
|
/// `4` (406)
|
|
LumaInverted,
|
|
/// Any other non-zero value (not sampled).
|
|
Other(u8),
|
|
}
|
|
|
|
impl MatteMode {
|
|
/// The mode for a stored code; `None` for 0 (no matte).
|
|
pub fn from_code(code: u8) -> Option<Self> {
|
|
Some(match code {
|
|
0 => return None,
|
|
1 => Self::Alpha,
|
|
2 => Self::AlphaInverted,
|
|
3 => Self::Luma,
|
|
4 => Self::LumaInverted,
|
|
other => Self::Other(other),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// The track matte of a layer.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct TrackMatte {
|
|
pub mode: MatteMode,
|
|
/// The id of the matte layer (`u32` at `ldta` byte 160); `None` when it is 0 or the header is
|
|
/// too short to hold it (the older "layer above" form, see the module documentation).
|
|
pub layer: Option<u32>,
|
|
}
|
|
|
|
impl<'f, 'a> Layer<'f, 'a> {
|
|
/// The track matte of the layer, `None` when it has none (or the header is unreadable).
|
|
pub fn track_matte(&self) -> Option<TrackMatte> {
|
|
let ldta = self.chunk().child(b"ldta")?.bytes()?;
|
|
let mode = MatteMode::from_code(*ldta.get(107)?)?;
|
|
let layer = be_u32(ldta, 160).filter(|id| *id != 0);
|
|
Some(TrackMatte { mode, layer })
|
|
}
|
|
}
|