🐛 Fix big blur rendering for wasm render

This commit is contained in:
Alejandro Alonso
2025-08-09 08:42:55 +02:00
parent c29a8cb0c4
commit c7a4c67d83
4 changed files with 107 additions and 32 deletions

View File

@@ -1,4 +1,5 @@
mod blend; mod blend;
mod blurs;
mod debug; mod debug;
mod fills; mod fills;
mod fonts; mod fonts;
@@ -21,7 +22,7 @@ use options::RenderOptions;
use surfaces::{SurfaceId, Surfaces}; use surfaces::{SurfaceId, Surfaces};
use crate::performance; use crate::performance;
use crate::shapes::{Corners, Fill, Shape, SolidColor, StructureEntry, Type}; use crate::shapes::{BlurType, Corners, Fill, Shape, SolidColor, StructureEntry, Type};
use crate::state::ShapesPool; use crate::state::ShapesPool;
use crate::tiles::{self, PendingTiles, TileRect}; use crate::tiles::{self, PendingTiles, TileRect};
use crate::uuid::Uuid; use crate::uuid::Uuid;
@@ -29,6 +30,7 @@ use crate::view::Viewbox;
use crate::wapi; use crate::wapi;
pub use blend::BlendMode; pub use blend::BlendMode;
pub use blurs::*;
pub use fonts::*; pub use fonts::*;
pub use images::*; pub use images::*;
@@ -377,10 +379,19 @@ impl RenderState {
Some(&skia::Paint::default()), Some(&skia::Paint::default()),
); );
} }
// Draw blurs after everything else
self.surfaces.draw_into(
SurfaceId::Blurs,
SurfaceId::Current,
Some(&skia::Paint::default()),
);
let surface_ids = SurfaceId::Strokes as u32 let surface_ids = SurfaceId::Strokes as u32
| SurfaceId::Fills as u32 | SurfaceId::Fills as u32
| SurfaceId::DropShadows as u32 | SurfaceId::DropShadows as u32
| SurfaceId::InnerShadows as u32; | SurfaceId::InnerShadows as u32
| SurfaceId::Blurs as u32;
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().clear(skia::Color::TRANSPARENT); s.canvas().clear(skia::Color::TRANSPARENT);
@@ -410,7 +421,8 @@ impl RenderState {
let surface_ids = SurfaceId::Strokes as u32 let surface_ids = SurfaceId::Strokes as u32
| SurfaceId::Fills as u32 | SurfaceId::Fills as u32
| SurfaceId::DropShadows as u32 | SurfaceId::DropShadows as u32
| SurfaceId::InnerShadows as u32; | SurfaceId::InnerShadows as u32
| SurfaceId::Blurs as u32;
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().save(); s.canvas().save();
}); });
@@ -456,7 +468,8 @@ impl RenderState {
let surface_ids = SurfaceId::Strokes as u32 let surface_ids = SurfaceId::Strokes as u32
| SurfaceId::Fills as u32 | SurfaceId::Fills as u32
| SurfaceId::DropShadows as u32 | SurfaceId::DropShadows as u32
| SurfaceId::InnerShadows as u32; | SurfaceId::InnerShadows as u32
| SurfaceId::Blurs as u32;
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().concat(&matrix); s.canvas().concat(&matrix);
}); });
@@ -466,6 +479,7 @@ impl RenderState {
if !shape.has_visible_strokes() { if !shape.has_visible_strokes() {
shadows::render_text_drop_shadows(self, &shape, &mut paragraphs, antialias); shadows::render_text_drop_shadows(self, &shape, &mut paragraphs, antialias);
render_text_blurs(self, &shape, &mut paragraphs, antialias);
} }
text::render(self, &shape, &mut paragraphs, None, None); text::render(self, &shape, &mut paragraphs, None, None);
@@ -491,6 +505,12 @@ impl RenderState {
&mut stroke_paragraphs, &mut stroke_paragraphs,
antialias, antialias,
); );
render_text_blurs(
self,
&shape,
&mut stroke_paragraphs,
antialias,
);
strokes::render( strokes::render(
self, self,
&shape, &shape,
@@ -510,12 +530,14 @@ impl RenderState {
} }
shadows::render_text_inner_shadows(self, &shape, &mut paragraphs, antialias); shadows::render_text_inner_shadows(self, &shape, &mut paragraphs, antialias);
render_text_blurs(self, &shape, &mut paragraphs, antialias);
} }
_ => { _ => {
let surface_ids = SurfaceId::Strokes as u32 let surface_ids = SurfaceId::Strokes as u32
| SurfaceId::Fills as u32 | SurfaceId::Fills as u32
| SurfaceId::DropShadows as u32 | SurfaceId::DropShadows as u32
| SurfaceId::InnerShadows as u32; | SurfaceId::InnerShadows as u32
| SurfaceId::Blurs as u32;
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().concat(&matrix); s.canvas().concat(&matrix);
}); });
@@ -541,8 +563,12 @@ impl RenderState {
} }
} }
// Render blurs for fills
// render_fill_blurs(self, &shape, antialias);
for stroke in shape.visible_strokes().rev() { for stroke in shape.visible_strokes().rev() {
shadows::render_stroke_drop_shadows(self, &shape, stroke, antialias); shadows::render_stroke_drop_shadows(self, &shape, stroke, antialias);
// render_stroke_blurs(self, &shape, stroke, antialias);
strokes::render(self, &shape, stroke, None, None, None, antialias, None); strokes::render(self, &shape, stroke, None, None, None, antialias, None);
shadows::render_stroke_inner_shadows(self, &shape, stroke, antialias); shadows::render_stroke_inner_shadows(self, &shape, stroke, antialias);
} }
@@ -736,9 +762,9 @@ impl RenderState {
.save_layer(&mask_rec); .save_layer(&mask_rec);
} }
if let Some(image_filter) = element.image_filter(self.get_scale()) { // if let Some(image_filter) = element.image_filter(self.get_scale()) {
paint.set_image_filter(image_filter); // paint.set_image_filter(image_filter);
} // }
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint); let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
self.surfaces self.surfaces

