Render: pictures reflect their pixels

A reflection on a picture (PowerPoint's "reflected" picture styles) drew
nothing: the reflection painted the shape's fill, and a picture has none.
It now mirrors the picture itself, clipped to its outline, under the same
fade.

Wild synthetic set: picture effects slide 16.77 -> 16.00 (the rest is
3-D camera rotation, not modelled).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-13 07:31:17 -04:00
parent 7cb62d5197
commit ee71879f80
2 changed files with 28 additions and 2 deletions

View File

@ -1284,9 +1284,20 @@ impl Renderer {
ctx.set_transform(Affine::IDENTITY);
ctx.push_layer(None, None, Some(rf.start_alpha.clamp(0.0, 1.0) as f32 * 0.6), None, Some(blur(rf.blur * 0.5 * k)));
ctx.set_transform(mm);
if let Some(fl) = fill {
paint::fill_path(ctx, rctx, fl, ph, &silhouette, Rect::new(0.0, 0.0, w, h), mm, FillMode::Norm, 1.0, rctx.pres);
match content {
// A picture reflects its pixels (it has no fill of its own).
Some(pf) => {
ctx.push_clip_layer(&silhouette);
paint::picture(ctx, rctx.pres, pf, Rect::new(0.0, 0.0, w, h), mm, None);
ctx.pop_layer();
}
None => {
if let Some(fl) = fill {
paint::fill_path(ctx, rctx, fl, ph, &silhouette, Rect::new(0.0, 0.0, w, h), mm, FillMode::Norm, 1.0, rctx.pres);
}
}
}
ctx.set_transform(mm);
// Fade mask.
ctx.push_layer(None, Some(peniko::BlendMode::new(peniko::Mix::Normal, peniko::Compose::DestIn)), None, None, None);
let grad = peniko::Gradient::new_linear(Point::new(0.0, h), Point::new(0.0, h * (1.0 - rf.end_pos.clamp(0.01, 1.0))))

View File

@ -710,3 +710,18 @@ fn an_upright_picture_fill_stays_upright_in_a_turned_shape() {
}
}
}
#[test]
fn a_picture_reflects_its_pixels() {
let mut p = picture_deck(None);
let s = std::sync::Arc::make_mut(&mut p.slides[0]);
if let Some(sh) = s.shapes.first_mut() {
let rf = deckcraft_model::style::Reflection { blur: 0.0, start_alpha: 1.0, end_alpha: 0.0, end_pos: 0.5, dist: 0.0 };
sh.effects = Some(deckcraft_model::Effects { reflection: Some(rf), ..Default::default() });
}
let img = render_slide(&p, 0, &RenderOpts { scale: 1.0, ..Default::default() });
// The blue picture spans 100..200 down; just below it, its reflection tints the page blue.
let below = img.pixel(200, 204);
assert!(below[2] > 200 && below[0] < 200, "{below:?}");
assert!(close(img.pixel(200, 290), WHITE, 10), "faded out by half its height");
}