mirror of
https://github.com/penpot/penpot.git
synced 2026-04-28 04:38:14 +00:00
Merge pull request #6122 from penpot/elenatorro-10516-fix-inner-shadow-rendering
🐛 Fix Fill Inner Shadow rendering
This commit is contained in:
commit
7a4c9d9933
@ -216,6 +216,9 @@ impl RenderState {
|
||||
self.surfaces
|
||||
.flush_and_submit(&mut self.gpu_state, SurfaceId::DropShadows);
|
||||
|
||||
self.surfaces
|
||||
.flush_and_submit(&mut self.gpu_state, SurfaceId::InnerShadows);
|
||||
|
||||
self.surfaces.draw_into(
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::Current,
|
||||
@ -228,6 +231,12 @@ impl RenderState {
|
||||
Some(&skia::Paint::default()),
|
||||
);
|
||||
|
||||
self.surfaces.draw_into(
|
||||
SurfaceId::InnerShadows,
|
||||
SurfaceId::Current,
|
||||
Some(&skia::Paint::default()),
|
||||
);
|
||||
|
||||
let mut render_overlay_below_strokes = false;
|
||||
if let Some(shape) = shape {
|
||||
render_overlay_below_strokes = shape.fills().len() > 0;
|
||||
@ -269,6 +278,7 @@ impl RenderState {
|
||||
self.surfaces.apply_mut(
|
||||
&[
|
||||
SurfaceId::Shadow,
|
||||
SurfaceId::InnerShadows,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::Overlay,
|
||||
SurfaceId::Fills,
|
||||
@ -286,7 +296,12 @@ impl RenderState {
|
||||
modifiers: Option<&Matrix>,
|
||||
clip_bounds: Option<(Rect, Option<Corners>, Matrix)>,
|
||||
) {
|
||||
let surface_ids = &[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows];
|
||||
let surface_ids = &[
|
||||
SurfaceId::Fills,
|
||||
SurfaceId::Strokes,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::InnerShadows,
|
||||
];
|
||||
self.surfaces.apply_mut(surface_ids, |s| {
|
||||
s.canvas().save();
|
||||
});
|
||||
@ -365,7 +380,12 @@ impl RenderState {
|
||||
}
|
||||
_ => {
|
||||
self.surfaces.apply_mut(
|
||||
&[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows],
|
||||
&[
|
||||
SurfaceId::Fills,
|
||||
SurfaceId::Strokes,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::InnerShadows,
|
||||
],
|
||||
|s| {
|
||||
s.canvas().concat(&matrix);
|
||||
},
|
||||
@ -379,22 +399,19 @@ impl RenderState {
|
||||
strokes::render(self, &shape, stroke);
|
||||
}
|
||||
|
||||
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
|
||||
shadows::render_inner_shadow(
|
||||
self,
|
||||
shadow,
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
shape.fills().len() > 0,
|
||||
);
|
||||
}
|
||||
|
||||
shadows::render_inner_shadows(self, &shape);
|
||||
shadows::render_drop_shadows(self, &shape);
|
||||
}
|
||||
};
|
||||
|
||||
self.apply_drawing_to_render_canvas(Some(&shape));
|
||||
self.surfaces.apply_mut(
|
||||
&[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows],
|
||||
&[
|
||||
SurfaceId::Fills,
|
||||
SurfaceId::Strokes,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::InnerShadows,
|
||||
],
|
||||
|s| {
|
||||
s.canvas().restore();
|
||||
},
|
||||
@ -419,14 +436,17 @@ impl RenderState {
|
||||
self.cancel_animation_frame(frame_id);
|
||||
}
|
||||
}
|
||||
let scale = self.get_scale();
|
||||
self.reset_canvas();
|
||||
self.surfaces.apply_mut(
|
||||
&[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows],
|
||||
&[
|
||||
SurfaceId::Fills,
|
||||
SurfaceId::Strokes,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::InnerShadows,
|
||||
],
|
||||
|s| {
|
||||
s.canvas().scale((
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
));
|
||||
s.canvas().scale((scale, scale));
|
||||
},
|
||||
);
|
||||
|
||||
@ -517,7 +537,7 @@ impl RenderState {
|
||||
.save_layer(&mask_rec);
|
||||
}
|
||||
|
||||
if let Some(image_filter) = element.image_filter(self.viewbox.zoom * self.options.dpr()) {
|
||||
if let Some(image_filter) = element.image_filter(self.get_scale()) {
|
||||
paint.set_image_filter(image_filter);
|
||||
}
|
||||
|
||||
@ -546,9 +566,9 @@ impl RenderState {
|
||||
|
||||
pub fn get_current_tile_bounds(&mut self) -> Rect {
|
||||
let (tile_x, tile_y) = self.current_tile.unwrap();
|
||||
let zoom = self.viewbox.zoom * self.options.dpr();
|
||||
let offset_x = self.viewbox.area.left * zoom;
|
||||
let offset_y = self.viewbox.area.top * zoom;
|
||||
let scale = self.get_scale();
|
||||
let offset_x = self.viewbox.area.left * scale;
|
||||
let offset_y = self.viewbox.area.top * scale;
|
||||
Rect::from_xywh(
|
||||
(tile_x as f32 * tiles::TILE_SIZE) - offset_x,
|
||||
(tile_y as f32 * tiles::TILE_SIZE) - offset_y,
|
||||
@ -782,4 +802,8 @@ impl RenderState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_scale(&self) -> f32 {
|
||||
self.viewbox.zoom() * self.options.dpr()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
use skia_safe::{self as skia};
|
||||
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::shapes::{Shadow, Shape, Type};
|
||||
use skia_safe::{self as skia, Paint};
|
||||
|
||||
// Drop Shadows
|
||||
pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape) {
|
||||
if shape.fills().len() > 0 {
|
||||
if shape.has_fills() {
|
||||
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
|
||||
render_fill_drop_shadow(render_state, &shape, &shadow);
|
||||
}
|
||||
} else {
|
||||
let scale = render_state.viewbox.zoom * render_state.options.dpr();
|
||||
let scale = render_state.get_scale();
|
||||
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
|
||||
render_stroke_drop_shadow(render_state, &shadow, scale);
|
||||
}
|
||||
@ -18,27 +18,10 @@ pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape) {
|
||||
|
||||
fn render_fill_drop_shadow(render_state: &mut RenderState, shape: &Shape, shadow: &Shadow) {
|
||||
let paint = &shadow.get_drop_shadow_paint();
|
||||
|
||||
match &shape.shape_type {
|
||||
Type::Rect(_) | Type::Frame(_) => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_rect_to(SurfaceId::DropShadows, shape, paint);
|
||||
}
|
||||
Type::Circle => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_circle_to(SurfaceId::DropShadows, shape, paint);
|
||||
}
|
||||
Type::Path(_) | Type::Bool(_) => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_path_to(SurfaceId::DropShadows, shape, paint);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
render_shadow_paint(render_state, shape, paint, SurfaceId::DropShadows);
|
||||
}
|
||||
|
||||
// TODO: Stroke shadows
|
||||
fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
|
||||
let shadow_paint = &shadow.to_paint(scale);
|
||||
|
||||
@ -58,30 +41,63 @@ fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, sc
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
}
|
||||
|
||||
pub fn render_inner_shadow(
|
||||
render_state: &mut RenderState,
|
||||
shadow: &Shadow,
|
||||
scale: f32,
|
||||
render_over_fills: bool,
|
||||
) {
|
||||
let shadow_paint = shadow.to_paint(scale);
|
||||
|
||||
if render_over_fills {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_into(SurfaceId::Fills, SurfaceId::Shadow, Some(&shadow_paint));
|
||||
// Inner Shadows
|
||||
pub fn render_inner_shadows(render_state: &mut RenderState, shape: &Shape) {
|
||||
if shape.has_fills() {
|
||||
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
|
||||
render_fill_inner_shadow(render_state, &shape, &shadow);
|
||||
}
|
||||
} else {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_into(SurfaceId::Strokes, SurfaceId::Shadow, Some(&shadow_paint));
|
||||
let scale = render_state.get_scale();
|
||||
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
|
||||
render_stroke_inner_shadow(render_state, &shadow, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_fill_inner_shadow(render_state: &mut RenderState, shape: &Shape, shadow: &Shadow) {
|
||||
let paint = &shadow.get_inner_shadow_paint();
|
||||
render_shadow_paint(render_state, shape, paint, SurfaceId::InnerShadows);
|
||||
}
|
||||
|
||||
// TODO: Stroke shadows
|
||||
fn render_stroke_inner_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
|
||||
let shadow_paint = &shadow.to_paint(scale);
|
||||
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_into(SurfaceId::Shadow, SurfaceId::Overlay, None);
|
||||
.draw_into(SurfaceId::Strokes, SurfaceId::Shadow, Some(shadow_paint));
|
||||
|
||||
render_state.surfaces.draw_into(
|
||||
SurfaceId::Shadow,
|
||||
SurfaceId::Overlay,
|
||||
Some(&skia::Paint::default()),
|
||||
);
|
||||
|
||||
render_state
|
||||
.surfaces
|
||||
.canvas(SurfaceId::Shadow)
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
}
|
||||
|
||||
fn render_shadow_paint(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paint: &Paint,
|
||||
surface_id: SurfaceId,
|
||||
) {
|
||||
match &shape.shape_type {
|
||||
Type::Rect(_) | Type::Frame(_) => {
|
||||
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
||||
}
|
||||
Type::Circle => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_circle_to(surface_id, shape, paint);
|
||||
}
|
||||
Type::Path(_) | Type::Bool(_) => {
|
||||
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ fn handle_stroke_caps(
|
||||
canvas: &skia::Canvas,
|
||||
is_open: bool,
|
||||
svg_attrs: &HashMap<String, String>,
|
||||
dpr_scale: f32,
|
||||
scale: f32,
|
||||
) {
|
||||
let points_count = path.count_points();
|
||||
let mut points = vec![Point::default(); points_count];
|
||||
@ -165,7 +165,7 @@ fn handle_stroke_caps(
|
||||
let first_point = points.first().unwrap();
|
||||
let last_point = points.last().unwrap();
|
||||
|
||||
let mut paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, dpr_scale);
|
||||
let mut paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale);
|
||||
|
||||
handle_stroke_cap(
|
||||
canvas,
|
||||
@ -332,11 +332,11 @@ fn draw_image_stroke_in_container(
|
||||
}
|
||||
|
||||
let size = image_fill.size();
|
||||
let scale = render_state.get_scale();
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::Fills);
|
||||
let container = &shape.selrect;
|
||||
let path_transform = shape.to_path_transform();
|
||||
let svg_attrs = &shape.svg_attrs;
|
||||
let dpr_scale = render_state.viewbox.zoom * render_state.options.dpr();
|
||||
|
||||
// Save canvas and layer state
|
||||
let mut pb = skia::Paint::default();
|
||||
@ -358,11 +358,11 @@ fn draw_image_stroke_in_container(
|
||||
&outer_rect,
|
||||
&shape_type.corners(),
|
||||
svg_attrs,
|
||||
dpr_scale,
|
||||
scale,
|
||||
);
|
||||
}
|
||||
Type::Circle => {
|
||||
draw_stroke_on_circle(canvas, stroke, container, &outer_rect, svg_attrs, dpr_scale)
|
||||
draw_stroke_on_circle(canvas, stroke, container, &outer_rect, svg_attrs, scale)
|
||||
}
|
||||
|
||||
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
||||
@ -381,12 +381,12 @@ fn draw_image_stroke_in_container(
|
||||
}
|
||||
}
|
||||
let is_open = p.is_open();
|
||||
let mut paint = stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, dpr_scale);
|
||||
let mut paint = stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, scale);
|
||||
canvas.draw_path(&path, &paint);
|
||||
if stroke.render_kind(is_open) == StrokeKind::OuterStroke {
|
||||
// Small extra inner stroke to overlap with the fill
|
||||
// and avoid unnecesary artifacts.
|
||||
paint.set_stroke_width(1. / dpr_scale);
|
||||
paint.set_stroke_width(1. / scale);
|
||||
canvas.draw_path(&path, &paint);
|
||||
}
|
||||
handle_stroke_caps(
|
||||
@ -396,7 +396,7 @@ fn draw_image_stroke_in_container(
|
||||
canvas,
|
||||
is_open,
|
||||
svg_attrs,
|
||||
dpr_scale,
|
||||
scale,
|
||||
);
|
||||
canvas.restore();
|
||||
}
|
||||
@ -437,8 +437,8 @@ fn draw_image_stroke_in_container(
|
||||
* This SHOULD be the only public function in this module.
|
||||
*/
|
||||
pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) {
|
||||
let scale = render_state.get_scale();
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::Strokes);
|
||||
let dpr_scale = render_state.viewbox.zoom * render_state.options.dpr();
|
||||
let selrect = shape.selrect;
|
||||
let path_transform = shape.to_path_transform();
|
||||
let svg_attrs = &shape.svg_attrs;
|
||||
@ -455,15 +455,14 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) {
|
||||
&selrect,
|
||||
&shape_type.corners(),
|
||||
svg_attrs,
|
||||
dpr_scale,
|
||||
scale,
|
||||
);
|
||||
}
|
||||
Type::Circle => {
|
||||
draw_stroke_on_circle(canvas, stroke, &selrect, &selrect, &svg_attrs, dpr_scale)
|
||||
draw_stroke_on_circle(canvas, stroke, &selrect, &selrect, svg_attrs, scale)
|
||||
}
|
||||
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
||||
if let Some(path) = shape_type.path() {
|
||||
let svg_attrs = &shape.svg_attrs;
|
||||
draw_stroke_on_path(
|
||||
canvas,
|
||||
stroke,
|
||||
@ -471,7 +470,7 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) {
|
||||
&selrect,
|
||||
path_transform.as_ref(),
|
||||
svg_attrs,
|
||||
dpr_scale,
|
||||
scale,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ pub enum SurfaceId {
|
||||
Strokes,
|
||||
Shadow,
|
||||
DropShadows,
|
||||
InnerShadows,
|
||||
Overlay,
|
||||
Debug,
|
||||
}
|
||||
@ -32,6 +33,7 @@ pub struct Surfaces {
|
||||
shadow: skia::Surface,
|
||||
// used for new shadow rendering
|
||||
drop_shadows: skia::Surface,
|
||||
inner_shadows: skia::Surface,
|
||||
// for drawing the things that are over shadows.
|
||||
overlay: skia::Surface,
|
||||
// for drawing debug info.
|
||||
@ -63,6 +65,7 @@ impl Surfaces {
|
||||
let current = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
let shadow = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
let drop_shadows = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
let inner_shadows = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
let overlay = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
let shape_fills = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
let shape_strokes = target.new_surface_with_dimensions(extra_tile_dims).unwrap();
|
||||
@ -78,6 +81,7 @@ impl Surfaces {
|
||||
current,
|
||||
shadow,
|
||||
drop_shadows,
|
||||
inner_shadows,
|
||||
overlay,
|
||||
shape_fills,
|
||||
shape_strokes,
|
||||
@ -154,7 +158,12 @@ impl Surfaces {
|
||||
-render_area.top() + self.margins.height as f32 / viewbox.zoom,
|
||||
);
|
||||
self.apply_mut(
|
||||
&[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows],
|
||||
&[
|
||||
SurfaceId::Fills,
|
||||
SurfaceId::Strokes,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::InnerShadows,
|
||||
],
|
||||
|s| {
|
||||
s.canvas().restore();
|
||||
s.canvas().save();
|
||||
@ -169,6 +178,7 @@ impl Surfaces {
|
||||
SurfaceId::Current => &mut self.current,
|
||||
SurfaceId::Shadow => &mut self.shadow,
|
||||
SurfaceId::DropShadows => &mut self.drop_shadows,
|
||||
SurfaceId::InnerShadows => &mut self.inner_shadows,
|
||||
SurfaceId::Overlay => &mut self.overlay,
|
||||
SurfaceId::Fills => &mut self.shape_fills,
|
||||
SurfaceId::Strokes => &mut self.shape_strokes,
|
||||
@ -205,6 +215,7 @@ impl Surfaces {
|
||||
pub fn reset(&mut self, color: skia::Color) {
|
||||
self.canvas(SurfaceId::Fills).restore_to_count(1);
|
||||
self.canvas(SurfaceId::DropShadows).restore_to_count(1);
|
||||
self.canvas(SurfaceId::InnerShadows).restore_to_count(1);
|
||||
self.canvas(SurfaceId::Strokes).restore_to_count(1);
|
||||
self.canvas(SurfaceId::Current).restore_to_count(1);
|
||||
self.apply_mut(
|
||||
@ -213,6 +224,7 @@ impl Surfaces {
|
||||
SurfaceId::Strokes,
|
||||
SurfaceId::Current,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::InnerShadows,
|
||||
SurfaceId::Shadow,
|
||||
SurfaceId::Overlay,
|
||||
],
|
||||
|
||||
@ -749,6 +749,10 @@ impl Shape {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_fills(&self) -> bool {
|
||||
!self.fills.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -76,6 +76,8 @@ impl Shadow {
|
||||
paint
|
||||
}
|
||||
|
||||
// To be removed: Old methods for Drop Shadows and Inner Shadows (now used opnly for stroke shadows)
|
||||
|
||||
fn drop_shadow_filters(&self, scale: f32) -> Option<ImageFilter> {
|
||||
let mut filter = image_filters::drop_shadow_only(
|
||||
(self.offset.0 * scale, self.offset.1 * scale),
|
||||
@ -124,7 +126,8 @@ impl Shadow {
|
||||
filter
|
||||
}
|
||||
|
||||
// New methods for DropShadow
|
||||
// New methods for Drop Shadows
|
||||
|
||||
pub fn get_drop_shadow_paint(&self) -> skia::Paint {
|
||||
let mut paint = skia::Paint::default();
|
||||
|
||||
@ -152,4 +155,43 @@ impl Shadow {
|
||||
|
||||
filter
|
||||
}
|
||||
|
||||
// New methods for Inner Shadows
|
||||
|
||||
pub fn get_inner_shadow_paint(&self) -> skia::Paint {
|
||||
let mut paint = skia::Paint::default();
|
||||
|
||||
let image_filter = self.get_inner_shadow_filter();
|
||||
|
||||
paint.set_image_filter(image_filter);
|
||||
paint.set_anti_alias(true);
|
||||
|
||||
paint
|
||||
}
|
||||
|
||||
fn get_inner_shadow_filter(&self) -> Option<ImageFilter> {
|
||||
let sigma = self.blur * 0.5;
|
||||
let mut filter = skia::image_filters::drop_shadow_only(
|
||||
(self.offset.0, self.offset.1), // DPR?
|
||||
(sigma, sigma),
|
||||
skia::Color::BLACK,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
filter = skia::image_filters::color_filter(
|
||||
skia::color_filters::blend(self.color, skia::BlendMode::SrcOut).unwrap(),
|
||||
filter,
|
||||
None,
|
||||
);
|
||||
|
||||
if self.spread > 0. {
|
||||
filter = skia::image_filters::dilate((self.spread, self.spread), filter, None);
|
||||
}
|
||||
|
||||
filter = skia::image_filters::blend(skia::BlendMode::SrcIn, None, filter, None);
|
||||
|
||||
filter
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,4 +51,8 @@ impl Viewbox {
|
||||
self.area
|
||||
.set_wh(self.width / self.zoom, self.height / self.zoom);
|
||||
}
|
||||
|
||||
pub fn zoom(&self) -> f32 {
|
||||
self.zoom
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user