mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
Merge pull request #7098 from penpot/superalex-fix-big-blur-2
🐛 Fix big blur rendering for wasm render
This commit is contained in:
6261
frontend/playwright/data/render-wasm/get-file-blurs.json
Normal file
6261
frontend/playwright/data/render-wasm/get-file-blurs.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -161,3 +161,19 @@ test("Updates canvas background", async ({
|
||||
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders a file with blurs applied to any kind of shape", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-blurs.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
id: "aa0a383a-7553-808a-8006-ae1237b52cf9",
|
||||
pageId: "aa0a383a-7553-808a-8006-ae160ba8bd86",
|
||||
});
|
||||
await workspace.waitForFirstRender();
|
||||
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
@@ -1,6 +1,7 @@
|
||||
mod blend;
|
||||
mod debug;
|
||||
mod fills;
|
||||
pub mod filters;
|
||||
mod fonts;
|
||||
mod gpu_state;
|
||||
pub mod grid_layout;
|
||||
@@ -21,7 +22,7 @@ use options::RenderOptions;
|
||||
pub use surfaces::{SurfaceId, Surfaces};
|
||||
|
||||
use crate::performance;
|
||||
use crate::shapes::{Corners, Fill, Shape, SolidColor, StructureEntry, Type};
|
||||
use crate::shapes::{Blur, BlurType, Corners, Fill, Shape, SolidColor, StructureEntry, Type};
|
||||
use crate::state::ShapesPool;
|
||||
use crate::tiles::{self, PendingTiles, TileRect};
|
||||
use crate::uuid::Uuid;
|
||||
@@ -179,6 +180,7 @@ pub(crate) struct RenderState {
|
||||
// can affect its child elements if they don't specify one themselves. If the planned
|
||||
// migration to remove group-level fills is completed, this code should be removed.
|
||||
pub nested_fills: Vec<Vec<Fill>>,
|
||||
pub nested_blurs: Vec<Option<Blur>>,
|
||||
pub show_grid: Option<Uuid>,
|
||||
pub focus_mode: FocusMode,
|
||||
}
|
||||
@@ -269,6 +271,7 @@ impl RenderState {
|
||||
),
|
||||
pending_tiles: PendingTiles::new_empty(),
|
||||
nested_fills: vec![],
|
||||
nested_blurs: vec![],
|
||||
show_grid: None,
|
||||
focus_mode: FocusMode::new(),
|
||||
}
|
||||
@@ -451,6 +454,23 @@ impl RenderState {
|
||||
shape.to_mut().apply_transform(shape_modifiers);
|
||||
}
|
||||
|
||||
let mut nested_blur_value = 0.;
|
||||
for nested_blur in self.nested_blurs.iter().flatten() {
|
||||
if !nested_blur.hidden && nested_blur.blur_type == BlurType::Layer {
|
||||
nested_blur_value += nested_blur.value.powf(2.);
|
||||
}
|
||||
}
|
||||
|
||||
if !shape.blur.hidden && shape.blur.blur_type == BlurType::Layer {
|
||||
nested_blur_value += shape.blur.value.powf(2.);
|
||||
}
|
||||
|
||||
if nested_blur_value > 0. {
|
||||
shape
|
||||
.to_mut()
|
||||
.set_blur(BlurType::Layer as u8, false, nested_blur_value.sqrt());
|
||||
}
|
||||
|
||||
let center = shape.center();
|
||||
let mut matrix = shape.transform;
|
||||
matrix.post_translate(center);
|
||||
@@ -491,7 +511,10 @@ impl RenderState {
|
||||
});
|
||||
|
||||
let text_content = text_content.new_bounds(shape.selrect());
|
||||
let mut paragraphs = text_content.get_skia_paragraphs();
|
||||
let mut paragraphs = text_content.get_skia_paragraphs(
|
||||
shape.image_filter(1.).as_ref(),
|
||||
shape.mask_filter(1.).as_ref(),
|
||||
);
|
||||
|
||||
if !shape.has_visible_strokes() {
|
||||
shadows::render_text_drop_shadows(self, &shape, &mut paragraphs, antialias);
|
||||
@@ -500,8 +523,12 @@ impl RenderState {
|
||||
text::render(self, &shape, &mut paragraphs, None, None);
|
||||
|
||||
for stroke in shape.visible_strokes().rev() {
|
||||
let mut stroke_paragraphs =
|
||||
text_content.get_skia_stroke_paragraphs(stroke, &shape.selrect());
|
||||
let mut stroke_paragraphs = text_content.get_skia_stroke_paragraphs(
|
||||
stroke,
|
||||
&shape.selrect(),
|
||||
shape.image_filter(1.).as_ref(),
|
||||
shape.mask_filter(1.).as_ref(),
|
||||
);
|
||||
shadows::render_text_drop_shadows(
|
||||
self,
|
||||
&shape,
|
||||
@@ -751,6 +778,13 @@ impl RenderState {
|
||||
}
|
||||
}
|
||||
|
||||
match element.shape_type {
|
||||
Type::Frame(_) | Type::Group(_) => {
|
||||
self.nested_blurs.push(Some(element.blur));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_blend_mode(element.blend_mode().into());
|
||||
paint.set_alpha_f(element.opacity());
|
||||
@@ -767,10 +801,6 @@ impl RenderState {
|
||||
.save_layer(&mask_rec);
|
||||
}
|
||||
|
||||
if let Some(image_filter) = element.image_filter(self.get_scale()) {
|
||||
paint.set_image_filter(image_filter);
|
||||
}
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||
self.surfaces
|
||||
.canvas(SurfaceId::Current)
|
||||
@@ -830,6 +860,13 @@ impl RenderState {
|
||||
self.nested_fills.pop();
|
||||
}
|
||||
|
||||
match element.shape_type {
|
||||
Type::Frame(_) | Type::Group(_) => {
|
||||
self.nested_blurs.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Detect clipping and apply it properly
|
||||
if let Type::Frame(_) = &element.shape_type {
|
||||
if element.clip() {
|
||||
|
||||
@@ -24,8 +24,15 @@ fn draw_image_fill(
|
||||
let src_rect = get_source_rect(size, container, image_fill);
|
||||
let dest_rect = container;
|
||||
|
||||
let mut image_paint = skia::Paint::default();
|
||||
image_paint.set_anti_alias(antialias);
|
||||
if let Some(filter) = shape.image_filter(1.) {
|
||||
image_paint.set_image_filter(filter.clone());
|
||||
}
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&image_paint);
|
||||
// Save the current canvas state
|
||||
canvas.save();
|
||||
canvas.save_layer(&layer_rec);
|
||||
|
||||
// Set the clipping rectangle to the container bounds
|
||||
match &shape.shape_type {
|
||||
@@ -84,26 +91,29 @@ fn draw_image_fill(
|
||||
* This SHOULD be the only public function in this module.
|
||||
*/
|
||||
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) {
|
||||
(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(_)) => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_rect_to(SurfaceId::Fills, shape, paint);
|
||||
.draw_rect_to(SurfaceId::Fills, shape, &paint);
|
||||
}
|
||||
(_, Type::Circle) => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_circle_to(SurfaceId::Fills, shape, paint);
|
||||
.draw_circle_to(SurfaceId::Fills, shape, &paint);
|
||||
}
|
||||
(_, Type::Path(_)) | (_, Type::Bool(_)) => {
|
||||
render_state
|
||||
.surfaces
|
||||
.draw_path_to(SurfaceId::Fills, shape, paint);
|
||||
.draw_path_to(SurfaceId::Fills, shape, &paint);
|
||||
}
|
||||
(_, Type::Group(_)) => {
|
||||
// Groups can have fills but they propagate them to their children
|
||||
|
||||
23
render-wasm/src/render/filters.rs
Normal file
23
render-wasm/src/render/filters.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use skia_safe::ImageFilter;
|
||||
|
||||
/// Composes two image filters, returning a combined filter if both are present,
|
||||
/// or the individual filter if only one is present, or None if neither is present.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `filter_a`: The first optional image filter.
|
||||
/// - `filter_b`: The second optional image filter.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Some(ImageFilter)`: The composed or single filter.
|
||||
/// - `None`: If both filters are `None`.
|
||||
pub fn compose_filters(
|
||||
filter_a: Option<&ImageFilter>,
|
||||
filter_b: Option<&ImageFilter>,
|
||||
) -> Option<ImageFilter> {
|
||||
match (filter_a, filter_b) {
|
||||
(Some(fa), Some(fb)) => ImageFilter::compose(fa, fb),
|
||||
(Some(fa), None) => Some(fa.clone()),
|
||||
(None, Some(fb)) => Some(fb.clone()),
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ fn render_fill_drop_shadow(
|
||||
shadow: &Shadow,
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = &shadow.get_drop_shadow_paint(antialias);
|
||||
let paint = &shadow.get_drop_shadow_paint(antialias, shape.image_filter(1.).as_ref());
|
||||
render_shadow_paint(render_state, shape, paint, SurfaceId::DropShadows);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ fn render_fill_inner_shadow(
|
||||
shadow: &Shadow,
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = &shadow.get_inner_shadow_paint(antialias);
|
||||
let paint = &shadow.get_inner_shadow_paint(antialias, shape.image_filter(1.).as_ref());
|
||||
render_shadow_paint(render_state, shape, paint, SurfaceId::InnerShadows);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ pub fn render_text_drop_shadow(
|
||||
paragraphs: &mut [Vec<ParagraphBuilder>],
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = shadow.get_drop_shadow_paint(antialias);
|
||||
let paint = shadow.get_drop_shadow_paint(antialias, shape.image_filter(1.).as_ref());
|
||||
|
||||
text::render(
|
||||
render_state,
|
||||
@@ -158,7 +158,7 @@ pub fn render_text_inner_shadow(
|
||||
paragraphs: &mut [Vec<ParagraphBuilder>],
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = shadow.get_inner_shadow_paint(antialias);
|
||||
let paint = shadow.get_inner_shadow_paint(antialias, shape.image_filter(1.).as_ref());
|
||||
|
||||
text::render(
|
||||
render_state,
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::shapes::{Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, St
|
||||
use skia_safe::{self as skia, textlayout::ParagraphBuilder, ImageFilter, RRect};
|
||||
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::render::filters::compose_filters;
|
||||
use crate::render::text::{self};
|
||||
use crate::render::{get_dest_rect, get_source_rect};
|
||||
|
||||
@@ -20,6 +21,7 @@ fn draw_stroke_on_rect(
|
||||
svg_attrs: &HashMap<String, String>,
|
||||
scale: f32,
|
||||
shadow: Option<&ImageFilter>,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
// Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to:
|
||||
@@ -29,9 +31,9 @@ fn draw_stroke_on_rect(
|
||||
let stroke_rect = stroke.outer_rect(rect);
|
||||
let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
|
||||
|
||||
if let Some(filter) = shadow {
|
||||
paint.set_image_filter(filter.clone());
|
||||
}
|
||||
// Apply both blur and shadow filters if present, composing them if necessary.
|
||||
let filter = compose_filters(blur, shadow);
|
||||
paint.set_image_filter(filter);
|
||||
|
||||
match corners {
|
||||
Some(radii) => {
|
||||
@@ -55,6 +57,7 @@ fn draw_stroke_on_circle(
|
||||
svg_attrs: &HashMap<String, String>,
|
||||
scale: f32,
|
||||
shadow: Option<&ImageFilter>,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
// Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to:
|
||||
@@ -64,9 +67,9 @@ fn draw_stroke_on_circle(
|
||||
let stroke_rect = stroke.outer_rect(rect);
|
||||
let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
|
||||
|
||||
if let Some(filter) = shadow {
|
||||
paint.set_image_filter(filter.clone());
|
||||
}
|
||||
// Apply both blur and shadow filters if present, composing them if necessary.
|
||||
let filter = compose_filters(blur, shadow);
|
||||
paint.set_image_filter(filter);
|
||||
|
||||
canvas.draw_oval(stroke_rect, &paint);
|
||||
}
|
||||
@@ -75,11 +78,17 @@ fn draw_outer_stroke_path(
|
||||
canvas: &skia::Canvas,
|
||||
path: &skia::Path,
|
||||
paint: &skia::Paint,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let mut outer_paint = skia::Paint::default();
|
||||
outer_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
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);
|
||||
canvas.save_layer(&layer_rec);
|
||||
canvas.draw_path(path, paint);
|
||||
@@ -97,9 +106,17 @@ fn draw_inner_stroke_path(
|
||||
canvas: &skia::Canvas,
|
||||
path: &skia::Path,
|
||||
paint: &skia::Paint,
|
||||
blur: Option<&ImageFilter>,
|
||||
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.draw_path(path, paint);
|
||||
canvas.restore();
|
||||
@@ -117,6 +134,7 @@ pub fn draw_stroke_on_path(
|
||||
svg_attrs: &HashMap<String, String>,
|
||||
scale: f32,
|
||||
shadow: Option<&ImageFilter>,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let mut skia_path = path.to_skia_path();
|
||||
@@ -127,19 +145,18 @@ pub fn draw_stroke_on_path(
|
||||
let mut paint: skia_safe::Handle<_> =
|
||||
stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
|
||||
|
||||
if let Some(filter) = shadow {
|
||||
paint.set_image_filter(filter.clone());
|
||||
}
|
||||
let filter = compose_filters(blur, shadow);
|
||||
paint.set_image_filter(filter);
|
||||
|
||||
match stroke.render_kind(is_open) {
|
||||
StrokeKind::Inner => {
|
||||
draw_inner_stroke_path(canvas, &skia_path, &paint, antialias);
|
||||
draw_inner_stroke_path(canvas, &skia_path, &paint, blur, antialias);
|
||||
}
|
||||
StrokeKind::Center => {
|
||||
canvas.draw_path(&skia_path, &paint);
|
||||
}
|
||||
StrokeKind::Outer => {
|
||||
draw_outer_stroke_path(canvas, &skia_path, &paint, antialias);
|
||||
draw_outer_stroke_path(canvas, &skia_path, &paint, blur, antialias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +168,7 @@ pub fn draw_stroke_on_path(
|
||||
is_open,
|
||||
svg_attrs,
|
||||
scale,
|
||||
blur,
|
||||
antialias,
|
||||
);
|
||||
}
|
||||
@@ -164,7 +182,6 @@ fn handle_stroke_cap(
|
||||
p2: &Point,
|
||||
) {
|
||||
paint.set_style(skia::PaintStyle::Fill);
|
||||
paint.set_blend_mode(skia::BlendMode::Src);
|
||||
match cap {
|
||||
StrokeCap::None => {}
|
||||
StrokeCap::Line => {
|
||||
@@ -204,6 +221,7 @@ fn handle_stroke_caps(
|
||||
is_open: bool,
|
||||
svg_attrs: &HashMap<String, String>,
|
||||
scale: f32,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let mut points = vec![Point::default(); path.count_points()];
|
||||
@@ -220,6 +238,10 @@ fn handle_stroke_caps(
|
||||
let mut paint_stroke =
|
||||
stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
|
||||
|
||||
if let Some(filter) = blur {
|
||||
paint_stroke.set_image_filter(filter.clone());
|
||||
}
|
||||
|
||||
handle_stroke_cap(
|
||||
canvas,
|
||||
stroke.cap_start,
|
||||
@@ -228,6 +250,7 @@ fn handle_stroke_caps(
|
||||
first_point,
|
||||
&points[1],
|
||||
);
|
||||
|
||||
handle_stroke_cap(
|
||||
canvas,
|
||||
stroke.cap_end,
|
||||
@@ -370,6 +393,10 @@ fn draw_image_stroke_in_container(
|
||||
let mut pb = skia::Paint::default();
|
||||
pb.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
pb.set_anti_alias(antialias);
|
||||
if let Some(filter) = shape.image_filter(1.) {
|
||||
pb.set_image_filter(filter);
|
||||
}
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&pb);
|
||||
canvas.save_layer(&layer_rec);
|
||||
|
||||
@@ -388,6 +415,7 @@ fn draw_image_stroke_in_container(
|
||||
svg_attrs,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
antialias,
|
||||
);
|
||||
}
|
||||
@@ -399,6 +427,7 @@ fn draw_image_stroke_in_container(
|
||||
svg_attrs,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
antialias,
|
||||
),
|
||||
|
||||
@@ -435,6 +464,7 @@ fn draw_image_stroke_in_container(
|
||||
is_open,
|
||||
svg_attrs,
|
||||
scale,
|
||||
shape.image_filter(1.).as_ref(),
|
||||
antialias,
|
||||
);
|
||||
canvas.restore();
|
||||
@@ -450,6 +480,9 @@ fn draw_image_stroke_in_container(
|
||||
let mut image_paint = skia::Paint::default();
|
||||
image_paint.set_blend_mode(skia::BlendMode::SrcIn);
|
||||
image_paint.set_anti_alias(antialias);
|
||||
if let Some(filter) = shape.image_filter(1.) {
|
||||
image_paint.set_image_filter(filter);
|
||||
}
|
||||
|
||||
let src_rect = get_source_rect(size, container, image_fill);
|
||||
let dest_rect = get_dest_rect(container, stroke.delta());
|
||||
@@ -517,11 +550,20 @@ pub fn render(
|
||||
svg_attrs,
|
||||
scale,
|
||||
shadow,
|
||||
shape.image_filter(1.).as_ref(),
|
||||
antialias,
|
||||
);
|
||||
}
|
||||
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,
|
||||
),
|
||||
Type::Text(_) => {
|
||||
text::render(
|
||||
@@ -543,6 +585,7 @@ pub fn render(
|
||||
svg_attrs,
|
||||
scale,
|
||||
shadow,
|
||||
shape.image_filter(1.).as_ref(),
|
||||
antialias,
|
||||
);
|
||||
}
|
||||
@@ -579,7 +622,7 @@ pub fn render_text_paths(
|
||||
match stroke.render_kind(false) {
|
||||
StrokeKind::Inner => {
|
||||
for (path, _) in paths {
|
||||
draw_inner_stroke_path(canvas, path, &paint, antialias);
|
||||
draw_inner_stroke_path(canvas, path, &paint, None, antialias);
|
||||
}
|
||||
}
|
||||
StrokeKind::Center => {
|
||||
@@ -589,7 +632,7 @@ pub fn render_text_paths(
|
||||
}
|
||||
StrokeKind::Outer => {
|
||||
for (path, _) in paths {
|
||||
draw_outer_stroke_path(canvas, path, &paint, antialias);
|
||||
draw_outer_stroke_path(canvas, path, &paint, None, antialias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,6 +923,21 @@ impl Shape {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mask_filter(&self, scale: f32) -> Option<skia::MaskFilter> {
|
||||
if !self.blur.hidden {
|
||||
match self.blur.blur_type {
|
||||
BlurType::None => None,
|
||||
BlurType::Layer => skia::MaskFilter::blur(
|
||||
skia::BlurStyle::Normal,
|
||||
self.blur.value * scale,
|
||||
Some(true),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_recursive(&self) -> bool {
|
||||
matches!(
|
||||
self.shape_type,
|
||||
|
||||
@@ -198,7 +198,7 @@ fn propagate_transform(
|
||||
|
||||
if let Type::Text(content) = &shape.shape_type {
|
||||
if content.grow_type() == GrowType::AutoHeight {
|
||||
let mut paragraphs = content.get_skia_paragraphs();
|
||||
let mut paragraphs = content.get_skia_paragraphs(None, None);
|
||||
set_paragraphs_width(shape_bounds_after.width(), &mut paragraphs);
|
||||
let height = auto_height(&mut paragraphs, shape_bounds_after.width());
|
||||
let resize_transform = math::resize_matrix(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use skia_safe::{self as skia, image_filters, ImageFilter, Paint};
|
||||
|
||||
use super::Color;
|
||||
use crate::render::filters::compose_filters;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ShadowStyle {
|
||||
@@ -62,13 +63,16 @@ impl Shadow {
|
||||
self.hidden
|
||||
}
|
||||
|
||||
pub fn get_drop_shadow_paint(&self, antialias: bool) -> Paint {
|
||||
pub fn get_drop_shadow_paint(
|
||||
&self,
|
||||
antialias: bool,
|
||||
blur_filter: Option<&ImageFilter>,
|
||||
) -> Paint {
|
||||
let mut paint = Paint::default();
|
||||
let image_filter = self.get_drop_shadow_filter();
|
||||
|
||||
paint.set_image_filter(image_filter);
|
||||
let shadow_filter = self.get_drop_shadow_filter();
|
||||
let filter = compose_filters(blur_filter, shadow_filter.as_ref());
|
||||
paint.set_image_filter(filter);
|
||||
paint.set_anti_alias(antialias);
|
||||
|
||||
paint
|
||||
}
|
||||
|
||||
@@ -89,14 +93,16 @@ impl Shadow {
|
||||
filter
|
||||
}
|
||||
|
||||
pub fn get_inner_shadow_paint(&self, antialias: bool) -> Paint {
|
||||
pub fn get_inner_shadow_paint(
|
||||
&self,
|
||||
antialias: bool,
|
||||
blur_filter: Option<&ImageFilter>,
|
||||
) -> Paint {
|
||||
let mut paint = Paint::default();
|
||||
|
||||
let image_filter = self.get_inner_shadow_filter();
|
||||
|
||||
paint.set_image_filter(image_filter);
|
||||
let shadow_filter = self.get_inner_shadow_filter();
|
||||
let filter = compose_filters(blur_filter, shadow_filter.as_ref());
|
||||
paint.set_image_filter(filter);
|
||||
paint.set_anti_alias(antialias);
|
||||
|
||||
paint
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::{
|
||||
math::{Matrix, Rect},
|
||||
render::{default_font, DEFAULT_EMOJI_FONT},
|
||||
render::{default_font, filters::compose_filters, DEFAULT_EMOJI_FONT},
|
||||
};
|
||||
use skia_safe::{
|
||||
self as skia,
|
||||
paint::Paint,
|
||||
textlayout::{ParagraphBuilder, ParagraphStyle},
|
||||
ImageFilter, MaskFilter,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -92,7 +93,11 @@ impl TextContent {
|
||||
self.paragraphs.push(paragraph);
|
||||
}
|
||||
|
||||
pub fn to_paragraphs(&self) -> Vec<Vec<ParagraphBuilder>> {
|
||||
pub fn to_paragraphs(
|
||||
&self,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
let fonts = get_font_collection();
|
||||
let fallback_fonts = get_fallback_fonts();
|
||||
let mut paragraph_group = Vec::new();
|
||||
@@ -101,7 +106,8 @@ impl TextContent {
|
||||
let paragraph_style = paragraph.paragraph_to_style();
|
||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||
for leaf in ¶graph.children {
|
||||
let text_style = leaf.to_style(paragraph, &self.bounds, fallback_fonts);
|
||||
let text_style =
|
||||
leaf.to_style(paragraph, &self.bounds, fallback_fonts, blur, blur_mask);
|
||||
let text = leaf.apply_text_transform();
|
||||
builder.push_style(&text_style);
|
||||
builder.add_text(&text);
|
||||
@@ -116,6 +122,8 @@ impl TextContent {
|
||||
&self,
|
||||
stroke: &Stroke,
|
||||
bounds: &Rect,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
let fallback_fonts = get_fallback_fonts();
|
||||
let fonts = get_font_collection();
|
||||
@@ -126,8 +134,12 @@ impl TextContent {
|
||||
std::collections::HashMap::new();
|
||||
|
||||
for leaf in paragraph.children.iter() {
|
||||
let text_paint = merge_fills(&leaf.fills, *bounds);
|
||||
let stroke_paints = get_text_stroke_paints(stroke, bounds, &text_paint);
|
||||
let mut text_paint = merge_fills(&leaf.fills, *bounds);
|
||||
if let Some(blur_mask) = blur_mask {
|
||||
text_paint.set_mask_filter(blur_mask.clone());
|
||||
}
|
||||
let stroke_paints =
|
||||
get_text_stroke_paints(stroke, bounds, &text_paint, blur, blur_mask);
|
||||
let text: String = leaf.apply_text_transform();
|
||||
|
||||
for (paint_idx, stroke_paint) in stroke_paints.iter().enumerate() {
|
||||
@@ -135,9 +147,14 @@ impl TextContent {
|
||||
let paragraph_style = paragraph.paragraph_to_style();
|
||||
ParagraphBuilder::new(¶graph_style, fonts)
|
||||
});
|
||||
|
||||
let stroke_style =
|
||||
leaf.to_stroke_style(paragraph, stroke_paint, fallback_fonts);
|
||||
let stroke_paint = stroke_paint.clone();
|
||||
let stroke_style = leaf.to_stroke_style(
|
||||
paragraph,
|
||||
&stroke_paint,
|
||||
fallback_fonts,
|
||||
blur,
|
||||
blur_mask,
|
||||
);
|
||||
builder.push_style(&stroke_style);
|
||||
builder.add_text(&text);
|
||||
}
|
||||
@@ -167,16 +184,22 @@ impl TextContent {
|
||||
paragraphs
|
||||
}
|
||||
|
||||
pub fn get_skia_paragraphs(&self) -> Vec<Vec<ParagraphBuilder>> {
|
||||
self.collect_paragraphs(self.to_paragraphs())
|
||||
pub fn get_skia_paragraphs(
|
||||
&self,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
self.collect_paragraphs(self.to_paragraphs(blur, blur_mask))
|
||||
}
|
||||
|
||||
pub fn get_skia_stroke_paragraphs(
|
||||
&self,
|
||||
stroke: &Stroke,
|
||||
bounds: &Rect,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> Vec<Vec<ParagraphBuilder>> {
|
||||
self.collect_paragraphs(self.to_stroke_paragraphs(stroke, bounds))
|
||||
self.collect_paragraphs(self.to_stroke_paragraphs(stroke, bounds, blur, blur_mask))
|
||||
}
|
||||
|
||||
pub fn grow_type(&self) -> GrowType {
|
||||
@@ -188,7 +211,7 @@ impl TextContent {
|
||||
}
|
||||
|
||||
pub fn visual_bounds(&self) -> (f32, f32) {
|
||||
let mut paragraphs = self.to_paragraphs();
|
||||
let mut paragraphs = self.to_paragraphs(None, None);
|
||||
let height = auto_height(&mut paragraphs, self.width());
|
||||
(self.width(), height)
|
||||
}
|
||||
@@ -378,10 +401,16 @@ impl TextLeaf {
|
||||
paragraph: &Paragraph,
|
||||
content_bounds: &Rect,
|
||||
fallback_fonts: &HashSet<String>,
|
||||
_blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> skia::textlayout::TextStyle {
|
||||
let mut style = skia::textlayout::TextStyle::default();
|
||||
let mut paint = merge_fills(&self.fills, *content_bounds);
|
||||
|
||||
if let Some(blur_mask) = blur_mask {
|
||||
paint.set_mask_filter(blur_mask.clone());
|
||||
}
|
||||
|
||||
let paint = merge_fills(&self.fills, *content_bounds);
|
||||
style.set_foreground_paint(&paint);
|
||||
style.set_font_size(self.font_size);
|
||||
style.set_letter_spacing(paragraph.letter_spacing);
|
||||
@@ -417,8 +446,10 @@ impl TextLeaf {
|
||||
paragraph: &Paragraph,
|
||||
stroke_paint: &Paint,
|
||||
fallback_fonts: &HashSet<String>,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> skia::textlayout::TextStyle {
|
||||
let mut style = self.to_style(paragraph, &Rect::default(), fallback_fonts);
|
||||
let mut style = self.to_style(paragraph, &Rect::default(), fallback_fonts, blur, blur_mask);
|
||||
style.set_foreground_paint(stroke_paint);
|
||||
style.set_font_size(self.font_size);
|
||||
style.set_letter_spacing(paragraph.letter_spacing);
|
||||
@@ -714,7 +745,13 @@ pub fn auto_height(paragraphs: &mut [Vec<ParagraphBuilder>], width: f32) -> f32
|
||||
})
|
||||
}
|
||||
|
||||
fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) -> Vec<Paint> {
|
||||
fn get_text_stroke_paints(
|
||||
stroke: &Stroke,
|
||||
bounds: &Rect,
|
||||
text_paint: &Paint,
|
||||
blur: Option<&ImageFilter>,
|
||||
blur_mask: Option<&MaskFilter>,
|
||||
) -> Vec<Paint> {
|
||||
let mut paints = Vec::new();
|
||||
|
||||
match stroke.kind {
|
||||
@@ -730,6 +767,9 @@ fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) ->
|
||||
let mut paint = text_paint.clone();
|
||||
paint.set_style(skia::PaintStyle::Fill);
|
||||
paint.set_anti_alias(true);
|
||||
if let Some(blur) = blur {
|
||||
paint.set_image_filter(blur.clone());
|
||||
}
|
||||
paints.push(paint);
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
@@ -738,6 +778,9 @@ fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) ->
|
||||
paint.set_anti_alias(true);
|
||||
paint.set_stroke_width(stroke.width * 2.0);
|
||||
set_paint_fill(&mut paint, &stroke.fill, bounds);
|
||||
if let Some(blur) = blur {
|
||||
paint.set_image_filter(blur.clone());
|
||||
}
|
||||
paints.push(paint);
|
||||
} else {
|
||||
let mut paint = text_paint.clone();
|
||||
@@ -749,7 +792,9 @@ fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) ->
|
||||
let mut paint = skia::Paint::default();
|
||||
let image_filter =
|
||||
skia_safe::image_filters::erode((stroke.width, stroke.width), None, None);
|
||||
paint.set_image_filter(image_filter);
|
||||
|
||||
let filter = compose_filters(blur, image_filter.as_ref());
|
||||
paint.set_image_filter(filter);
|
||||
paint.set_anti_alias(false);
|
||||
paint.set_blend_mode(skia::BlendMode::DstOut);
|
||||
paints.push(paint);
|
||||
@@ -762,6 +807,9 @@ fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) ->
|
||||
paint.set_stroke_width(stroke.width);
|
||||
|
||||
set_paint_fill(&mut paint, &stroke.fill, bounds);
|
||||
if let Some(blur) = blur {
|
||||
paint.set_image_filter(blur.clone());
|
||||
}
|
||||
|
||||
paints.push(paint);
|
||||
}
|
||||
@@ -771,14 +819,20 @@ fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) ->
|
||||
paint.set_blend_mode(skia::BlendMode::DstOver);
|
||||
paint.set_anti_alias(true);
|
||||
paint.set_stroke_width(stroke.width * 2.0);
|
||||
|
||||
set_paint_fill(&mut paint, &stroke.fill, bounds);
|
||||
|
||||
if let Some(blur_mask) = blur_mask {
|
||||
paint.set_mask_filter(blur_mask.clone());
|
||||
}
|
||||
paints.push(paint);
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_style(skia::PaintStyle::Fill);
|
||||
paint.set_blend_mode(skia::BlendMode::Clear);
|
||||
paint.set_color(skia::Color::TRANSPARENT);
|
||||
paint.set_anti_alias(true);
|
||||
if let Some(blur_mask) = blur_mask {
|
||||
paint.set_mask_filter(blur_mask.clone());
|
||||
}
|
||||
paints.push(paint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ impl TextPaths {
|
||||
}
|
||||
|
||||
pub fn get_skia_paragraphs(&self) -> Vec<Vec<ParagraphBuilder>> {
|
||||
let paragraphs = self.to_paragraphs();
|
||||
let paragraphs = self.to_paragraphs(None, None);
|
||||
self.collect_paragraphs(paragraphs)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
||||
height = shape.selrect.height();
|
||||
|
||||
if let Type::Text(content) = &shape.shape_type {
|
||||
let mut paragraphs = content.get_skia_paragraphs();
|
||||
let mut paragraphs = content.get_skia_paragraphs(
|
||||
shape.image_filter(1.).as_ref(),
|
||||
shape.mask_filter(1.).as_ref(),
|
||||
);
|
||||
m_width = max_width(&mut paragraphs, width);
|
||||
|
||||
match content.grow_type() {
|
||||
|
||||
Reference in New Issue
Block a user