0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-22 06:46:40 -05:00

♻️ Refactor render shape

This commit is contained in:
Aitor Moreno 2025-02-20 11:29:21 +01:00
parent 3268225941
commit 5755d7d8f9

View file

@ -275,6 +275,7 @@ impl RenderState {
modifiers: Option<&Matrix>, modifiers: Option<&Matrix>,
clip_bounds: Option<skia::Rect>, clip_bounds: Option<skia::Rect>,
) { ) {
self.drawing_surface.canvas().save();
if let Some(modifiers) = modifiers { if let Some(modifiers) = modifiers {
self.drawing_surface.canvas().concat(&modifiers); self.drawing_surface.canvas().concat(&modifiers);
} }
@ -340,6 +341,7 @@ impl RenderState {
}; };
self.apply_drawing_to_render_canvas(); self.apply_drawing_to_render_canvas();
self.drawing_surface.canvas().restore();
} }
pub fn start_render_loop( pub fn start_render_loop(
@ -361,6 +363,7 @@ impl RenderState {
self.drawing_surface self.drawing_surface
.canvas() .canvas()
.translate((self.viewbox.pan_x, self.viewbox.pan_y)); .translate((self.viewbox.pan_x, self.viewbox.pan_y));
//
self.pending_nodes = vec![NodeRenderState { self.pending_nodes = vec![NodeRenderState {
id: Uuid::nil(), id: Uuid::nil(),
visited_children: false, visited_children: false,
@ -479,6 +482,63 @@ impl RenderState {
debug::render(self); debug::render(self);
} }
pub fn render_shape_enter(&mut self, element: &mut Shape, mask: bool) {
// Masked groups needs two rendering passes, the first one rendering
// the content and the second one rendering the mask so we need to do
// an extra save_layer to keep all the masked group separate from other
// already drawn elements.
match element.kind {
Kind::Group(group) => {
if group.masked {
let paint = skia::Paint::default();
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
self.render_surface.canvas().save_layer(&layer_rec);
}
}
_ => {}
}
let mut paint = skia::Paint::default();
paint.set_blend_mode(element.blend_mode().into());
paint.set_alpha_f(element.opacity());
// When we're rendering the mask shape we need to set a special blend mode
// called 'destination-in' that keeps the drawn content within the mask.
// @see https://skia.org/docs/user/api/skblendmode_overview/
if mask {
let mut mask_paint = skia::Paint::default();
mask_paint.set_blend_mode(skia::BlendMode::DstIn);
let mask_rec = skia::canvas::SaveLayerRec::default().paint(&mask_paint);
self.render_surface.canvas().save_layer(&mask_rec);
}
if let Some(image_filter) = element.image_filter(self.viewbox.zoom * self.options.dpr())
{
paint.set_image_filter(image_filter);
}
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
self.render_surface.canvas().save_layer(&layer_rec);
}
pub fn render_shape_exit(&mut self, element: &mut Shape, visited_mask: bool) {
if visited_mask {
// Because masked groups needs two rendering passes (first drawing
// the content and then drawing the mask), we need to do an
// extra restore.
match element.kind {
Kind::Group(group) => {
if group.masked {
self.render_surface.canvas().restore();
}
}
_ => {}
}
}
self.render_surface.canvas().restore();
}
pub fn render_shape_tree( pub fn render_shape_tree(
&mut self, &mut self,
tree: &mut HashMap<Uuid, Shape>, tree: &mut HashMap<Uuid, Shape>,
@ -498,7 +558,7 @@ impl RenderState {
visited_mask, visited_mask,
mask, mask,
} = node_render_state; } = node_render_state;
let element = tree.get(&node_render_state.id).ok_or( let element = tree.get_mut(&node_render_state.id).ok_or(
"Error: Element with root_id {node_render_state.id} not found in the tree." "Error: Element with root_id {node_render_state.id} not found in the tree."
.to_string(), .to_string(),
)?; )?;
@ -534,20 +594,8 @@ impl RenderState {
} }
_ => {} _ => {}
} }
} else {
// Because masked groups needs two rendering passes (first drawing
// the content and then drawing the mask), we need to do an
// extra restore.
match element.kind {
Kind::Group(group) => {
if group.masked {
self.render_surface.canvas().restore();
}
}
_ => {}
}
} }
self.render_surface.canvas().restore(); self.render_shape_exit(element, visited_mask);
continue; continue;
} }
@ -562,54 +610,16 @@ impl RenderState {
} }
} }
// Masked groups needs two rendering passes, the first one rendering self.render_shape_enter(element, mask);
// the content and the second one rendering the mask so we need to do
// an extra save_layer to keep all the masked group separate from other
// already drawn elements.
match element.kind {
Kind::Group(group) => {
if group.masked {
let paint = skia::Paint::default();
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
self.render_surface.canvas().save_layer(&layer_rec);
}
}
_ => {}
}
let mut paint = skia::Paint::default();
paint.set_blend_mode(element.blend_mode().into());
paint.set_alpha_f(element.opacity());
// When we're rendering the mask shape we need to set a special blend mode
// called 'destination-in' that keeps the drawn content within the mask.
// @see https://skia.org/docs/user/api/skblendmode_overview/
if mask {
let mut mask_paint = skia::Paint::default();
mask_paint.set_blend_mode(skia::BlendMode::DstIn);
let mask_rec = skia::canvas::SaveLayerRec::default().paint(&mask_paint);
self.render_surface.canvas().save_layer(&mask_rec);
}
if let Some(image_filter) = element.image_filter(self.viewbox.zoom * self.options.dpr())
{
paint.set_image_filter(image_filter);
}
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
self.render_surface.canvas().save_layer(&layer_rec);
self.drawing_surface.canvas().save();
if !node_render_state.id.is_nil() { if !node_render_state.id.is_nil() {
self.render_shape( self.render_shape(
&mut element.clone(), element,
modifiers.get(&element.id), modifiers.get(&element.id),
clip_bounds, clip_bounds,
); );
} else { } else {
self.apply_drawing_to_render_canvas(); self.apply_drawing_to_render_canvas();
} }
self.drawing_surface.canvas().restore();
// Set the node as visited_children before processing children // Set the node as visited_children before processing children
self.pending_nodes.push(NodeRenderState { self.pending_nodes.push(NodeRenderState {