mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 04:37:29 -06:00
Merge pull request #4620 from Taha-Mahmoodi/fix/4420-affinity-fill-color
Fix #4420: import attached Affinity recolor adjustment layers as color overlays
This commit is contained in:
@@ -441,8 +441,34 @@ impl<'s, 'a, 'b> Reader<'s, 'a, 'b> {
|
||||
_ => self.warn("layer effects of other kinds (left out)"),
|
||||
}
|
||||
}
|
||||
for a in s.objs(id, b"AdCh") {
|
||||
if let Some(effect) = self.attached_recolor(a) {
|
||||
out.push(effect);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
fn attached_recolor(&mut self, a: ObjId) -> Option<Effect> {
|
||||
let s = self.s;
|
||||
if !s.is(a, b"RcRA") {
|
||||
return None;
|
||||
}
|
||||
if s.bool(a, b"Visi") == Some(false) {
|
||||
return None;
|
||||
}
|
||||
let adjp = s.obj(a, b"AdjP")?;
|
||||
if !s.is(adjp, b"RecP") {
|
||||
return None;
|
||||
}
|
||||
let h = s.f64(adjp, b"RecH").unwrap_or(0.0);
|
||||
let sat = s.f64(adjp, b"RecS").unwrap_or(1.0);
|
||||
let l = s.f64(adjp, b"RecL").unwrap_or(0.5);
|
||||
let (r, g, b) = crate::paint::hsl(h, sat, l);
|
||||
let color = crate::paint::Color::Rgb { r, g, b, a: 1.0 };
|
||||
let opacity = s.f64(a, b"Opac").filter(|v| v.is_finite()).unwrap_or(1.0).clamp(0.0, 1.0);
|
||||
let blend = self.effect_blend(a);
|
||||
Some(Effect::ColorOverlay { color, opacity, blend })
|
||||
}
|
||||
|
||||
/// A layer effect's blend mode (`BlnM`); unknown modes are Normal (warned).
|
||||
fn effect_blend(&mut self, e: ObjId) -> Blend {
|
||||
@@ -479,6 +505,8 @@ impl<'s, 'a, 'b> Reader<'s, 'a, 'b> {
|
||||
Some(_) => self.warn("several pixel masks on one layer (only the first is used)"),
|
||||
None => {}
|
||||
}
|
||||
} else if class == Tag::of(b"RcRA") {
|
||||
// Handled in `effects` as a colour overlay.
|
||||
} else {
|
||||
self.warn(format!("{} layers attached to other layers (imported without them)", describe(class)));
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ fn color_within(r: &mut Reader, id: ObjId, depth: u32) -> Option<Color> {
|
||||
})
|
||||
}
|
||||
|
||||
fn hsl(h: f64, s: f64, l: f64) -> (f64, f64, f64) {
|
||||
pub(crate) fn hsl(h: f64, s: f64, l: f64) -> (f64, f64, f64) {
|
||||
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
|
||||
let hp = (h.rem_euclid(1.0)) * 6.0;
|
||||
let x = c * (1.0 - (hp % 2.0 - 1.0).abs());
|
||||
@@ -355,3 +355,17 @@ pub(crate) fn stroke(r: &mut Reader, pen: ObjId, ldsc: ObjId, local: Affine, beh
|
||||
}
|
||||
Some(Stroke { paint, width, cap, join, miter_limit, align, dash, behind })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hsl_lightness_1_is_white_and_0_is_black() {
|
||||
assert_eq!(hsl(0.0, 1.0, 1.0), (1.0, 1.0, 1.0));
|
||||
assert_eq!(hsl(0.5, 1.0, 1.0), (1.0, 1.0, 1.0));
|
||||
assert_eq!(hsl(0.0, 1.0, 0.0), (0.0, 0.0, 0.0));
|
||||
let (r, g, b) = hsl(0.0, 1.0, 0.5);
|
||||
assert!((r - 1.0).abs() < 1e-6 && g.abs() < 1e-6 && b.abs() < 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,3 +702,21 @@ fn objects_beyond_the_document_range_are_left_out() {
|
||||
assert!(names.iter().any(|n| n == "Magenta box") && !names.iter().any(|n| n == "Far box"), "{names:?}");
|
||||
assert!(loaded.warnings.iter().any(|w| w.contains("beyond the document range")), "{:?}", loaded.warnings);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_af_g_symbol_fill_is_white() {
|
||||
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../test.af");
|
||||
let Ok(bytes) = std::fs::read(&path) else {
|
||||
return;
|
||||
};
|
||||
let loaded = vectorcraft_engine::cmd::fileio::load("test.af", &bytes).unwrap();
|
||||
assert!(!loaded.preview_only, "{:?}", loaded.warnings);
|
||||
let d = &loaded.doc;
|
||||
// The visible "g41" group holds the "g" symbol (path43).
|
||||
let g41 = all(d).into_iter().find(|n| n.visible && n.name.as_deref() == Some("g41")).expect("visible g41 group must be present");
|
||||
let path43 = g41.children().and_then(|c| c.iter().find(|n| n.name.as_deref() == Some("path43"))).expect("path43 inside g41 must be present");
|
||||
let Some(AppearanceItem::Fill(fill)) = path43.appearance.items.first() else {
|
||||
panic!("path43 has no fill: {:?}", path43.appearance);
|
||||
};
|
||||
assert_eq!(fill.paint, Paint::solid(Color::rgb(1.0, 1.0, 1.0)), "The 'g' symbol must have a white fill in test.af, got {:?}", fill.paint);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user