diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 040df0e6e7..62dfc89d60 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -470,18 +470,6 @@ impl RenderState { text::render(self, &shape, &mut paragraphs, None, None); - if shape.has_visible_inner_strokes() { - // Inner strokes paints need the text fill to apply correctly their blend modes - // (e.g., SrcATop, DstOver) - text::render( - self, - &shape, - &mut paragraphs, - Some(SurfaceId::Strokes), - None, - ); - } - for stroke in shape.visible_strokes().rev() { let mut stroke_paragraphs = text_content.get_skia_stroke_paragraphs(stroke, &shape.selrect()); diff --git a/render-wasm/src/render/text.rs b/render-wasm/src/render/text.rs index 2df1bf137d..ff33d0cab1 100644 --- a/render-wasm/src/render/text.rs +++ b/render-wasm/src/render/text.rs @@ -24,6 +24,9 @@ pub fn render( _ => 0.0, }; + let layer_rec = skia_safe::canvas::SaveLayerRec::default(); + canvas.save_layer(&layer_rec); + for group in paragraphs { let mut group_offset_y = global_offset_y; let group_len = group.len(); @@ -36,15 +39,7 @@ pub fn render( let mut paragraph_builder = ParagraphBuilder::new(&builder.get_paragraph_style(), fonts); let mut text_style: skia_safe::Handle<_> = builder.peek_style(); - let current_paint = text_style.foreground().clone(); - let blend_mode = current_paint.as_blend_mode(); - let mut new_paint = paint.unwrap().clone(); - if blend_mode != Some(skia_safe::BlendMode::SrcIn) { - new_paint.set_stroke_width(current_paint.stroke_width()); - new_paint.set_style(skia_safe::PaintStyle::StrokeAndFill); - } - new_paint.set_anti_alias(true); - text_style.set_foreground_paint(&new_paint); + text_style.set_foreground_paint(paint.unwrap()); paragraph_builder.reset(); paragraph_builder.push_style(&text_style); paragraph_builder.add_text(&text); @@ -138,6 +133,8 @@ pub fn render( global_offset_y = group_offset_y; } } + + canvas.restore(); } pub fn calculate_text_decoration_rect( diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index fcbf353214..643b2f9249 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -980,6 +980,7 @@ impl Shape { self.visible_strokes().next().is_some() } + #[allow(dead_code)] pub fn has_visible_inner_strokes(&self) -> bool { self.visible_strokes().any(|s| s.kind == StrokeKind::Inner) } diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index c1ddc8dd19..5a0b5adc3d 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -118,24 +118,35 @@ impl TextContent { bounds: &Rect, ) -> Vec> { let fallback_fonts = get_fallback_fonts(); - let stroke_paints = get_text_stroke_paints(stroke, bounds); let fonts = get_font_collection(); let mut paragraph_group = Vec::new(); for paragraph in &self.paragraphs { - let mut stroke_paragraphs = Vec::new(); - for stroke_paint in &stroke_paints { - let paragraph_style = paragraph.paragraph_to_style(); - let mut builder = ParagraphBuilder::new(¶graph_style, fonts); - for leaf in ¶graph.children { + let mut stroke_paragraphs_map: std::collections::HashMap = + 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 text: String = leaf.apply_text_transform(); + + for (paint_idx, stroke_paint) in stroke_paints.iter().enumerate() { + let builder = stroke_paragraphs_map.entry(paint_idx).or_insert_with(|| { + 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 text: String = leaf.apply_text_transform(); builder.push_style(&stroke_style); builder.add_text(&text); } - stroke_paragraphs.push(builder); } + + let stroke_paragraphs: Vec = (0..stroke_paragraphs_map.len()) + .map(|i| stroke_paragraphs_map.remove(&i).unwrap()) + .collect(); + paragraph_group.push(stroke_paragraphs); } @@ -693,20 +704,55 @@ pub fn auto_height(paragraphs: &mut [Vec], width: f32) -> f32 }) } -fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect) -> Vec { +fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect, text_paint: &Paint) -> Vec { let mut paints = Vec::new(); match stroke.kind { StrokeKind::Inner => { - let mut paint = skia::Paint::default(); - paint.set_style(skia::PaintStyle::Stroke); - paint.set_blend_mode(skia::BlendMode::SrcIn); - paint.set_anti_alias(true); - paint.set_stroke_width(stroke.width * 2.0); + let shader = text_paint.shader(); + let mut is_opaque = true; - set_paint_fill(&mut paint, &stroke.fill, bounds); + if shader.is_some() { + is_opaque = shader.unwrap().is_opaque(); + } - paints.push(paint); + if is_opaque { + let mut paint = text_paint.clone(); + paint.set_style(skia::PaintStyle::Fill); + paint.set_anti_alias(true); + paints.push(paint); + + let mut paint = skia::Paint::default(); + paint.set_style(skia::PaintStyle::Stroke); + paint.set_blend_mode(skia::BlendMode::SrcIn); + paint.set_anti_alias(true); + paint.set_stroke_width(stroke.width * 2.0); + set_paint_fill(&mut paint, &stroke.fill, bounds); + paints.push(paint); + } else { + // outer + let mut paint = skia::Paint::default(); + paint.set_style(skia::PaintStyle::Stroke); + paint.set_blend_mode(skia::BlendMode::DstATop); + paint.set_anti_alias(true); + paint.set_stroke_width(stroke.width * 2.0); + paints.push(paint); + + let mut paint = skia::Paint::default(); + paint.set_style(skia::PaintStyle::Fill); + paint.set_blend_mode(skia::BlendMode::Clear); + paint.set_anti_alias(true); + paints.push(paint); + + // inner + let mut paint = skia::Paint::default(); + paint.set_style(skia::PaintStyle::Stroke); + paint.set_stroke_width(stroke.width * 2.0); + paint.set_blend_mode(skia::BlendMode::Xor); + paint.set_anti_alias(true); + set_paint_fill(&mut paint, &stroke.fill, bounds); + paints.push(paint); + } } StrokeKind::Center => { let mut paint = skia::Paint::default();