View File

@@ -1,8 +1,8 @@
use skia_safe::{self as skia, Paint, RRect}; use skia_safe::{self as skia, image_filters, Paint, RRect};
use super::{RenderState, SurfaceId}; use super::{RenderState, SurfaceId};
use crate::render::get_source_rect; use crate::render::get_source_rect;
use crate::shapes::{Fill, Frame, ImageFill, Rect, Shape, Type}; use crate::shapes::{BlurType, Fill, Frame, ImageFill, Rect, Shape, Type};
fn draw_image_fill( fn draw_image_fill(
render_state: &mut RenderState, render_state: &mut RenderState,
@@ -84,26 +84,29 @@ fn draw_image_fill(
* This SHOULD be the only public function in this module. * This SHOULD be the only public function in this module.
*/ */
pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill, antialias: bool) { pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill, antialias: bool) {
let paint = &fill.to_paint(&shape.selrect, antialias); let mut paint = fill.to_paint(&shape.selrect, antialias);
if let Some(image_filter) = shape.image_filter(1.) {
paint.set_image_filter(image_filter);
}
match (fill, &shape.shape_type) { match (fill, &shape.shape_type) {
(Fill::Image(image_fill), _) => { (Fill::Image(image_fill), _) => {
draw_image_fill(render_state, shape, image_fill, paint, antialias); draw_image_fill(render_state, shape, image_fill, &paint, antialias);
} }
(_, Type::Rect(_) | Type::Frame(_)) => { (_, Type::Rect(_) | Type::Frame(_)) => {
render_state render_state
.surfaces .surfaces
.draw_rect_to(SurfaceId::Fills, shape, paint); .draw_rect_to(SurfaceId::Fills, shape, &paint);
} }
(_, Type::Circle) => { (_, Type::Circle) => {
render_state render_state
.surfaces .surfaces
.draw_circle_to(SurfaceId::Fills, shape, paint); .draw_circle_to(SurfaceId::Fills, shape, &paint);
} }
(_, Type::Path(_)) | (_, Type::Bool(_)) => { (_, Type::Path(_)) | (_, Type::Bool(_)) => {
render_state render_state
.surfaces .surfaces
.draw_path_to(SurfaceId::Fills, shape, paint); .draw_path_to(SurfaceId::Fills, shape, &paint);
} }
(_, Type::Group(_)) => { (_, Type::Group(_)) => {
// Groups can have fills but they propagate them to their children // Groups can have fills but they propagate them to their children

View File

@@ -9,6 +9,19 @@ use super::{RenderState, SurfaceId};
use crate::render::text::{self}; use crate::render::text::{self};
use crate::render::{get_dest_rect, get_source_rect}; use crate::render::{get_dest_rect, get_source_rect};
/// Composes blur and shadow filters, returning a combined filter if both are present,
/// or the individual filter if only one is present, or None if neither is present.
fn compose_filters(blur: Option<&ImageFilter>, shadow: Option<&ImageFilter>) -> Option<ImageFilter> {
match (blur, shadow) {
(Some(blur_filter), Some(shadow_filter)) => {
ImageFilter::compose(blur_filter, shadow_filter)
}
(Some(blur_filter), None) => Some(blur_filter.clone()),
(None, Some(shadow_filter)) => Some(shadow_filter.clone()),
(None, None) => None,
}
}
// FIXME: See if we can simplify these arguments // FIXME: See if we can simplify these arguments
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn draw_stroke_on_rect( fn draw_stroke_on_rect(
@@ -20,6 +33,7 @@ fn draw_stroke_on_rect(
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
shadow: Option<&ImageFilter>, shadow: Option<&ImageFilter>,
blur: Option<&ImageFilter>,
antialias: bool, antialias: bool,
) { ) {
// Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to: // Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to:
@@ -29,9 +43,9 @@ fn draw_stroke_on_rect(
let stroke_rect = stroke.outer_rect(rect); let stroke_rect = stroke.outer_rect(rect);
let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias); let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
if let Some(filter) = shadow { // Apply both blur and shadow filters if present, composing them if necessary.
paint.set_image_filter(filter.clone()); let filter = compose_filters(blur, shadow);
} paint.set_image_filter(filter);
match corners { match corners {
Some(radii) => { Some(radii) => {
@@ -55,6 +69,7 @@ fn draw_stroke_on_circle(
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
shadow: Option<&ImageFilter>, shadow: Option<&ImageFilter>,
blur: Option<&ImageFilter>,
antialias: bool, antialias: bool,
) { ) {
// Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to: // Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to:
@@ -64,9 +79,9 @@ fn draw_stroke_on_circle(
let stroke_rect = stroke.outer_rect(rect); let stroke_rect = stroke.outer_rect(rect);
let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias); let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
if let Some(filter) = shadow { // Apply both blur and shadow filters if present, composing them if necessary.
paint.set_image_filter(filter.clone()); let filter = compose_filters(blur, shadow);
} paint.set_image_filter(filter);
canvas.draw_oval(stroke_rect, &paint); canvas.draw_oval(stroke_rect, &paint);
} }
@@ -75,11 +90,17 @@ fn draw_outer_stroke_path(
canvas: &skia::Canvas, canvas: &skia::Canvas,
path: &skia::Path, path: &skia::Path,
paint: &skia::Paint, paint: &skia::Paint,
blur: Option<&ImageFilter>,
antialias: bool, antialias: bool,
) { ) {
let mut outer_paint = skia::Paint::default(); let mut outer_paint = skia::Paint::default();
outer_paint.set_blend_mode(skia::BlendMode::SrcOver); outer_paint.set_blend_mode(skia::BlendMode::SrcOver);
outer_paint.set_anti_alias(antialias); outer_paint.set_anti_alias(antialias);
if let Some(filter) = blur {
outer_paint.set_image_filter(filter.clone());
}
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&outer_paint); let layer_rec = skia::canvas::SaveLayerRec::default().paint(&outer_paint);
canvas.save_layer(&layer_rec); canvas.save_layer(&layer_rec);
canvas.draw_path(path, paint); canvas.draw_path(path, paint);
@@ -87,6 +108,7 @@ fn draw_outer_stroke_path(
let mut clear_paint = skia::Paint::default(); let mut clear_paint = skia::Paint::default();
clear_paint.set_blend_mode(skia::BlendMode::Clear); clear_paint.set_blend_mode(skia::BlendMode::Clear);
clear_paint.set_anti_alias(antialias); clear_paint.set_anti_alias(antialias);
canvas.draw_path(path, &clear_paint); canvas.draw_path(path, &clear_paint);
canvas.restore(); canvas.restore();
@@ -97,9 +119,17 @@ fn draw_inner_stroke_path(
canvas: &skia::Canvas, canvas: &skia::Canvas,
path: &skia::Path, path: &skia::Path,
paint: &skia::Paint, paint: &skia::Paint,
blur: Option<&ImageFilter>,
antialias: bool, antialias: bool,
) { ) {
canvas.save(); let mut inner_paint = skia::Paint::default();
inner_paint.set_anti_alias(antialias);
if let Some(filter) = blur {
inner_paint.set_image_filter(filter.clone());
}
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&inner_paint);
canvas.save_layer(&layer_rec);
canvas.clip_path(path, skia::ClipOp::Intersect, antialias); canvas.clip_path(path, skia::ClipOp::Intersect, antialias);
canvas.draw_path(path, paint); canvas.draw_path(path, paint);
canvas.restore(); canvas.restore();
@@ -117,6 +147,7 @@ pub fn draw_stroke_on_path(
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
shadow: Option<&ImageFilter>, shadow: Option<&ImageFilter>,
blur: Option<&ImageFilter>,
antialias: bool, antialias: bool,
) { ) {
let mut skia_path = path.to_skia_path(); let mut skia_path = path.to_skia_path();
@@ -127,19 +158,18 @@ pub fn draw_stroke_on_path(
let mut paint: skia_safe::Handle<_> = let mut paint: skia_safe::Handle<_> =
stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias); stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
if let Some(filter) = shadow { let filter = compose_filters(blur, shadow);
paint.set_image_filter(filter.clone()); paint.set_image_filter(filter);
}
match stroke.render_kind(is_open) { match stroke.render_kind(is_open) {
StrokeKind::Inner => { StrokeKind::Inner => {
draw_inner_stroke_path(canvas, &skia_path, &paint, antialias); draw_inner_stroke_path(canvas, &skia_path, &paint, blur, antialias);
} }
StrokeKind::Center => { StrokeKind::Center => {
canvas.draw_path(&skia_path, &paint); canvas.draw_path(&skia_path, &paint);
} }
StrokeKind::Outer => { StrokeKind::Outer => {
draw_outer_stroke_path(canvas, &skia_path, &paint, antialias); draw_outer_stroke_path(canvas, &skia_path, &paint, blur, antialias);
} }
} }
@@ -388,6 +418,7 @@ fn draw_image_stroke_in_container(
svg_attrs, svg_attrs,
scale, scale,
None, None,
None,
antialias, antialias,
); );
} }
@@ -399,6 +430,7 @@ fn draw_image_stroke_in_container(
svg_attrs, svg_attrs,
scale, scale,
None, None,
None,
antialias, antialias,
), ),
@@ -517,12 +549,14 @@ pub fn render(
svg_attrs, svg_attrs,
scale, scale,
shadow, shadow,
shape.image_filter(1.).as_ref(),
antialias, antialias,
); );
} }
Type::Circle => draw_stroke_on_circle( Type::Circle => draw_stroke_on_circle(
canvas, stroke, &selrect, &selrect, svg_attrs, scale, shadow, antialias, canvas, stroke, &selrect, &selrect, svg_attrs, scale, shadow, shape.image_filter(1.).as_ref(), antialias,
), ),
//TODO: Add blur and shadow to text
Type::Text(_) => { Type::Text(_) => {
text::render( text::render(
render_state, render_state,
@@ -543,6 +577,7 @@ pub fn render(
svg_attrs, svg_attrs,
scale, scale,
shadow, shadow,
shape.image_filter(1.).as_ref(),
antialias, antialias,
); );
} }
@@ -572,6 +607,7 @@ pub fn render_text_paths(
let mut paint: skia_safe::Handle<_> = let mut paint: skia_safe::Handle<_> =
stroke.to_text_stroked_paint(false, selrect, svg_attrs, scale, antialias); stroke.to_text_stroked_paint(false, selrect, svg_attrs, scale, antialias);
//TODO: Add blur and shadow to text
if let Some(filter) = shadow { if let Some(filter) = shadow {
paint.set_image_filter(filter.clone()); paint.set_image_filter(filter.clone());
} }
@@ -579,7 +615,8 @@ pub fn render_text_paths(
match stroke.render_kind(false) { match stroke.render_kind(false) {
StrokeKind::Inner => { StrokeKind::Inner => {
for (path, _) in paths { for (path, _) in paths {
draw_inner_stroke_path(canvas, path, &paint, antialias); //TODO: Add blur and shadow to text
draw_inner_stroke_path(canvas, path, &paint, None, antialias);
} }
} }
StrokeKind::Center => { StrokeKind::Center => {
@@ -589,7 +626,8 @@ pub fn render_text_paths(
} }
StrokeKind::Outer => { StrokeKind::Outer => {
for (path, _) in paths { for (path, _) in paths {
draw_outer_stroke_path(canvas, path, &paint, antialias); //TODO: Add blur and shadow to text
draw_outer_stroke_path(canvas, path, &paint, None, antialias);
} }
} }
} }

View File

@@ -24,8 +24,9 @@ pub enum SurfaceId {
Strokes = 0b0_0001_0000, Strokes = 0b0_0001_0000,
DropShadows = 0b0_0010_0000, DropShadows = 0b0_0010_0000,
InnerShadows = 0b0_0100_0000, InnerShadows = 0b0_0100_0000,
UI = 0b0_1000_0000, Blurs = 0b0_1000_0000,
Debug = 0b1_0000_0000, UI = 0b1_0000_0000,
Debug = 0b10_0000_0000,
} }
pub struct Surfaces { pub struct Surfaces {
@@ -42,6 +43,8 @@ pub struct Surfaces {
drop_shadows: skia::Surface, drop_shadows: skia::Surface,
// used for rendering over shadows. // used for rendering over shadows.
inner_shadows: skia::Surface, inner_shadows: skia::Surface,
// used for rendering blurs
blurs: skia::Surface,
// used for displaying auxiliary workspace elements // used for displaying auxiliary workspace elements
ui: skia::Surface, ui: skia::Surface,
// for drawing debug info. // for drawing debug info.
@@ -73,6 +76,8 @@ impl Surfaces {
gpu_state.create_surface_with_isize("drop_shadows".to_string(), extra_tile_dims); gpu_state.create_surface_with_isize("drop_shadows".to_string(), extra_tile_dims);
let inner_shadows = let inner_shadows =
gpu_state.create_surface_with_isize("inner_shadows".to_string(), extra_tile_dims); gpu_state.create_surface_with_isize("inner_shadows".to_string(), extra_tile_dims);
let blurs =
gpu_state.create_surface_with_isize("blurs".to_string(), extra_tile_dims);
let shape_fills = let shape_fills =
gpu_state.create_surface_with_isize("shape_fills".to_string(), extra_tile_dims); gpu_state.create_surface_with_isize("shape_fills".to_string(), extra_tile_dims);
let shape_strokes = let shape_strokes =
@@ -88,6 +93,7 @@ impl Surfaces {
current, current,
drop_shadows, drop_shadows,
inner_shadows, inner_shadows,
blurs,
shape_fills, shape_fills,
shape_strokes, shape_strokes,
ui, ui,
@@ -180,6 +186,7 @@ impl Surfaces {
-render_area.left() + self.margins.width as f32 / scale, -render_area.left() + self.margins.width as f32 / scale,
-render_area.top() + self.margins.height as f32 / scale, -render_area.top() + self.margins.height as f32 / scale,
); );
self.apply_mut( self.apply_mut(
SurfaceId::Fills as u32 SurfaceId::Fills as u32
| SurfaceId::Strokes as u32 | SurfaceId::Strokes as u32
@@ -201,6 +208,7 @@ impl Surfaces {
SurfaceId::Current => &mut self.current, SurfaceId::Current => &mut self.current,
SurfaceId::DropShadows => &mut self.drop_shadows, SurfaceId::DropShadows => &mut self.drop_shadows,
SurfaceId::InnerShadows => &mut self.inner_shadows, SurfaceId::InnerShadows => &mut self.inner_shadows,
SurfaceId::Blurs => &mut self.blurs,
SurfaceId::Fills => &mut self.shape_fills, SurfaceId::Fills => &mut self.shape_fills,
SurfaceId::Strokes => &mut self.shape_strokes, SurfaceId::Strokes => &mut self.shape_strokes,
SurfaceId::Debug => &mut self.debug, SurfaceId::Debug => &mut self.debug,