mirror of
https://github.com/penpot/penpot.git
synced 2026-09-23 20:36:15 +00:00
🐛 Crop cached drop shadows to their filter bounds (#11801)
Snapshot only the scaled shadow bounds instead of the full reusable filter surface. This avoids retaining viewport-sized textures for each small shadow while keeping cached pixels valid when the surface is reused. Use the bounded snapshot for both shape and frame shadow caches. Add regression tests for rounding, scaling, surface limits, and snapshot reuse. AI-assisted-by: gpt-6
This commit is contained in:
parent
117c8db0bb
commit
f0880cf3f9
@ -3656,11 +3656,7 @@ impl RenderState {
|
||||
)?;
|
||||
|
||||
if let Some((mut surface, filter_scale)) = filter_result {
|
||||
let cached = shadows::CachedDropShadowFilter::new(
|
||||
bounds,
|
||||
filter_scale,
|
||||
surface.image_snapshot(),
|
||||
);
|
||||
let cached = shadows::CachedDropShadowFilter::new(bounds, filter_scale, &mut surface);
|
||||
shadows::blit_cached_drop_shadow_filter(
|
||||
&mut self.surfaces,
|
||||
&cached,
|
||||
|
||||
@ -43,7 +43,19 @@ pub(crate) struct CachedDropShadowFilter {
|
||||
}
|
||||
|
||||
impl CachedDropShadowFilter {
|
||||
pub(crate) fn new(bounds: Rect, filter_scale: f32, image: skia::Image) -> Self {
|
||||
pub(crate) fn new(bounds: Rect, filter_scale: f32, surface: &mut skia::Surface) -> Self {
|
||||
// The reusable filter surface can be viewport-sized. Retaining all of it
|
||||
// for every shadow keeps large, mostly transparent textures alive.
|
||||
// Filter drawing translates bounds to the origin before applying scale.
|
||||
let width = (bounds.width() * filter_scale)
|
||||
.ceil()
|
||||
.clamp(1.0, surface.width() as f32) as i32;
|
||||
let height = (bounds.height() * filter_scale)
|
||||
.ceil()
|
||||
.clamp(1.0, surface.height() as f32) as i32;
|
||||
let image = surface
|
||||
.image_snapshot_with_bounds(skia::IRect::from_wh(width, height))
|
||||
.expect("shadow filter bounds must intersect its surface");
|
||||
Self {
|
||||
bounds,
|
||||
filter_scale,
|
||||
@ -340,11 +352,7 @@ fn render_cached_filter_frame_shadow(
|
||||
)?;
|
||||
|
||||
if let Some((mut surface, filter_scale)) = filter_result {
|
||||
let cached = CachedDropShadowFilter {
|
||||
bounds,
|
||||
filter_scale,
|
||||
image: surface.image_snapshot(),
|
||||
};
|
||||
let cached = CachedDropShadowFilter::new(bounds, filter_scale, &mut surface);
|
||||
blit_cached_drop_shadow_filter(&mut state.surfaces, &cached, None);
|
||||
state.drop_shadow_filter_cache.store(key, cached);
|
||||
}
|
||||
@ -570,3 +578,44 @@ pub fn render_text_shadows(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cached_shadow_retains_only_its_scaled_bounds() {
|
||||
for (bounds, scale, expected) in [
|
||||
(Rect::from_xywh(100.0, 200.0, 50.5, 40.5), 1.0, (51, 41)),
|
||||
(Rect::from_xywh(100.0, 200.0, 50.5, 40.5), 0.5, (26, 21)),
|
||||
(Rect::from_xywh(0.0, 0.0, 2000.0, 1500.0), 1.0, (1024, 768)),
|
||||
] {
|
||||
let mut surface = skia::surfaces::raster_n32_premul((1024, 768)).unwrap();
|
||||
let cached = CachedDropShadowFilter::new(bounds, scale, &mut surface);
|
||||
|
||||
assert_eq!(cached.image.dimensions(), skia::ISize::from(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cached_shadow_survives_filter_surface_reuse() {
|
||||
let mut surface = skia::surfaces::raster_n32_premul((1024, 768)).unwrap();
|
||||
surface.canvas().clear(skia::Color::TRANSPARENT);
|
||||
let mut paint = Paint::default();
|
||||
paint.set_color(skia::Color::RED);
|
||||
surface
|
||||
.canvas()
|
||||
.draw_rect(Rect::from_xywh(4.0, 5.0, 10.0, 10.0), &paint);
|
||||
let cached = CachedDropShadowFilter::new(
|
||||
Rect::from_xywh(100.0, 200.0, 50.5, 40.5),
|
||||
1.0,
|
||||
&mut surface,
|
||||
);
|
||||
|
||||
surface.canvas().clear(skia::Color::BLUE);
|
||||
|
||||
let pixels = cached.image.peek_pixels().unwrap();
|
||||
assert_eq!(pixels.get_color((5, 6)), skia::Color::RED);
|
||||
assert_eq!(pixels.get_color((0, 0)), skia::Color::TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user