Photoshop CC brush presets store the Dual Brush spacing the panel shows in the dual tip (dualBrush > Brsh > Spcn), and also write a Spcn of 100 % on the dualBrush object beside it. The importer read that outer value last, so every dual brush came in at 100 % spacing (the reporter's 10 %, 18 % and 24 % presets all did), which changes the stroke a lot. The tip's spacing now wins; the outer value only stands in for a tip without one. Fixes part 1 of #4809 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1168a9dd73
commit
87aed12d62
|
|
@ -429,7 +429,12 @@ impl Mapper<'_> {
|
|||
}
|
||||
dual.mode = mask_mode(enum_value(db, "BlnM")).unwrap_or(MaskMode::Multiply);
|
||||
dual.flip = boolean(db, "Flip").unwrap_or(false);
|
||||
if let Some(v) = pct(db, "Spcn") {
|
||||
// The dual tip's own spacing (`Brsh › Spcn`) is what Photoshop's Dual Brush panel
|
||||
// shows. The `Spcn` beside it on the `dualBrush` object reads 100 % in Photoshop CC
|
||||
// presets whatever the panel says (#4809), so it only stands in for a tip without one.
|
||||
if obj(db, "Brsh").and_then(|t| pct(t, "Spcn")).is_none()
|
||||
&& let Some(v) = pct(db, "Spcn")
|
||||
{
|
||||
dual.spacing = v.clamp(0.01, 10.0);
|
||||
}
|
||||
dual.scatter = self.dynamic(obj(db, "scatterDynamics")).jitter;
|
||||
|
|
|
|||
|
|
@ -399,3 +399,32 @@ fn low_depth_height_texture_brush_paints() {
|
|||
assert_eq!(covered, 36, "{mode}: the stroke painted {covered} of 36 pixels");
|
||||
}
|
||||
}
|
||||
|
||||
/// #4809: Photoshop CC presets store the dual tip's spacing in `dualBrush › Brsh › Spcn` and a
|
||||
/// `Spcn` of 100 % on the `dualBrush` object beside it; the tip's value is the panel's. A tip
|
||||
/// without one still takes the outer value.
|
||||
#[test]
|
||||
fn dual_brush_spacing_comes_from_its_tip() {
|
||||
let dual = |tip: Descriptor| {
|
||||
Descriptor::new("brushPreset")
|
||||
.with("Nm ", t("SPACING 10%"))
|
||||
.with("Brsh", Value::Descriptor(Descriptor::new("computedBrush").with("Dmtr", u(b"#Pxl", 30.0))))
|
||||
.with(
|
||||
"dualBrush",
|
||||
Value::Descriptor(
|
||||
Descriptor::new("dualBrush")
|
||||
.with("useDualBrush", Value::Boolean(true))
|
||||
.with("Brsh", Value::Descriptor(tip))
|
||||
.with("BlnM", en("BlnM", "Ovrl"))
|
||||
.with("Spcn", prc(100.0)),
|
||||
),
|
||||
)
|
||||
};
|
||||
let spacing = |preset: Descriptor| {
|
||||
let bytes = write_v6(2, &[], &[], &[preset], false).unwrap();
|
||||
read_abr(&bytes, "Test").unwrap().presets[0].brush.dual_brush.spacing
|
||||
};
|
||||
let tip = Descriptor::new("computedBrush").with("Dmtr", u(b"#Pxl", 40.0));
|
||||
assert!((spacing(dual(tip.clone().with("Spcn", prc(10.0)))) - 0.10).abs() < 1e-6, "the tip's 10 %, not the outer 100 %");
|
||||
assert!((spacing(dual(tip)) - 1.0).abs() < 1e-6, "no tip spacing: the outer value");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue