mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-06 18:41:26 +00:00
AEP import: an effect record is read only in the written or the default state
A record in another tdsb state is reported as damaged data again, as before the default-state change. The synthetic test checks Levels' recorded popup directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e730da1435
commit
e25025545a
4 files changed
+29
-7
No files matched your search
@@ -382,17 +382,18 @@ impl<'f, 'a> EffectParam<'f, 'a> {
|
||||
self.recorded_value()
|
||||
}
|
||||
|
||||
/// The value in the value record whatever the record's state (`tdsb`): what
|
||||
/// [`EffectParam::stored_value`] reads, also from a record in the default state (`tdsb` 3).
|
||||
/// Lumetri Color writes the values of its hidden parameters so (High Dynamic Range, HDR
|
||||
/// Specular, Look: 1200 `lumetri-01-set-all`, `-02-sec-top`, `-06-sec-light`; After Effects
|
||||
/// reads back the recorded values). `None` with keyframes or without a record.
|
||||
/// The value in the value record when the record is written (`tdsb` 1) or in the default
|
||||
/// state (`tdsb` 3): what [`EffectParam::stored_value`] reads, also from a default-state
|
||||
/// record. Lumetri Color writes the values of its hidden parameters so (High Dynamic Range,
|
||||
/// HDR Specular, Look: 1200 `lumetri-01-set-all`, `-02-sec-top`, `-06-sec-light`; After
|
||||
/// Effects reads back the recorded values). `None` with keyframes, without a record or for a
|
||||
/// record in any other state (no sample has one; the importer reports it as damaged).
|
||||
pub fn recorded_value(&self) -> Option<ParamValue> {
|
||||
let p = self.stored?;
|
||||
if self.kind == ParamKind::Layer {
|
||||
return be_u32(p.chunk().child(b"tdpi")?.bytes()?, 0).map(ParamValue::Layer);
|
||||
}
|
||||
if p.is_animated() {
|
||||
if !matches!(p.flags(), Some(1 | 3)) || p.is_animated() {
|
||||
return None;
|
||||
}
|
||||
let v = p.static_value()?;
|
||||
|
||||
@@ -133,6 +133,8 @@ struct ParamOut<'a> {
|
||||
col: &'a mut Collector,
|
||||
}
|
||||
|
||||
/// Imports one parameter of `fx` into its EffectCraft property: its keyframes, the value in its
|
||||
/// record or the `pard` default, with a loss for what cannot be read or mapped.
|
||||
fn import_param(fx: &AepEffect<'_, '_>, map: &EffectMap, p: &EffectParam<'_, '_>, cx: &LayerCtx<'_>, shown: &str, out: ParamOut<'_>) {
|
||||
let ParamOut { group, refs, col } = out;
|
||||
if carries_nothing(p) {
|
||||
|
||||
@@ -339,6 +339,7 @@ fn effect_values(p: &Project) -> Vec<(String, effectcraft_keyframe::Value)> {
|
||||
out
|
||||
}
|
||||
|
||||
/// A default-state record (`tdsb` 3) reads like a written one, except through `stored_value`.
|
||||
#[test]
|
||||
fn a_default_state_effect_record_holds_the_value() {
|
||||
// After Effects writes the values of some parameters as records in the default state (`tdsb`
|
||||
@@ -368,6 +369,7 @@ fn a_default_state_effect_record_holds_the_value() {
|
||||
assert_eq!(seen, 1);
|
||||
}
|
||||
|
||||
/// Default-state records import with their values and no loss, like written records.
|
||||
#[test]
|
||||
fn default_state_effect_records_import_like_written_ones() {
|
||||
// Before, such a record was reported as an unreadable value and the default used.
|
||||
@@ -383,4 +385,21 @@ fn default_state_effect_records_import_like_written_ones() {
|
||||
let (p, _) = run(&with_record("ADBE Glo2-0011", 3, 0.75), true);
|
||||
let mid: Vec<_> = effect_values(&p).into_iter().filter(|(id, _)| id == "abMidpoint").collect();
|
||||
assert_eq!(mid, vec![("abMidpoint".to_string(), effectcraft_keyframe::Value::Scalar(75.0))]);
|
||||
// Levels' Clip To Output Black (`-0008`) recorded as After Effects' item 1, On: EffectCraft's
|
||||
// On is item 1 of Off, On, Off for 32 bpc Color (the default is item 2).
|
||||
let (p, _) = run(&with_record("ADBE Easy Levels2-0008", 3, 1.0), true);
|
||||
let clip: Vec<_> = effect_values(&p).into_iter().filter(|(id, _)| id == "clipToOutputBlack").collect();
|
||||
assert_eq!(clip, vec![("clipToOutputBlack".to_string(), effectcraft_keyframe::Value::Enum(1))]);
|
||||
}
|
||||
|
||||
/// A record in a state other than 1 or 3 is not read and is reported.
|
||||
#[test]
|
||||
fn an_effect_record_in_another_state_is_reported_as_damaged() {
|
||||
// Only the written (1) and the default (3) state hold a value; any other state is not read.
|
||||
for tdsb in [0, 2, 5] {
|
||||
let (_, r) = run(&with_record("ADBE Glo2-0011", tdsb, 0.75), true);
|
||||
let r = r.unwrap();
|
||||
let damaged = losses(&r, LossCategory::DamagedData);
|
||||
assert!(damaged.iter().any(|l| l.contains("unreadable value")), "tdsb {tdsb}: {:?}", r.report.losses);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -437,7 +437,7 @@ The expression engine is project-wide (`LIST ExEn/Utf8`, `javascript-1.0`). API:
|
||||
| **float slider valid minimum, slider range** | `pard` | 104; 112 / 116 | f32; f32 x 2 | the smallest valid value (Exposure -100, Offset -2, Gamma Correction 0.1 as f32) and the range the slider covers (Exposure -4..4, Offset -0.5..0.5, Gamma 0.1..10); the slider range is repeated as `tdum` / `tduM` (f64) in each value record (391-394). @120 holds the default again as f32 (Gamma 1.0, Blurriness 25), u16 @124 the display decimals (**H**: Exposure 2, Offset 4) | 389, 391-394 | V |
|
||||
| **definitions of a second instance** | top-level `LIST EfdG` | | | one `EfDf` per effect TYPE (`tdmn` = match name, then a copy of ONE instance's `sspc`, including that instance's value records); the `EfDC` count before them is a little-endian u32. When two layers carry the same effect type, only one instance keeps a full `parT`; **the other has an empty `LIST parT` (4 bytes)** but its own `tdgp` (input layer `-0000` with its own `tdpi`, the button record, and its value records). Its definitions come from the `EfdG` copy by match name, its values from its own records, never from the copy (383: the copy holds S100's Start [0.1, 0.2]; L's Ramp reads the 368 defaults). Which instance keeps the full `parT` is **?** (in 383 S100 is both first in the file and newest). The `EfDf` order follows the stack order (365 / 366); `Pefl/pjef` does not | 365, 366, 383 | V |
|
||||
| popup items | `parT/pdnm/Utf8` | | text | items separated by `\|` | 077 (`Item 1\|Item 2\|Item 3`), 214 | V |
|
||||
| stored value | `sspc/tdgp/<match>-NNNN` | | `tdbs` | present only for a parameter that differs from its default; `tdsb` 1, `cdat` as in 3.4. **Absent record = the `pard` default.** Some parameters are recorded in the default state (`tdsb` 3) and the record still holds the value After Effects uses: every one of the 25 such records in the corpus and the samples differs from its `pard` default and reads back as recorded (Lumetri Color's hidden controls High Dynamic Range, HDR Specular, the second Look, Legacy Tone And Saturation, Face Detection, Auto Color, Auto Intensity and SemanticAutoTone Progress, 1200; Cryptomatte's Selection Mode on, 1400). No effect record in the default state holds a value that does not count. The reader takes the value whatever the state (`EffectParam::recorded_value`, `EffectParam::value`); `EffectParam::stored_value` keeps to written records. That other effects record values so is H (no sample shows one) | see below; 1200, 1400 | V (1200, 1400), H (other effects) |
|
||||
| stored value | `sspc/tdgp/<match>-NNNN` | | `tdbs` | present only for a parameter that differs from its default; `tdsb` 1, `cdat` as in 3.4. **Absent record = the `pard` default.** Some parameters are recorded in the default state (`tdsb` 3) and the record still holds the value After Effects uses: every one of the 25 such records in the corpus and the samples differs from its `pard` default and reads back as recorded (Lumetri Color's hidden controls High Dynamic Range, HDR Specular, the second Look, Legacy Tone And Saturation, Face Detection, Auto Color, Auto Intensity and SemanticAutoTone Progress, 1200; Cryptomatte's Selection Mode on, 1400). No effect record in the default state holds a value that does not count. The reader takes the value in either state (`EffectParam::recorded_value`, `EffectParam::value`); `EffectParam::stored_value` keeps to written records, and a record in any other state (no sample has one) is not read: the importer reports it as damaged data. That other effects record values so is H (no sample shows one) | see below; 1200, 1400 | V (1200, 1400), H (other effects) |
|
||||
|
||||
Stored values by type (`tdsb` 1, or 3 as above):
|
||||
|
||||
|
||||
Reference in new issue
Block a user