mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-13 23:08:23 +00:00
Render: shapes filled with their group's fill (a:grpFill) show it
A shape inside a group whose fill is grpFill drew nothing (the fill was read but the renderer skipped it). PowerPoint fills it with the group's fill laid out over the whole group: a gradient or pattern runs on across the children rather than restarting in each. Groups without a fill of their own pass their parent's on. Wild synthetic set: group fills slide 27.33 -> 7.88. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7d642e12c9
commit
47dfaaca61
2 files changed
+46
-1
No files matched your search
@@ -244,11 +244,14 @@ pub struct Renderer {
|
||||
/// The transform a top-level shape is drawn under (slide to pixels); a group's children draw
|
||||
/// under it combined with the group's child-coordinate mapping, which must not scale lines.
|
||||
view: Affine,
|
||||
/// The fill of the group being drawn, with the transform and box it is laid out in: children
|
||||
/// filled with the group's fill (`a:grpFill`) show it, as one fill across the whole group.
|
||||
group_fill: Option<(Fill, Affine, Rect)>,
|
||||
}
|
||||
|
||||
impl Default for Renderer {
|
||||
fn default() -> Self {
|
||||
Renderer { resources: Resources::new(), paths: Vec::new(), bg: None, drawn: None, view: Affine::IDENTITY }
|
||||
Renderer { resources: Resources::new(), paths: Vec::new(), bg: None, drawn: None, view: Affine::IDENTITY, group_fill: None }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,9 +782,16 @@ impl Renderer {
|
||||
ShapeKind::Group { children, child } => {
|
||||
let ch = Rect::new(child.x, child.y, child.x + child.w, child.y + child.h);
|
||||
let gm = parent * deckcraft_geom::group_child_affine(&x, ch);
|
||||
// A group's own fill (a group filled with its parent's passes that on).
|
||||
let own = match &s.fill {
|
||||
Some(Fill::Group) | None => self.group_fill.clone(),
|
||||
Some(fl) => Some((fl.clone(), m, Rect::new(0.0, 0.0, x.w, x.h))),
|
||||
};
|
||||
let outer = std::mem::replace(&mut self.group_fill, own);
|
||||
for c in children {
|
||||
self.shape(ctx, f, rctx, c, gm, depth + 1);
|
||||
}
|
||||
self.group_fill = outer;
|
||||
}
|
||||
ShapeKind::Table(t) => {
|
||||
// Rows grow with their text: what the cells cover, borders included.
|
||||
@@ -925,6 +935,15 @@ impl Renderer {
|
||||
let path = (view.inverse() * m) * sp.path.clone();
|
||||
paint::fill_path(ctx, rctx, bg, *ph, &path, slide, *view, sp.fill, fade(i).fill, f.pres);
|
||||
}
|
||||
// `grpFill`: the group's fill, laid out over the whole group.
|
||||
(Fill::Group, _) => {
|
||||
if let Some((gf, gm, gbox)) = &self.group_fill
|
||||
&& gm.determinant().abs() > 1e-12
|
||||
{
|
||||
let path = (gm.inverse() * m) * sp.path.clone();
|
||||
paint::fill_path(ctx, rctx, gf, None, &path, *gbox, *gm, sp.fill, fade(i).fill, f.pres);
|
||||
}
|
||||
}
|
||||
_ => paint::fill_path(ctx, rctx, fl, fill_ph, &sp.path, Rect::new(0.0, 0.0, w, h), m, sp.fill, fade(i).fill, f.pres),
|
||||
}
|
||||
ctx.set_fill_rule(peniko::Fill::NonZero);
|
||||
|
||||
@@ -287,3 +287,29 @@ fn a_pictures_border_lies_outside_the_picture() {
|
||||
let img = render_slide(&picture_deck(Some(line)), 0, &RenderOpts { scale: 1.0, ..Default::default() });
|
||||
assert!(close(img.pixel(120, 96), RED, 30) && close(img.pixel(140, 96), WHITE, 10), "{:?}", img.pixel(140, 96));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn children_filled_with_the_group_fill_show_one_fill_across_the_group() {
|
||||
use deckcraft_model::style::{Gradient, GradientShape, GradientStop};
|
||||
let stops = vec![GradientStop { pos: 0.0, color: ColorRef::rgb(RED) }, GradientStop { pos: 1.0, color: ColorRef::rgb(Rgba::rgb(0, 0, 255)) }];
|
||||
let grad = Fill::Gradient(Gradient { stops, shape: GradientShape::Linear { angle: 0.0, scaled: false }, rotate_with_shape: true });
|
||||
let child = |id: u32, x: f64| Shape {
|
||||
id: ShapeId(id),
|
||||
xfrm: Some(Xfrm::new(x, 0.0, 90.0, 100.0)),
|
||||
fill: Some(Fill::Group),
|
||||
line: no_line(),
|
||||
..Default::default()
|
||||
};
|
||||
let g = Shape {
|
||||
id: ShapeId(16),
|
||||
xfrm: Some(Xfrm::new(100.0, 100.0, 200.0, 100.0)),
|
||||
fill: Some(grad),
|
||||
kind: ShapeKind::Group { children: vec![child(17, 0.0), child(18, 110.0)], child: Xfrm::new(0.0, 0.0, 200.0, 100.0) },
|
||||
..Default::default()
|
||||
};
|
||||
let img = render(vec![g]);
|
||||
let (l, r) = (img.pixel(105, 150), img.pixel(295, 150));
|
||||
assert!(l[0] > 200 && l[2] < 60, "left end of the group's gradient {l:?}");
|
||||
assert!(r[2] > 200 && r[0] < 60, "right end {r:?}");
|
||||
assert!(close(img.pixel(200, 150), WHITE, 5), "the gap between the children stays empty");
|
||||
}
|
||||
Reference in new issue
Block a user