mirror of
https://github.com/penpot/penpot.git
synced 2026-08-17 18:28:41 +00:00
⚡ Skip save_layer for plain image fills
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
01e27edf74
commit
aa8efc7cae
@ -23,6 +23,8 @@
|
|||||||
- Surfaces allocate with `effective_paint_tile_size` = `min(paint, atlas_slot max)` so
|
- Surfaces allocate with `effective_paint_tile_size` = `min(paint, atlas_slot max)` so
|
||||||
DPR 2 on a 4096² atlas paints 512 (not 1024→downscale-to-512). Overpainting the atlas
|
DPR 2 on a 4096² atlas paints 512 (not 1024→downscale-to-512). Overpainting the atlas
|
||||||
was pure GPU waste on zoom settle.
|
was pure GPU waste on zoom settle.
|
||||||
|
- 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).
|
||||||
- Scales: `get_paint_scale()` matches tile CTM; `get_view_scale()` is `zoom×dpr` for
|
- Scales: `get_paint_scale()` matches tile CTM; `get_view_scale()` is `zoom×dpr` for
|
||||||
viewport/backbuffer mapping. `get_scale()` is an alias of paint scale (legacy name).
|
viewport/backbuffer mapping. `get_scale()` is an alias of paint scale (legacy name).
|
||||||
- Zoom settle: visible tiles present via `FrameType::ViewportReady` before interest-ring
|
- Zoom settle: visible tiles present via `FrameType::ViewportReady` before interest-ring
|
||||||
|
|||||||
@ -56,6 +56,19 @@ 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 +98,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