mirror of
https://github.com/penpot/penpot.git
synced 2026-08-17 02:09:12 +00:00
⚡ Skip save_layer for plain image fills (#11230)
Avoid an offscreen buffer per Fill::Image during tile walks: only use save_layer when a shape image filter is present; axis-aligned rects and frames without corner radii also skip the redundant container clip.
This commit is contained in:
parent
be83656d55
commit
cb57fd9dfa
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
## Tile/render behavior
|
## Tile/render behavior
|
||||||
|
|
||||||
|
- Raster `Fill::Image`: skip `save_layer` unless the shape has an image filter; plain
|
||||||
|
Rect/Frame (no corners) also skip the container clip (`draw_image_fill` in fills.rs).
|
||||||
- Interactive transforms are distinct from viewport fast mode. `set_modifiers_start` enables fast mode and interactive transform; interactive transform still flushes each animation frame.
|
- Interactive transforms are distinct from viewport fast mode. `set_modifiers_start` enables fast mode and interactive transform; interactive transform still flushes each animation frame.
|
||||||
- During interactive transform, modifier tile invalidation is deferred to `render()` once per rAF. Outside interactive transform, `set_modifiers` rebuilds modifier tiles immediately.
|
- During interactive transform, modifier tile invalidation is deferred to `render()` once per rAF. Outside interactive transform, `set_modifiers` rebuilds modifier tiles immediately.
|
||||||
- `set_modifiers_end` disables fast/interactive state and cancels pending async render; the caller must request the final full-quality render.
|
- `set_modifiers_end` disables fast/interactive state and cancels pending async render; the caller must request the final full-quality render.
|
||||||
|
|||||||
@ -56,6 +56,15 @@ fn clip_to_shape(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Axis-aligned rect/frame with no corner radii: `dest` fills `selrect`, so a
|
||||||
|
/// clip to the container is a no-op before `draw_image_rect`.
|
||||||
|
fn is_axis_aligned_image_rect(shape: &Shape) -> bool {
|
||||||
|
matches!(
|
||||||
|
&shape.shape_type,
|
||||||
|
Type::Rect(Rect { corners: None }) | Type::Frame(Frame { corners: None, .. })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_image_fill(
|
fn draw_image_fill(
|
||||||
render_state: &mut RenderState,
|
render_state: &mut RenderState,
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
@ -85,29 +94,53 @@ fn draw_image_fill(
|
|||||||
|
|
||||||
let src_rect = get_source_rect(size, container, image_fill);
|
let src_rect = get_source_rect(size, container, image_fill);
|
||||||
let dest_rect = container;
|
let dest_rect = container;
|
||||||
|
let sampling = get_resources().sampling_options;
|
||||||
|
|
||||||
let mut image_paint = skia::Paint::default();
|
// `save_layer` is only required when a shape-level image filter (blur) must
|
||||||
image_paint.set_anti_alias(antialias);
|
// run over the clipped image. Otherwise a plain save/clip (or no clip for
|
||||||
|
// axis-aligned rects) avoids an offscreen buffer per fill — the hot path
|
||||||
|
// for photo-heavy boards during tile walks.
|
||||||
if let Some(filter) = shape.image_filter(1.) {
|
if let Some(filter) = shape.image_filter(1.) {
|
||||||
image_paint.set_image_filter(filter.clone());
|
let mut layer_paint = skia::Paint::default();
|
||||||
|
layer_paint.set_anti_alias(antialias);
|
||||||
|
layer_paint.set_image_filter(filter);
|
||||||
|
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&layer_paint);
|
||||||
|
canvas.save_layer(&layer_rec);
|
||||||
|
clip_to_shape(canvas, shape, container, antialias);
|
||||||
|
canvas.draw_image_rect_with_sampling_options(
|
||||||
|
image,
|
||||||
|
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
||||||
|
dest_rect,
|
||||||
|
sampling,
|
||||||
|
paint,
|
||||||
|
);
|
||||||
|
canvas.restore();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&image_paint);
|
let mut draw_paint = paint.clone();
|
||||||
// Save the current canvas state
|
draw_paint.set_anti_alias(antialias);
|
||||||
canvas.save_layer(&layer_rec);
|
|
||||||
|
|
||||||
|
if is_axis_aligned_image_rect(shape) {
|
||||||
|
canvas.draw_image_rect_with_sampling_options(
|
||||||
|
image,
|
||||||
|
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
||||||
|
dest_rect,
|
||||||
|
sampling,
|
||||||
|
&draw_paint,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
clip_to_shape(canvas, shape, container, antialias);
|
clip_to_shape(canvas, shape, container, antialias);
|
||||||
|
|
||||||
// Draw the image with the calculated destination rectangle
|
|
||||||
canvas.draw_image_rect_with_sampling_options(
|
canvas.draw_image_rect_with_sampling_options(
|
||||||
image,
|
image,
|
||||||
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
||||||
dest_rect,
|
dest_rect,
|
||||||
get_resources().sampling_options,
|
sampling,
|
||||||
paint,
|
&draw_paint,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Restore the canvas to remove the clipping
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user