mirror of
https://github.com/penpot/penpot.git
synced 2026-08-28 23:59:00 +00:00
wip
This commit is contained in:
parent
4cb9f951d2
commit
4a9f55d47d
@ -126,7 +126,24 @@ pub extern "C" fn render(timestamp: i32, flags: u8) -> Result<FrameType> {
|
||||
let is_partial = flags & RenderFlag::Partial as u8 == RenderFlag::Partial as u8;
|
||||
if flags & RenderFlag::SyncTiles as u8 != 0 {
|
||||
render_state.preserve_target_during_render = true;
|
||||
// Pan/zoom pause: the viewbox is stable, so render at full quality
|
||||
// and let the tiles reach the atlas. Fast-mode tiles are dropped by
|
||||
// `apply_render_to_final_canvas`. `set_view_start` re-arms fast mode
|
||||
// if the gesture resumes.
|
||||
if render_state.options.is_fast_mode() {
|
||||
render_state.options.set_fast_mode(false);
|
||||
render_state.tile_viewbox.update(&render_state.viewbox);
|
||||
render_state.rebuild_tile_index(&state.shapes);
|
||||
}
|
||||
}
|
||||
|
||||
// Gesture in progress with no pause: the viewbox is moving and
|
||||
// `render_from_cache` owns the screen. Stop the progressive loop rather
|
||||
// than rasterizing tiles that would be dropped.
|
||||
if render_state.options.is_fast_mode() && !render_state.options.is_interactive_transform() {
|
||||
return Ok(FrameType::Full);
|
||||
}
|
||||
|
||||
let frame_type = if is_partial && !render_state.preserve_target_during_render {
|
||||
state
|
||||
.continue_render_loop(timestamp)
|
||||
|
||||
@ -656,6 +656,11 @@ impl RenderState {
|
||||
Self::blur_from_variance(total)
|
||||
}
|
||||
|
||||
fn subtract_blur_values(total: Option<Blur>, applied: Option<Blur>) -> Option<Blur> {
|
||||
let remaining = Self::blur_variance(total) - Self::blur_variance(applied);
|
||||
Self::blur_from_variance(remaining.max(0.))
|
||||
}
|
||||
|
||||
fn frame_clip_layer_blur(shape: &Shape) -> Option<Blur> {
|
||||
shape.frame_clip_layer_blur()
|
||||
}
|
||||
@ -2339,10 +2344,6 @@ impl RenderState {
|
||||
let preserve_target = self.preserve_target_during_render;
|
||||
self.preserve_target_during_render = false;
|
||||
|
||||
if preserve_target && self.options.is_fast_mode() {
|
||||
self.rebuild_tile_index(tree);
|
||||
}
|
||||
|
||||
if self.options.is_interactive_transform() {
|
||||
// Keep `Target` as the previous frame and overwrite only the tiles
|
||||
// that changed. This avoids clearing + redrawing an atlas backdrop
|
||||
@ -3039,6 +3040,8 @@ impl RenderState {
|
||||
shadow: &Shadow,
|
||||
scale: f32,
|
||||
inherited_layer_blur: Option<Blur>,
|
||||
layer_applied_blur: Option<Blur>,
|
||||
layer_owns_shadow_blur: bool,
|
||||
node_render_state: &NodeRenderState,
|
||||
target_surface: SurfaceId,
|
||||
) -> Result<()> {
|
||||
@ -3071,19 +3074,29 @@ impl RenderState {
|
||||
nested_clip_bounds,
|
||||
scale,
|
||||
inherited_layer_blur,
|
||||
layer_applied_blur,
|
||||
layer_owns_shadow_blur,
|
||||
target_surface,
|
||||
)?;
|
||||
} else {
|
||||
let text_extrect = shadow_shape.extrect(tree, scale);
|
||||
let layer_bounds = shadow
|
||||
.get_drop_shadow_filter()
|
||||
.map(|filter| filter.compute_fast_bounds(text_extrect))
|
||||
.unwrap_or(text_extrect);
|
||||
let paint = skia::Paint::default();
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default()
|
||||
.bounds(&layer_bounds)
|
||||
.paint(&paint);
|
||||
self.surfaces
|
||||
.canvas(SurfaceId::DropShadows)
|
||||
.save_layer(&layer_rec);
|
||||
|
||||
let mut transformed_shadow: Cow<Shadow> = Cow::Borrowed(shadow);
|
||||
transformed_shadow.to_mut().color = skia::Color::BLACK;
|
||||
transformed_shadow.to_mut().blur = transformed_shadow.blur;
|
||||
transformed_shadow.to_mut().spread = transformed_shadow.spread;
|
||||
if layer_owns_shadow_blur {
|
||||
transformed_shadow.to_mut().blur = 0.0;
|
||||
}
|
||||
|
||||
let mut new_shadow_paint = skia::Paint::default();
|
||||
new_shadow_paint.set_image_filter(transformed_shadow.get_drop_shadow_filter());
|
||||
@ -3124,6 +3137,8 @@ impl RenderState {
|
||||
clip_bounds: Option<ClipStack>,
|
||||
scale: f32,
|
||||
extra_layer_blur: Option<Blur>,
|
||||
layer_applied_blur: Option<Blur>,
|
||||
layer_owns_shadow_blur: bool,
|
||||
target_surface: SurfaceId,
|
||||
) -> Result<()> {
|
||||
let mut transformed_shadow: Cow<Shadow> = Cow::Borrowed(shadow);
|
||||
@ -3131,8 +3146,10 @@ impl RenderState {
|
||||
transformed_shadow.to_mut().color = skia::Color::BLACK;
|
||||
|
||||
let mut plain_shape = Cow::Borrowed(shape);
|
||||
let combined_blur =
|
||||
Self::combine_blur_values(self.combined_layer_blur(shape.blur), extra_layer_blur);
|
||||
let combined_blur = Self::subtract_blur_values(
|
||||
Self::combine_blur_values(self.combined_layer_blur(shape.blur), extra_layer_blur),
|
||||
layer_applied_blur,
|
||||
);
|
||||
let blur_filter = combined_blur.and_then(|blur| {
|
||||
let sigma = blur.sigma();
|
||||
skia::image_filters::blur((sigma, sigma), None, None, None)
|
||||
@ -3186,8 +3203,11 @@ impl RenderState {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// blur=0 at high zoom: draw directly on DropShadows with geometric spread (no filter).
|
||||
if scale > 1.0 && shadow.blur <= 0.0 {
|
||||
if layer_owns_shadow_blur {
|
||||
transformed_shadow.to_mut().blur = 0.0;
|
||||
}
|
||||
|
||||
if combined_blur.is_none() && transformed_shadow.blur <= 0.0 {
|
||||
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||
drop_canvas.save();
|
||||
//drop_canvas.scale((scale, scale));
|
||||
@ -3227,7 +3247,9 @@ impl RenderState {
|
||||
}
|
||||
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default()
|
||||
.bounds(&bounds)
|
||||
.paint(&shadow_paint);
|
||||
|
||||
// Low zoom path: use blur filter but apply offset and spread geometrically
|
||||
if use_low_zoom_path {
|
||||
@ -3321,11 +3343,11 @@ impl RenderState {
|
||||
)?;
|
||||
|
||||
if let Some((mut surface, filter_scale)) = filter_result {
|
||||
let cached = shadows::CachedDropShadowFilter::new(
|
||||
bounds,
|
||||
filter_scale,
|
||||
surface.image_snapshot(),
|
||||
);
|
||||
let Some(image) = shadows::snapshot_filter_region(&mut surface, &bounds, filter_scale)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let cached = shadows::CachedDropShadowFilter::new(bounds, filter_scale, image);
|
||||
shadows::blit_cached_drop_shadow_filter(
|
||||
&mut self.surfaces,
|
||||
&cached,
|
||||
@ -3379,8 +3401,36 @@ impl RenderState {
|
||||
continue;
|
||||
}
|
||||
rendered_any = true;
|
||||
let paint = skia::Paint::default();
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||
let layer_bounds = shadow
|
||||
.get_drop_shadow_filter()
|
||||
.map(|filter| filter.compute_fast_bounds(*element_extrect))
|
||||
.unwrap_or(*element_extrect);
|
||||
|
||||
let layer_blur = Self::combine_blur_values(
|
||||
inherited_layer_blur,
|
||||
Some(Blur::new(BlurType::LayerBlur, false, shadow.blur)),
|
||||
);
|
||||
let layer_sigma = layer_blur.map_or(0., |blur| blur.sigma());
|
||||
let layer_owns_blur = !use_direct_container_shadow
|
||||
&& layer_sigma * 3.0 * scale <= self.surfaces.margins().width as f32;
|
||||
let layer_applied_blur = if layer_owns_blur {
|
||||
inherited_layer_blur
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
if layer_owns_blur && layer_sigma > 0. {
|
||||
paint.set_image_filter(skia::image_filters::blur(
|
||||
(layer_sigma, layer_sigma),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
));
|
||||
}
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default()
|
||||
.bounds(&layer_bounds)
|
||||
.paint(&paint);
|
||||
self.surfaces
|
||||
.canvas(SurfaceId::DropShadows)
|
||||
.save_layer(&layer_rec);
|
||||
@ -3402,6 +3452,8 @@ impl RenderState {
|
||||
clip_bounds.clone(),
|
||||
scale,
|
||||
None,
|
||||
layer_applied_blur,
|
||||
layer_owns_blur,
|
||||
target_surface,
|
||||
)?;
|
||||
if !element.container_fill_covers_shadow_descendants(tree, scale) {
|
||||
@ -3411,6 +3463,8 @@ impl RenderState {
|
||||
shadow,
|
||||
scale,
|
||||
inherited_layer_blur,
|
||||
layer_applied_blur,
|
||||
layer_owns_blur,
|
||||
node_render_state,
|
||||
target_surface,
|
||||
)?;
|
||||
|
||||
@ -26,6 +26,17 @@ pub fn compose_filters(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter_region(bounds: &Rect, scale: f32, max_width: i32, max_height: i32) -> skia::IRect {
|
||||
const PAD: i32 = 4;
|
||||
let width = ((bounds.width() * scale).ceil() as i32)
|
||||
.saturating_add(PAD)
|
||||
.clamp(1, max_width);
|
||||
let height = ((bounds.height() * scale).ceil() as i32)
|
||||
.saturating_add(PAD)
|
||||
.clamp(1, max_height);
|
||||
skia::IRect::from_xywh(0, 0, width, height)
|
||||
}
|
||||
|
||||
/// Renders filtered content offscreen and composites it back into the target surface.
|
||||
///
|
||||
/// This helper is meant for shapes that rely on blur/filters that should be evaluated
|
||||
@ -45,6 +56,7 @@ where
|
||||
if let Some((mut surface, scale)) =
|
||||
render_into_filter_surface(render_state, bounds, 1.0, draw_fn)?
|
||||
{
|
||||
let region = Rect::from(filter_region(&bounds, scale, surface.width(), surface.height()));
|
||||
let canvas = render_state.surfaces.canvas_and_mark_dirty(target_surface);
|
||||
|
||||
// If we scaled down, we need to scale the source rect and adjust the destination
|
||||
@ -52,11 +64,13 @@ where
|
||||
canvas.save();
|
||||
canvas.scale((1.0 / scale, 1.0 / scale));
|
||||
canvas.translate((bounds.left * scale, bounds.top * scale));
|
||||
canvas.clip_rect(region, None, false);
|
||||
surface.draw(canvas, (0.0, 0.0), get_resources().sampling_options, None);
|
||||
canvas.restore();
|
||||
} else {
|
||||
canvas.save();
|
||||
canvas.translate((bounds.left, bounds.top));
|
||||
canvas.clip_rect(region, None, false);
|
||||
surface.draw(canvas, (0.0, 0.0), get_resources().sampling_options, None);
|
||||
canvas.restore();
|
||||
}
|
||||
@ -118,10 +132,12 @@ where
|
||||
// Combine overflow-fit scale with caller-requested extra downscale
|
||||
let scale = (fit_scale * extra_downscale).max(MIN_COMBINED_SCALE);
|
||||
|
||||
let region = filter_region(&bounds, scale, filter_width, filter_height);
|
||||
{
|
||||
let canvas = render_state.surfaces.canvas(filter_id);
|
||||
canvas.clear(skia::Color::TRANSPARENT);
|
||||
canvas.save();
|
||||
canvas.clip_irect(region, None);
|
||||
canvas.clear(skia::Color::TRANSPARENT);
|
||||
// Apply scale first, then translate
|
||||
canvas.scale((scale, scale));
|
||||
canvas.translate((-bounds.left, -bounds.top));
|
||||
|
||||
@ -30,7 +30,7 @@ impl GpuState {
|
||||
context_options.skip_gl_error_checks = Enable::Yes;
|
||||
// context_options.runtime_program_cache_size = 1024;
|
||||
// context_options.allow_multiple_glyph_cache_textures = Enable::Yes;
|
||||
// context_options.allow_path_mask_caching = false;
|
||||
context_options.allow_path_mask_caching = true;
|
||||
|
||||
let context = gpu::direct_contexts::make_gl(interface, Some(&context_options)).ok_or(
|
||||
Error::CriticalError("Failed to create GL context".to_string()),
|
||||
|
||||
@ -142,16 +142,32 @@ fn spread_inset(spread: f32) -> Option<f32> {
|
||||
Some(-spread).filter(|&s| s > 0.0)
|
||||
}
|
||||
|
||||
fn blur_layer_filter(blur: f32, sigma_scale: f32) -> Option<skia::ImageFilter> {
|
||||
if blur <= 0.0 {
|
||||
return None;
|
||||
}
|
||||
let sigma = radius_to_sigma(blur) * sigma_scale;
|
||||
skia::image_filters::blur((sigma, sigma), None, None, None)
|
||||
}
|
||||
|
||||
fn blur_layer_paint(blur: f32, sigma_scale: f32) -> skia::Paint {
|
||||
let mut paint = skia::Paint::default();
|
||||
if blur > 0.0 {
|
||||
let sigma = radius_to_sigma(blur) * sigma_scale;
|
||||
paint.set_image_filter(skia::image_filters::blur((sigma, sigma), None, None, None));
|
||||
}
|
||||
paint.set_image_filter(blur_layer_filter(blur, sigma_scale));
|
||||
paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
paint
|
||||
}
|
||||
|
||||
fn frame_shadow_layer_bounds(frame: &Shape, shadow: &Shadow, sigma_scale: f32) -> Rect {
|
||||
let mut rect = frame.selrect;
|
||||
if shadow.spread > 0.0 {
|
||||
rect.outset((shadow.spread, shadow.spread));
|
||||
}
|
||||
match blur_layer_filter(shadow.blur, sigma_scale) {
|
||||
Some(filter) => filter.compute_fast_bounds(rect),
|
||||
None => rect,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_frame_shadow_rect(
|
||||
surfaces: &mut super::Surfaces,
|
||||
surface_id: SurfaceId,
|
||||
@ -222,6 +238,15 @@ fn blur_downscale_for_frame_shadow(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn snapshot_filter_region(
|
||||
surface: &mut skia::Surface,
|
||||
bounds: &Rect,
|
||||
filter_scale: f32,
|
||||
) -> Option<skia::Image> {
|
||||
let region = filters::filter_region(bounds, filter_scale, surface.width(), surface.height());
|
||||
surface.image_snapshot_with_bounds(region)
|
||||
}
|
||||
|
||||
pub(crate) fn blit_cached_drop_shadow_filter(
|
||||
surfaces: &mut super::Surfaces,
|
||||
cached: &CachedDropShadowFilter,
|
||||
@ -259,13 +284,16 @@ fn render_inline_frame_shadow(
|
||||
) -> Result<()> {
|
||||
let antialias = frame_shadow_antialias(state, frame, scale);
|
||||
let layer_paint = blur_layer_paint(shadow.blur, 1.0);
|
||||
let layer_bounds = frame_shadow_layer_bounds(frame, shadow, 1.0);
|
||||
let draw_matrix = frame_shadow_draw_matrix(frame, shadow);
|
||||
|
||||
{
|
||||
let drop_canvas = state.surfaces.canvas(SurfaceId::DropShadows);
|
||||
drop_canvas.save();
|
||||
drop_canvas.concat(&draw_matrix);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&layer_paint);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default()
|
||||
.bounds(&layer_bounds)
|
||||
.paint(&layer_paint);
|
||||
drop_canvas.save_layer(&layer_rec);
|
||||
}
|
||||
|
||||
@ -315,7 +343,10 @@ fn render_cached_filter_frame_shadow(
|
||||
state.options.blur_downscale_threshold,
|
||||
);
|
||||
let layer_paint = blur_layer_paint(shadow.blur, blur_downscale);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&layer_paint);
|
||||
let layer_bounds = frame_shadow_layer_bounds(frame, shadow, blur_downscale);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default()
|
||||
.bounds(&layer_bounds)
|
||||
.paint(&layer_paint);
|
||||
|
||||
let shadow_draw_matrix = frame_shadow_draw_matrix(frame, shadow);
|
||||
let filter_result = filters::render_into_filter_surface(
|
||||
@ -340,10 +371,13 @@ fn render_cached_filter_frame_shadow(
|
||||
)?;
|
||||
|
||||
if let Some((mut surface, filter_scale)) = filter_result {
|
||||
let Some(image) = snapshot_filter_region(&mut surface, &bounds, filter_scale) else {
|
||||
return Ok(());
|
||||
};
|
||||
let cached = CachedDropShadowFilter {
|
||||
bounds,
|
||||
filter_scale,
|
||||
image: surface.image_snapshot(),
|
||||
image,
|
||||
};
|
||||
blit_cached_drop_shadow_filter(&mut state.surfaces, &cached, None);
|
||||
state.drop_shadow_filter_cache.store(key, cached);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user