mirror of
https://github.com/penpot/penpot.git
synced 2025-05-23 22:26:11 +02:00
🎉 Implement inner shadows (wasm) (#5767)
* 🎉 Implement inner shadows (wasm) * 🐛 Fix reset canvas problem --------- Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
This commit is contained in:
parent
2ffb77cb4d
commit
6cbaacf1e0
6 changed files with 127 additions and 20 deletions
|
@ -12,6 +12,7 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape-tree :as ctt]
|
[app.common.types.shape-tree :as ctt]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
|
@ -111,7 +112,8 @@
|
||||||
text-modifiers (mf/deref refs/workspace-text-modifier)
|
text-modifiers (mf/deref refs/workspace-text-modifier)
|
||||||
|
|
||||||
objects-modified (mf/with-memo [base-objects text-modifiers modifiers]
|
objects-modified (mf/with-memo [base-objects text-modifiers modifiers]
|
||||||
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers))
|
(binding [cts/*wasm-sync* false]
|
||||||
|
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers)))
|
||||||
|
|
||||||
selected-shapes (keep (d/getf objects-modified) selected)
|
selected-shapes (keep (d/getf objects-modified) selected)
|
||||||
|
|
||||||
|
|
|
@ -13,4 +13,3 @@
|
||||||
(let [fn-sym (with-meta (gensym "fn-") {:tag 'function})]
|
(let [fn-sym (with-meta (gensym "fn-") {:tag 'function})]
|
||||||
`(let [~fn-sym (cljs.core/unchecked-get ~module ~name)]
|
`(let [~fn-sym (cljs.core/unchecked-get ~module ~name)]
|
||||||
(~fn-sym ~@params))))
|
(~fn-sym ~@params))))
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use skia::Contains;
|
|
||||||
use skia_safe as skia;
|
use skia_safe as skia;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::math;
|
use crate::math;
|
||||||
use crate::view::Viewbox;
|
use crate::view::Viewbox;
|
||||||
use skia::Matrix;
|
use skia::{Contains, Matrix};
|
||||||
|
|
||||||
mod blend;
|
mod blend;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
@ -60,6 +59,7 @@ pub(crate) struct RenderState {
|
||||||
pub render_surface: skia::Surface,
|
pub render_surface: skia::Surface,
|
||||||
pub drawing_surface: skia::Surface,
|
pub drawing_surface: skia::Surface,
|
||||||
pub shadow_surface: skia::Surface,
|
pub shadow_surface: skia::Surface,
|
||||||
|
pub overlay_surface: skia::Surface,
|
||||||
pub debug_surface: skia::Surface,
|
pub debug_surface: skia::Surface,
|
||||||
pub font_provider: skia::textlayout::TypefaceFontProvider,
|
pub font_provider: skia::textlayout::TypefaceFontProvider,
|
||||||
pub cached_surface_image: Option<CachedSurfaceImage>,
|
pub cached_surface_image: Option<CachedSurfaceImage>,
|
||||||
|
@ -86,6 +86,9 @@ impl RenderState {
|
||||||
let shadow_surface = final_surface
|
let shadow_surface = final_surface
|
||||||
.new_surface_with_dimensions((width, height))
|
.new_surface_with_dimensions((width, height))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let overlay_surface = final_surface
|
||||||
|
.new_surface_with_dimensions((width, height))
|
||||||
|
.unwrap();
|
||||||
let drawing_surface = final_surface
|
let drawing_surface = final_surface
|
||||||
.new_surface_with_dimensions((width, height))
|
.new_surface_with_dimensions((width, height))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -102,6 +105,7 @@ impl RenderState {
|
||||||
gpu_state,
|
gpu_state,
|
||||||
final_surface,
|
final_surface,
|
||||||
render_surface,
|
render_surface,
|
||||||
|
overlay_surface,
|
||||||
shadow_surface,
|
shadow_surface,
|
||||||
drawing_surface,
|
drawing_surface,
|
||||||
debug_surface,
|
debug_surface,
|
||||||
|
@ -164,6 +168,10 @@ impl RenderState {
|
||||||
.final_surface
|
.final_surface
|
||||||
.new_surface_with_dimensions((dpr_width, dpr_height))
|
.new_surface_with_dimensions((dpr_width, dpr_height))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
self.overlay_surface = self
|
||||||
|
.final_surface
|
||||||
|
.new_surface_with_dimensions((dpr_width, dpr_height))
|
||||||
|
.unwrap();
|
||||||
self.shadow_surface = self
|
self.shadow_surface = self
|
||||||
.final_surface
|
.final_surface
|
||||||
.new_surface_with_dimensions((dpr_width, dpr_height))
|
.new_surface_with_dimensions((dpr_width, dpr_height))
|
||||||
|
@ -201,6 +209,10 @@ impl RenderState {
|
||||||
.canvas()
|
.canvas()
|
||||||
.clear(self.background_color)
|
.clear(self.background_color)
|
||||||
.reset_matrix();
|
.reset_matrix();
|
||||||
|
self.overlay_surface
|
||||||
|
.canvas()
|
||||||
|
.clear(self.background_color)
|
||||||
|
.reset_matrix();
|
||||||
self.debug_surface
|
self.debug_surface
|
||||||
.canvas()
|
.canvas()
|
||||||
.clear(skia::Color::TRANSPARENT)
|
.clear(skia::Color::TRANSPARENT)
|
||||||
|
@ -232,8 +244,20 @@ impl RenderState {
|
||||||
.context
|
.context
|
||||||
.flush_and_submit_surface(&mut self.render_surface, None);
|
.flush_and_submit_surface(&mut self.render_surface, None);
|
||||||
|
|
||||||
self.shadow_surface.canvas().clear(skia::Color::TRANSPARENT);
|
self.gpu_state
|
||||||
|
.context
|
||||||
|
.flush_and_submit_surface(&mut self.overlay_surface, None);
|
||||||
|
self.overlay_surface.draw(
|
||||||
|
&mut self.render_surface.canvas(),
|
||||||
|
(0.0, 0.0),
|
||||||
|
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.shadow_surface.canvas().clear(skia::Color::TRANSPARENT);
|
||||||
|
self.overlay_surface
|
||||||
|
.canvas()
|
||||||
|
.clear(skia::Color::TRANSPARENT);
|
||||||
self.drawing_surface
|
self.drawing_surface
|
||||||
.canvas()
|
.canvas()
|
||||||
.clear(skia::Color::TRANSPARENT);
|
.clear(skia::Color::TRANSPARENT);
|
||||||
|
@ -296,13 +320,25 @@ impl RenderState {
|
||||||
for stroke in shape.strokes().rev() {
|
for stroke in shape.strokes().rev() {
|
||||||
strokes::render(self, shape, stroke);
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
|
||||||
|
shadows::render_drop_shadow(
|
||||||
|
self,
|
||||||
|
shadow,
|
||||||
|
self.viewbox.zoom * self.options.dpr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
|
|
||||||
shadows::render_drop_shadow(self, shadow, self.viewbox.zoom * self.options.dpr());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.apply_drawing_to_render_canvas();
|
self.apply_drawing_to_render_canvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use super::RenderState;
|
||||||
use crate::shapes::Shadow;
|
use crate::shapes::Shadow;
|
||||||
|
|
||||||
pub fn render_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
|
pub fn render_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
|
||||||
let shadow_paint = shadow.to_paint(true, scale);
|
let shadow_paint = shadow.to_paint(scale);
|
||||||
render_state.drawing_surface.draw(
|
render_state.drawing_surface.draw(
|
||||||
&mut render_state.shadow_surface.canvas(),
|
&mut render_state.shadow_surface.canvas(),
|
||||||
(0.0, 0.0),
|
(0.0, 0.0),
|
||||||
|
@ -18,6 +18,30 @@ pub fn render_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale
|
||||||
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest),
|
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest),
|
||||||
Some(&skia::Paint::default()),
|
Some(&skia::Paint::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
render_state
|
||||||
|
.shadow_surface
|
||||||
|
.canvas()
|
||||||
|
.clear(skia::Color::TRANSPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_inner_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
|
||||||
|
let shadow_paint = shadow.to_paint(scale);
|
||||||
|
|
||||||
|
render_state.drawing_surface.draw(
|
||||||
|
render_state.shadow_surface.canvas(),
|
||||||
|
(0.0, 0.0),
|
||||||
|
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest),
|
||||||
|
Some(&shadow_paint),
|
||||||
|
);
|
||||||
|
|
||||||
|
render_state.shadow_surface.draw(
|
||||||
|
&mut render_state.overlay_surface.canvas(),
|
||||||
|
(0.0, 0.0),
|
||||||
|
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
render_state
|
render_state
|
||||||
.shadow_surface
|
.shadow_surface
|
||||||
.canvas()
|
.canvas()
|
||||||
|
|
|
@ -399,6 +399,12 @@ impl Shape {
|
||||||
.filter(|shadow| shadow.style() == ShadowStyle::Drop)
|
.filter(|shadow| shadow.style() == ShadowStyle::Drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inner_shadows(&self) -> impl DoubleEndedIterator<Item = &Shadow> {
|
||||||
|
self.shadows
|
||||||
|
.iter()
|
||||||
|
.filter(|shadow| shadow.style() == ShadowStyle::Inner)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_path_transform(&self) -> Option<skia::Matrix> {
|
pub fn to_path_transform(&self) -> Option<skia::Matrix> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Kind::Path(_) | Kind::Bool(_, _) => {
|
Kind::Path(_) | Kind::Bool(_, _) => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use skia_safe::{self as skia, image_filters};
|
use skia_safe::{self as skia, image_filters, ImageFilter};
|
||||||
|
|
||||||
use super::Color;
|
use super::Color;
|
||||||
|
|
||||||
|
@ -26,10 +26,10 @@ impl Default for ShadowStyle {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct Shadow {
|
pub struct Shadow {
|
||||||
color: Color,
|
pub color: Color,
|
||||||
blur: f32,
|
pub blur: f32,
|
||||||
spread: f32,
|
pub spread: f32,
|
||||||
offset: (f32, f32),
|
pub offset: (f32, f32),
|
||||||
style: ShadowStyle,
|
style: ShadowStyle,
|
||||||
hidden: bool,
|
hidden: bool,
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,21 @@ impl Shadow {
|
||||||
self.hidden
|
self.hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_paint(&self, dilate: bool, scale: f32) -> skia::Paint {
|
pub fn to_paint(&self, scale: f32) -> skia::Paint {
|
||||||
let mut paint = skia::Paint::default();
|
let mut paint = skia::Paint::default();
|
||||||
|
|
||||||
|
let image_filter = match self.style {
|
||||||
|
ShadowStyle::Drop => self.drop_shadow_filters(scale),
|
||||||
|
ShadowStyle::Inner => self.inner_shadow_filters(scale),
|
||||||
|
};
|
||||||
|
|
||||||
|
paint.set_image_filter(image_filter);
|
||||||
|
paint.set_anti_alias(true);
|
||||||
|
|
||||||
|
paint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_shadow_filters(&self, scale: f32) -> Option<ImageFilter> {
|
||||||
let mut filter = image_filters::drop_shadow_only(
|
let mut filter = image_filters::drop_shadow_only(
|
||||||
(self.offset.0 * scale, self.offset.1 * scale),
|
(self.offset.0 * scale, self.offset.1 * scale),
|
||||||
(self.blur * scale, self.blur * scale),
|
(self.blur * scale, self.blur * scale),
|
||||||
|
@ -73,14 +86,41 @@ impl Shadow {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
if dilate {
|
if self.spread > 0. {
|
||||||
filter =
|
filter =
|
||||||
image_filters::dilate((self.spread * scale, self.spread * scale), filter, None);
|
image_filters::dilate((self.spread * scale, self.spread * scale), filter, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
paint.set_image_filter(filter);
|
filter
|
||||||
paint.set_anti_alias(true);
|
}
|
||||||
|
|
||||||
paint
|
fn inner_shadow_filters(&self, scale: f32) -> Option<ImageFilter> {
|
||||||
|
let sigma = self.blur * 0.5;
|
||||||
|
let mut filter = skia::image_filters::drop_shadow_only(
|
||||||
|
(self.offset.0 * scale, self.offset.1 * scale), // DPR?
|
||||||
|
(sigma * scale, sigma * scale),
|
||||||
|
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 * scale, self.spread * scale),
|
||||||
|
filter,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
filter = skia::image_filters::blend(skia::BlendMode::SrcIn, None, filter, None);
|
||||||
|
|
||||||
|
filter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue