From eddabc0d68325e9cb27a7f6dfeff9115e5150767 Mon Sep 17 00:00:00 2001 From: Elena Torro Date: Thu, 8 May 2025 13:28:04 +0200 Subject: [PATCH] :tada: Add text stroke fills --- render-wasm/src/render.rs | 10 +++++-- render-wasm/src/shapes.rs | 51 ++++++++++++++++++++++++++------- render-wasm/src/shapes/fills.rs | 8 ++++-- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index aaf825d35..672751a67 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1,4 +1,4 @@ -use skia_safe::{self as skia, Matrix, RRect, Rect}; +use skia_safe::{self as skia, image, Matrix, RRect, Rect}; use crate::uuid::Uuid; use std::collections::{HashMap, HashSet, VecDeque}; @@ -20,7 +20,7 @@ mod surfaces; mod text; mod tiles; -use crate::shapes::{modified_children_ids, Corners, Shape, StructureEntry, Type}; +use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type}; use gpu_state::GpuState; use options::RenderOptions; use surfaces::{SurfaceId, Surfaces}; @@ -362,7 +362,11 @@ impl RenderState { text::render(self, &shape, ¶graphs, None, None); for stroke in shape.strokes().rev() { - let stroke_paints = shape.get_text_stroke_paint(&stroke); + let mut image: Option = None; + if let Fill::Image(image_fill) = &stroke.fill { + image = self.images.get(&image_fill.id()).cloned(); + } + let stroke_paints = shape.get_text_stroke_paint(&stroke, image.as_ref()); let stroke_paragraphs = text_content .get_skia_stroke_paragraphs(&self.fonts.font_collection(), &stroke_paints); shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias); diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index afbd3177c..8438d2503 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -1,3 +1,4 @@ +use skia_safe::Image; use skia_safe::{self as skia, paint::Paint}; use crate::render::BlendMode; @@ -812,7 +813,37 @@ impl Shape { !self.fills.is_empty() } - pub fn get_text_stroke_paint(&self, stroke: &Stroke) -> Vec { + fn set_paint_fill(&self, paint: &mut Paint, fill: &Fill, image: Option) { + match fill { + Fill::Solid(SolidColor(color)) => { + paint.set_color(*color); + } + Fill::LinearGradient(gradient) => { + paint.set_shader(gradient.to_linear_shader(&self.selrect())); + } + Fill::RadialGradient(gradient) => { + paint.set_shader(gradient.to_radial_shader(&self.selrect())); + } + Fill::Image(image_fill) => { + if let Some(image) = image { + let position = (self.selrect().x(), self.selrect().y()); + let sampling_options = skia::SamplingOptions::new( + skia::FilterMode::Linear, + skia::MipmapMode::Nearest, + ); + let tile_modes = (skia::TileMode::Clamp, skia::TileMode::Clamp); + let mut matrix = skia::Matrix::default(); + matrix.set_translate(position); + + let shader = image.to_shader(tile_modes, sampling_options, &matrix); + paint.set_shader(shader); + paint.set_alpha(image_fill.opacity()); + } + } + } + } + + pub fn get_text_stroke_paint(&self, stroke: &Stroke, image: Option<&Image>) -> Vec { let mut paints = Vec::new(); match stroke.kind { @@ -827,9 +858,9 @@ impl Shape { paint.set_blend_mode(skia::BlendMode::SrcATop); paint.set_anti_alias(true); paint.set_stroke_width(stroke.width * 2.0); - if let Fill::Solid(SolidColor(color)) = stroke.fill { - paint.set_color(color); - } + + self.set_paint_fill(&mut paint, &stroke.fill, image.cloned()); + paints.push(paint); } StrokeKind::CenterStroke => { @@ -837,9 +868,9 @@ impl Shape { paint.set_style(skia::PaintStyle::Stroke); paint.set_anti_alias(true); paint.set_stroke_width(stroke.width); - if let Fill::Solid(SolidColor(color)) = stroke.fill { - paint.set_color(color); - } + + self.set_paint_fill(&mut paint, &stroke.fill, image.cloned()); + paints.push(paint); } StrokeKind::OuterStroke => { @@ -848,9 +879,9 @@ impl Shape { paint.set_blend_mode(skia::BlendMode::DstOver); paint.set_anti_alias(true); paint.set_stroke_width(stroke.width * 2.0); - if let Fill::Solid(SolidColor(color)) = stroke.fill { - paint.set_color(color); - } + + self.set_paint_fill(&mut paint, &stroke.fill, image.cloned()); + paints.push(paint); let mut paint = skia::Paint::default(); diff --git a/render-wasm/src/shapes/fills.rs b/render-wasm/src/shapes/fills.rs index 0263bdd94..27125f96f 100644 --- a/render-wasm/src/shapes/fills.rs +++ b/render-wasm/src/shapes/fills.rs @@ -41,7 +41,7 @@ impl Gradient { self.offsets.extend(offsets); } - fn to_linear_shader(&self, rect: &Rect) -> Option { + pub fn to_linear_shader(&self, rect: &Rect) -> Option { let start = ( rect.left + self.start.0 * rect.width(), rect.top + self.start.1 * rect.height(), @@ -60,7 +60,7 @@ impl Gradient { ) } - fn to_radial_shader(&self, rect: &Rect) -> Option { + pub fn to_radial_shader(&self, rect: &Rect) -> Option { let center = skia::Point::new( rect.left + self.start.0 * rect.width(), rect.top + self.start.1 * rect.height(), @@ -119,6 +119,10 @@ impl ImageFill { pub fn id(&self) -> Uuid { self.id } + + pub fn opacity(&self) -> u8 { + self.opacity + } } #[derive(Debug, Clone, PartialEq, Copy)]