🐛 Fix different fonts on texts shadows (#7214)

* 🐛 Fix different fonts on texts shadows

* 🔧 Refactor text rendering and move text-decoration logic outside

* 🔧 Use transparency correctly
This commit is contained in:
Elena Torró
2025-09-02 12:56:07 +02:00
committed by GitHub
parent 796aaed11e
commit 5f37601122
9 changed files with 421 additions and 253 deletions

View File

@@ -537,32 +537,58 @@ impl RenderState {
});
let text_content = text_content.new_bounds(shape.selrect());
let mut paragraphs = text_content.to_paragraphs(
shape.image_filter(1.).as_ref(),
shape.mask_filter(1.).as_ref(),
);
let drop_shadows = shape.drop_shadow_paints();
let inner_shadows = shape.inner_shadow_paints();
let blur_filter = shape.image_filter(1.);
let blur_mask = shape.mask_filter(1.);
let mut paragraphs =
text_content.to_paragraphs(blur_filter.as_ref(), blur_mask.as_ref(), None);
if !shape.has_visible_strokes() {
shadows::render_text_drop_shadows(self, &shape, &mut paragraphs, antialias);
// Render all drop shadows if there are no visible strokes
if !shape.has_visible_strokes() && !drop_shadows.is_empty() {
for drop_shadow in &drop_shadows {
let mut paragraphs_with_drop_shadows = text_content.to_paragraphs(
blur_filter.as_ref(),
blur_mask.as_ref(),
Some(drop_shadow),
);
shadows::render_text_drop_shadows(
self,
&shape,
&mut paragraphs_with_drop_shadows,
);
}
}
let count_inner_strokes = shape.count_visible_inner_strokes();
text::render(self, &shape, &mut paragraphs, None, None);
text::render(self, &shape, &mut paragraphs, None);
for stroke in shape.visible_strokes().rev() {
let mut stroke_paragraphs = text_content.to_stroke_paragraphs(
for drop_shadow in &drop_shadows {
let mut stroke_paragraphs_with_drop_shadows = text_content
.to_stroke_paragraphs(
stroke,
&shape.selrect(),
shape.image_filter(1.).as_ref(),
shape.mask_filter(1.).as_ref(),
blur_filter.as_ref(),
blur_mask.as_ref(),
Some(drop_shadow),
count_inner_strokes,
);
shadows::render_text_drop_shadows(
self,
&shape,
&mut stroke_paragraphs,
antialias,
&mut stroke_paragraphs_with_drop_shadows,
);
}
let mut stroke_paragraphs = text_content.to_stroke_paragraphs(
stroke,
&shape.selrect(),
blur_filter.as_ref(),
blur_mask.as_ref(),
None,
count_inner_strokes,
);
strokes::render(
self,
&shape,
@@ -571,17 +597,38 @@ impl RenderState {
None,
Some(&mut stroke_paragraphs),
antialias,
None,
);
for inner_shadow in &inner_shadows {
let mut stroke_paragraphs_with_inner_shadows = text_content
.to_stroke_paragraphs(
stroke,
&shape.selrect(),
blur_filter.as_ref(),
blur_mask.as_ref(),
Some(inner_shadow),
count_inner_strokes,
);
shadows::render_text_inner_shadows(
self,
&shape,
&mut stroke_paragraphs,
antialias,
&mut stroke_paragraphs_with_inner_shadows,
);
}
}
shadows::render_text_inner_shadows(self, &shape, &mut paragraphs, antialias);
for inner_shadow in &inner_shadows {
let mut paragraphs_with_inner_shadows = text_content.to_paragraphs(
blur_filter.as_ref(),
blur_mask.as_ref(),
Some(inner_shadow),
);
shadows::render_text_inner_shadows(
self,
&shape,
&mut paragraphs_with_inner_shadows,
);
}
}
_ => {
let surface_ids = SurfaceId::Strokes as u32
@@ -630,7 +677,7 @@ impl RenderState {
shadows::render_stroke_drop_shadows(self, shape, stroke, antialias);
//In clipped content strokes are drawn over the contained elements in a subsequent step
if !shape.clip() {
strokes::render(self, shape, stroke, None, None, None, antialias, None);
strokes::render(self, shape, stroke, None, None, None, antialias);
}
shadows::render_stroke_inner_shadows(self, shape, stroke, antialias);
}

View File

@@ -59,7 +59,6 @@ pub fn render_stroke_drop_shadows(
filter.as_ref(),
None,
antialias,
None,
)
}
}
@@ -82,7 +81,6 @@ pub fn render_stroke_inner_shadows(
filter.as_ref(),
None,
antialias,
None,
)
}
}
@@ -92,11 +90,13 @@ pub fn render_text_drop_shadows(
render_state: &mut RenderState,
shape: &Shape,
paragraphs: &mut [Vec<ParagraphBuilder>],
antialias: bool,
) {
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
render_text_drop_shadow(render_state, shape, shadow, paragraphs, antialias);
}
text::render(
render_state,
shape,
paragraphs,
Some(SurfaceId::DropShadows),
);
}
// Render text paths (unused)
@@ -122,50 +122,16 @@ pub fn render_text_path_stroke_drop_shadows(
}
}
pub fn render_text_drop_shadow(
render_state: &mut RenderState,
shape: &Shape,
shadow: &Shadow,
paragraphs: &mut [Vec<ParagraphBuilder>],
antialias: bool,
) {
let paint = shadow.get_drop_shadow_paint(antialias, shape.image_filter(1.).as_ref());
text::render(
render_state,
shape,
paragraphs,
Some(SurfaceId::DropShadows),
Some(&paint),
);
}
pub fn render_text_inner_shadows(
render_state: &mut RenderState,
shape: &Shape,
paragraphs: &mut [Vec<ParagraphBuilder>],
antialias: bool,
) {
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
render_text_inner_shadow(render_state, shape, shadow, paragraphs, antialias);
}
}
pub fn render_text_inner_shadow(
render_state: &mut RenderState,
shape: &Shape,
shadow: &Shadow,
paragraphs: &mut [Vec<ParagraphBuilder>],
antialias: bool,
) {
let paint = shadow.get_inner_shadow_paint(antialias, shape.image_filter(1.).as_ref());
text::render(
render_state,
shape,
paragraphs,
Some(SurfaceId::InnerShadows),
Some(&paint),
);
}

