Pptx/Render: picture fills with rotWithShape=0 stay upright

A shape's picture fill marked not to rotate with the shape turned and
flipped with it (rotWithShape was neither kept nor written: always 1).
PowerPoint keeps such a picture upright, stretched (or tiled) over the
box the rotated shape covers on the slide and clipped to the shape. Now
read, written (PictureFill::upright) and drawn so.

Wild synthetic set: picture fills on rotated/flipped shapes 12.89 ->
3.87.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-13 08:14:10 -04:00
co-authored by Claude Opus 5.5
parent 3ec12412e0
commit cdaeb2fbde
7 changed files with 68 additions and 2 deletions
+13
View File
@@ -82,6 +82,19 @@ pub fn fill_path(
ctx.fill_path(path);
}
Fill::Picture(pf) => {
// `rotWithShape="0"`: upright on the slide, stretched over the box the turned shape
// covers, clipped to it.
let c = m.as_coeffs();
let turned = c[1].abs() > 1e-9 || c[2].abs() > 1e-9 || c[0] < 0.0 || c[3] < 0.0;
if pf.upright && turned && m.determinant().abs() > 1e-12 {
let on_slide = m.transform_rect_bbox(bounds);
let on_path = m * path.clone();
ctx.set_transform(Affine::IDENTITY);
ctx.push_clip_layer(&on_path);
picture(ctx, pres, &resolved(rctx, pf, ph), on_slide, Affine::IDENTITY, None);
ctx.pop_layer();
return;
}
ctx.push_clip_layer(path);
picture(ctx, pres, &resolved(rctx, pf, ph), bounds, m, None);
ctx.pop_layer();
+27
View File
@@ -683,3 +683,30 @@ fn stars_and_right_arrows_have_sharp_corners_by_default() {
assert!(close(img.pixel(95, 95), WHITE, 30), "{:?}", img.pixel(95, 95));
assert!(deckcraft_geom::preset::mitred_by_default("rightArrow") && !deckcraft_geom::preset::mitred_by_default("leftArrow"));
}
#[test]
fn an_upright_picture_fill_stays_upright_in_a_turned_shape() {
use deckcraft_model::style::PictureFill;
let blue = Rgba::rgb(0, 0, 255);
for upright in [false, true] {
let mut p = Presentation::default();
let media = p.add_media("t.png", "image/png", half_red_half_blue());
// Half red (left) half blue picture in a 200 × 200 square turned a quarter (and so
// covering the same square).
let sh = Shape {
id: ShapeId(30),
xfrm: Some(Xfrm { rot: 90.0, ..Xfrm::new(100.0, 100.0, 200.0, 200.0) }),
fill: Some(Fill::Picture(PictureFill { media, upright, ..Default::default() })),
line: no_line(),
..Default::default()
};
std::sync::Arc::make_mut(&mut p.slides[0]).shapes = vec![sh];
let img = render_slide(&p, 0, &RenderOpts { scale: 1.0, ..Default::default() });
let (left, top) = (img.pixel(110, 200), img.pixel(200, 110));
if upright {
assert!(close(left, RED, 30) && close(img.pixel(290, 200), blue, 30), "upright: red left {left:?}");
} else {
assert!(close(top, RED, 30), "turned with the shape: red at the top {top:?}");
}
}
}