View File

@@ -521,7 +521,6 @@ pub fn render(
shadow: Option<&ImageFilter>,
paragraphs: Option<&mut Vec<Vec<ParagraphBuilder>>>,
antialias: bool,
paint: Option<&skia::Paint>,
) {
let scale = render_state.get_scale();
let canvas = render_state
@@ -571,7 +570,6 @@ pub fn render(
shape,
paragraphs.expect("Text shapes should have paragraphs"),
Some(SurfaceId::Strokes),
paint,
);
}
shape_type @ (Type::Path(_) | Type::Bool(_)) => {

View File

@@ -1,16 +1,18 @@
use super::{RenderState, Shape, SurfaceId};
use crate::shapes::VerticalAlign;
use crate::utils::get_font_collection;
use skia_safe::{textlayout::ParagraphBuilder, Paint, Path};
use skia_safe::{
canvas::SaveLayerRec, textlayout::LineMetrics, textlayout::Paragraph,
textlayout::ParagraphBuilder, textlayout::RectHeightStyle, textlayout::RectWidthStyle,
textlayout::StyleMetrics, textlayout::TextDecoration, textlayout::TextStyle, Canvas, Paint,
Path,
};
pub fn render(
render_state: &mut RenderState,
shape: &Shape,
paragraphs: &mut [Vec<ParagraphBuilder>],
surface_id: Option<SurfaceId>,
paint: Option<&Paint>,
) {
let fonts = get_font_collection();
let canvas = render_state
.surfaces
.canvas(surface_id.unwrap_or(SurfaceId::Fills));
@@ -31,52 +33,65 @@ pub fn render(
_ => 0.0,
};
let layer_rec = skia_safe::canvas::SaveLayerRec::default();
let layer_rec = SaveLayerRec::default();
canvas.save_layer(&layer_rec);
for group in paragraphs {
let mut group_offset_y = global_offset_y;
let group_len = group.len();
for (index, builder) in group.iter_mut().enumerate() {
for builder in group.iter_mut() {
let mut skia_paragraph = builder.build();
if paint.is_some() && index == 0 {
let text = builder.get_text().to_string();
let mut paragraph_builder =
ParagraphBuilder::new(&builder.get_paragraph_style(), fonts);
let mut text_style: skia_safe::Handle<_> = builder.peek_style();
text_style.set_foreground_paint(paint.unwrap());
paragraph_builder.reset();
paragraph_builder.push_style(&text_style);
paragraph_builder.add_text(&text);
skia_paragraph = paragraph_builder.build();
} else if paint.is_some() && index > 0 {
continue;
}
skia_paragraph.layout(paragraph_width);
let paragraph_height = skia_paragraph.height();
let xy = (shape.selrect().x(), shape.selrect().y() + group_offset_y);
skia_paragraph.paint(canvas, xy);
for line_metrics in skia_paragraph.get_line_metrics().iter() {
let style_metrics: Vec<_> = line_metrics
.get_style_metrics(line_metrics.start_index..line_metrics.end_index)
.into_iter()
.collect();
let mut current_x_offset = 0.0;
let total_chars = line_metrics.end_index - line_metrics.start_index;
let line_start_offset = line_metrics.left as f32;
if total_chars == 0 || style_metrics.is_empty() {
continue;
render_text_decoration(canvas, &skia_paragraph, builder, line_metrics, xy);
}
let line_baseline = xy.1 + line_metrics.baseline as f32;
let full_text = builder.get_text();
if group_len == 1 {
group_offset_y += paragraph_height;
}
}
// 1. Caculate text decoration for line
if group_len > 1 {
let mut first_paragraph = group[0].build();
first_paragraph.layout(paragraph_width);
global_offset_y += first_paragraph.height();
} else {
global_offset_y = group_offset_y;
}
}
canvas.restore();
}
fn draw_text_decorations(
canvas: &Canvas,
text_style: &TextStyle,
y: Option<f32>,
thickness: f32,
text_left: f32,
text_width: f32,
) {
if let Some(y) = y {
let r = skia_safe::Rect::new(
text_left,
y - thickness / 2.0,
text_left + text_width,
y + thickness / 2.0,
);
let mut decoration_paint = text_style.foreground();
decoration_paint.set_anti_alias(true);
canvas.draw_rect(r, &decoration_paint);
}
}
fn calculate_decoration_metrics(
style_metrics: &Vec<(usize, &StyleMetrics)>,
line_baseline: f32,
) -> (f32, Option<f32>, f32, Option<f32>) {
let mut max_underline_thickness: f32 = 0.0;
let mut underline_y = None;
let mut max_strike_thickness: f32 = 0.0;
@@ -92,17 +107,12 @@ pub fn render(
.underline_thickness()
.unwrap_or(1.0)
.max(min_thickness);
if style_metric.text_style.decoration().ty
== skia_safe::textlayout::TextDecoration::UNDERLINE
{
let y =
line_baseline + font_metrics.underline_position().unwrap_or(thickness);
if style_metric.text_style.decoration().ty == TextDecoration::UNDERLINE {
let y = line_baseline + font_metrics.underline_position().unwrap_or(thickness);
max_underline_thickness = max_underline_thickness.max(thickness);
underline_y = Some(y);
}
if style_metric.text_style.decoration().ty
== skia_safe::textlayout::TextDecoration::LINE_THROUGH
{
if style_metric.text_style.decoration().ty == TextDecoration::LINE_THROUGH {
let y = line_baseline
+ font_metrics
.strikeout_position()
@@ -111,10 +121,44 @@ pub fn render(
strike_y = Some(y);
}
}
(
max_underline_thickness,
underline_y,
max_strike_thickness,
strike_y,
)
}
// 2. Draw decorations per segment
fn render_text_decoration(
canvas: &Canvas,
skia_paragraph: &Paragraph,
builder: &mut ParagraphBuilder,
line_metrics: &LineMetrics,
xy: (f32, f32),
) {
let style_metrics: Vec<_> = line_metrics
.get_style_metrics(line_metrics.start_index..line_metrics.end_index)
.into_iter()
.collect();
let mut current_x_offset = 0.0;
let total_chars = line_metrics.end_index - line_metrics.start_index;
let line_start_offset = line_metrics.left as f32;
if total_chars == 0 || style_metrics.is_empty() {
return;
}
let line_baseline = xy.1 + line_metrics.baseline as f32;
let full_text = builder.get_text();
// Calculate decoration metrics
let (max_underline_thickness, underline_y, max_strike_thickness, strike_y) =
calculate_decoration_metrics(&style_metrics, line_baseline);
// Draw decorations per segment (text leaf)
for (i, (style_start, style_metric)) in style_metrics.iter().enumerate() {
let text_style = style_metric.text_style;
let text_style = &style_metric.text_style;
let style_end = style_metrics
.get(i + 1)
.map(|(next_i, _)| *next_i)
@@ -140,8 +184,8 @@ pub fn render(
let rects = skia_paragraph.get_rects_for_range(
seg_start..seg_end,
skia_safe::textlayout::RectHeightStyle::Tight,
skia_safe::textlayout::RectWidthStyle::Tight,
RectHeightStyle::Tight,
RectWidthStyle::Tight,
);
let (segment_width, actual_x_offset) = if !rects.is_empty() {
let total_width: f32 = rects.iter().map(|r| r.rect.width()).sum();
@@ -156,69 +200,33 @@ pub fn render(
(measured_width, current_x_offset)
};
// Underline
if text_style.decoration().ty
== skia_safe::textlayout::TextDecoration::UNDERLINE
{
if let Some(y) = underline_y {
let thickness = max_underline_thickness;
let text_left = xy.0 + line_start_offset + actual_x_offset;
let text_width = segment_width;
let r = skia_safe::Rect::new(
// Underline
if text_style.decoration().ty == TextDecoration::UNDERLINE {
draw_text_decorations(
canvas,
text_style,
underline_y,
max_underline_thickness,
text_left,
y - thickness / 2.0,
text_left + text_width,
y + thickness / 2.0,
text_width,
);
let mut decoration_paint = text_style.foreground();
decoration_paint.set_anti_alias(true);
canvas.draw_rect(r, &decoration_paint);
}
}
// Strikethrough
if text_style.decoration().ty
== skia_safe::textlayout::TextDecoration::LINE_THROUGH
{
if let Some(y) = strike_y {
let thickness = max_strike_thickness;
let text_left = xy.0 + line_start_offset + actual_x_offset;
let text_width = segment_width;
let r = skia_safe::Rect::new(
if text_style.decoration().ty == TextDecoration::LINE_THROUGH {
draw_text_decorations(
canvas,
text_style,
strike_y,
max_strike_thickness,
text_left,
y - thickness / 2.0,
text_left + text_width,
y + thickness / 2.0,
text_width,
);
let mut decoration_paint = text_style.foreground();
decoration_paint.set_anti_alias(true);
canvas.draw_rect(r, &decoration_paint);
}
}
current_x_offset += segment_width;
}
}
// Only increment group_offset_y for regular paragraphs (single element groups)
// For stroke groups (multiple elements), keep same offset for blending
if group_len == 1 {
group_offset_y += paragraph_height;
}
// For stroke groups (group_len > 1), don't increment group_offset_y within the group
// This ensures all stroke variants render at the same position for proper blending
}
// For stroke groups (multiple elements), increment global_offset_y once per group
if group_len > 1 {
let mut first_paragraph = group[0].build();
first_paragraph.layout(paragraph_width);
global_offset_y += first_paragraph.height();
} else {
// For regular paragraphs, global_offset_y was already incremented inside the loop
global_offset_y = group_offset_y;
}
}
canvas.restore();
}
fn calculate_total_paragraphs_height(paragraphs: &mut [ParagraphBuilder], width: f32) -> f32 {

View File

@@ -1152,6 +1152,34 @@ impl Shape {
self.children_ids(include_hidden)
}
}
pub fn drop_shadow_paints(&self) -> Vec<skia_safe::Paint> {
let drop_shadows: Vec<&crate::shapes::shadows::Shadow> =
self.drop_shadows().filter(|s| !s.hidden()).collect();
drop_shadows
.into_iter()
.map(|shadow| {
let mut paint = skia_safe::Paint::default();
let filter = shadow.get_drop_shadow_filter();
paint.set_image_filter(filter);
paint
})
.collect()
}
pub fn inner_shadow_paints(&self) -> Vec<skia_safe::Paint> {
let inner_shadows: Vec<&crate::shapes::shadows::Shadow> =
self.inner_shadows().filter(|s| !s.hidden()).collect();
inner_shadows
.into_iter()
.map(|shadow| {
let mut paint = skia_safe::Paint::default();
let filter = shadow.get_inner_shadow_filter();
paint.set_image_filter(filter);
paint
})
.collect()
}
}
#[cfg(test)]

View File

@@ -200,7 +200,7 @@ fn propagate_transform(
match content.grow_type() {
GrowType::AutoHeight => {
let paragraph_width = shape_bounds_after.width();
let mut paragraphs = content.to_paragraphs(None, None);
let mut paragraphs = content.to_paragraphs(None, None, None);
let height = auto_height(&mut paragraphs, paragraph_width);
let resize_transform = math::resize_matrix(
&shape_bounds_after,
@@ -213,7 +213,7 @@ fn propagate_transform(
}
GrowType::AutoWidth => {
let paragraph_width = content.get_width();
let mut paragraphs = content.to_paragraphs(None, None);
let mut paragraphs = content.to_paragraphs(None, None, None);
let height = auto_height(&mut paragraphs, paragraph_width);
let resize_transform = math::resize_matrix(
&shape_bounds_after,

View File

@@ -2,6 +2,7 @@ use crate::{
math::{Matrix, Rect},
render::{default_font, filters::compose_filters, DEFAULT_EMOJI_FONT},
};
use skia_safe::{
self as skia,
paint::Paint,
@@ -111,6 +112,7 @@ impl TextContent {
&self,
blur: Option<&ImageFilter>,
blur_mask: Option<&MaskFilter>,
shadow: Option<&Paint>,
) -> Vec<Vec<ParagraphBuilder>> {
let fonts = get_font_collection();
let fallback_fonts = get_fallback_fonts();
@@ -120,7 +122,8 @@ impl TextContent {
let paragraph_style = paragraph.paragraph_to_style();
let mut builder = ParagraphBuilder::new(&paragraph_style, fonts);
for leaf in &paragraph.children {
let text_style = leaf.to_style(&self.bounds, fallback_fonts, blur, blur_mask);
let text_style =
leaf.to_style(&self.bounds, fallback_fonts, blur, blur_mask, shadow);
let text = leaf.apply_text_transform();
builder.push_style(&text_style);
builder.add_text(&text);
@@ -137,6 +140,7 @@ impl TextContent {
bounds: &Rect,
blur: Option<&ImageFilter>,
blur_mask: Option<&MaskFilter>,
shadow: Option<&Paint>,
count_inner_strokes: usize,
) -> Vec<Vec<ParagraphBuilder>> {
let fallback_fonts = get_fallback_fonts();
@@ -152,14 +156,26 @@ impl TextContent {
if let Some(blur_mask) = blur_mask {
text_paint.set_mask_filter(blur_mask.clone());
}
let stroke_paints = get_text_stroke_paints(
let stroke_paints = if shadow.is_some() {
get_text_stroke_paints_with_shadows(
stroke,
blur,
blur_mask,
shadow,
leaf.is_transparent(),
)
} else {
get_text_stroke_paints(
stroke,
bounds,
&text_paint,
blur,
blur_mask,
count_inner_strokes,
);
)
};
let text: String = leaf.apply_text_transform();
for (paint_idx, stroke_paint) in stroke_paints.iter().enumerate() {
@@ -169,7 +185,7 @@ impl TextContent {
});
let stroke_paint = stroke_paint.clone();
let stroke_style =
leaf.to_stroke_style(&stroke_paint, fallback_fonts, blur, blur_mask);
leaf.to_stroke_style(&stroke_paint, fallback_fonts, blur, blur_mask, None);
builder.push_style(&stroke_style);
builder.add_text(&text);
}
@@ -187,7 +203,7 @@ impl TextContent {
pub fn get_width(&self) -> f32 {
if self.grow_type() == GrowType::AutoWidth {
let temp_paragraphs = self.to_paragraphs(None, None);
let temp_paragraphs = self.to_paragraphs(None, None, None);
let mut temp_paragraphs = temp_paragraphs;
auto_width(&mut temp_paragraphs, f32::MAX).ceil()
} else {
@@ -205,7 +221,7 @@ impl TextContent {
pub fn visual_bounds(&self) -> (f32, f32) {
let paragraph_width = self.get_width();
let mut paragraphs = self.to_paragraphs(None, None);
let mut paragraphs = self.to_paragraphs(None, None, None);
let paragraph_height = auto_height(&mut paragraphs, paragraph_width);
(paragraph_width, paragraph_height)
}
@@ -408,15 +424,24 @@ impl TextLeaf {
fallback_fonts: &HashSet<String>,
_blur: Option<&ImageFilter>,
blur_mask: Option<&MaskFilter>,
shadow: Option<&Paint>,
) -> 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());
if shadow.is_some() {
let paint = shadow.unwrap().clone();
style.set_foreground_paint(&paint);
} else {
let paint = merge_fills(&self.fills, *content_bounds);
style.set_foreground_paint(&paint);
}
if let Some(blur_mask) = blur_mask {
let mut paint = skia::Paint::default();
paint.set_mask_filter(blur_mask.clone());
style.set_foreground_paint(&paint);
}
style.set_font_size(self.font_size);
style.set_letter_spacing(self.letter_spacing);
style.set_half_leading(false);
@@ -450,8 +475,9 @@ impl TextLeaf {
fallback_fonts: &HashSet<String>,
blur: Option<&ImageFilter>,
blur_mask: Option<&MaskFilter>,
shadow: Option<&Paint>,
) -> skia::textlayout::TextStyle {
let mut style = self.to_style(&Rect::default(), fallback_fonts, blur, blur_mask);
let mut style = self.to_style(&Rect::default(), fallback_fonts, blur, blur_mask, shadow);
style.set_foreground_paint(stroke_paint);
style.set_font_size(self.font_size);
style.set_letter_spacing(self.letter_spacing);
@@ -492,6 +518,16 @@ impl TextLeaf {
pub fn scale_content(&mut self, value: f32) {
self.font_size *= value;
}
pub fn is_transparent(&self) -> bool {
if self.fills.is_empty() {
return true;
}
self.fills.iter().all(|fill| match fill {
shapes::Fill::Solid(shapes::SolidColor(color)) => color.a() == 0,
_ => false,
})
}
}
const RAW_PARAGRAPH_DATA_SIZE: usize = std::mem::size_of::<RawParagraphData>();
@@ -734,6 +770,94 @@ pub fn auto_height(paragraphs: &mut [Vec<ParagraphBuilder>], width: f32) -> f32
})
}
fn get_text_stroke_paints_with_shadows(
stroke: &Stroke,
blur: Option<&ImageFilter>,
blur_mask: Option<&MaskFilter>,
shadow: Option<&Paint>,
is_transparent: bool,
) -> Vec<Paint> {
let mut paints = Vec::new();
match stroke.kind {
StrokeKind::Inner => {
let mut paint = skia_safe::Paint::default();
paint.set_style(skia::PaintStyle::Fill);
paint.set_anti_alias(true);
if let Some(blur) = blur {
paint.set_image_filter(blur.clone());
}
if let Some(shadow) = shadow {
paint.set_image_filter(shadow.image_filter());
}
paints.push(paint.clone());
if is_transparent {
let image_filter = skia_safe::image_filters::erode(
(stroke.width, stroke.width),
paint.image_filter(),
None,
);
paint.set_image_filter(image_filter);
paint.set_blend_mode(skia::BlendMode::DstOut);
paints.push(paint.clone());
}
}
StrokeKind::Center => {
let mut paint = skia_safe::Paint::default();
paint.set_anti_alias(true);
paint.set_stroke_width(stroke.width);
if let Some(blur) = blur {
paint.set_image_filter(blur.clone());
}
if let Some(shadow) = shadow {
paint.set_image_filter(shadow.image_filter());
}
if is_transparent {
paint.set_style(skia::PaintStyle::Stroke);
} else {
paint.set_style(skia::PaintStyle::StrokeAndFill);
}
paints.push(paint);
}
StrokeKind::Outer => {
let mut paint = skia_safe::Paint::default();
paint.set_style(skia::PaintStyle::StrokeAndFill);
paint.set_anti_alias(true);
paint.set_stroke_width(stroke.width * 2.0);
if let Some(blur_mask) = blur_mask {
paint.set_mask_filter(blur_mask.clone());
}
if let Some(shadow) = shadow {
paint.set_image_filter(shadow.image_filter());
}
paints.push(paint.clone());
if is_transparent {
let image_filter = skia_safe::image_filters::erode(
(stroke.width, stroke.width),
paint.image_filter(),
None,
);
paint.set_image_filter(image_filter);
paint.set_blend_mode(skia::BlendMode::DstOut);
paints.push(paint.clone());
}
}
}
paints
}
fn get_text_stroke_paints(
stroke: &Stroke,
bounds: &Rect,
@@ -819,9 +943,6 @@ fn get_text_stroke_paints(
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);
}
}

View File

@@ -20,7 +20,7 @@ impl TextPaths {
let mut paths = Vec::new();
let mut offset_y = self.bounds.y();
let mut paragraphs = self.to_paragraphs(None, None);
let mut paragraphs = self.to_paragraphs(None, None, None);
for paragraphs in paragraphs.iter_mut() {
for paragraph_builder in paragraphs.iter_mut() {

View File

@@ -45,7 +45,7 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 {
if let Type::Text(content) = &shape.shape_type {
// 1. Reset Paragraphs
let paragraph_width = content.get_width();
let mut paragraphs = content.to_paragraphs(None, None);
let mut paragraphs = content.to_paragraphs(None, None, None);
let built_paragraphs = build_paragraphs_with_width(&mut paragraphs, paragraph_width);
// 2. Max Width Calculation
@@ -57,12 +57,12 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 {
// 3. Width and Height Calculation
match content.grow_type() {
GrowType::AutoHeight => {
let mut paragraph_height = content.to_paragraphs(None, None);
let mut paragraph_height = content.to_paragraphs(None, None, None);
height = auto_height(&mut paragraph_height, paragraph_width).ceil();
}
GrowType::AutoWidth => {
width = paragraph_width;
let mut paragraph_height = content.to_paragraphs(None, None);
let mut paragraph_height = content.to_paragraphs(None, None, None);
height = auto_height(&mut paragraph_height, paragraph_width).ceil();
}
GrowType::Fixed => {}