mirror of
https://github.com/penpot/penpot.git
synced 2025-06-02 06:41:37 +02:00
🎉 Add text solid strokes (#6384)
* 🎉 Add text strokes * 🔧 Minor refactor
This commit is contained in:
parent
44bf276c49
commit
61eb2f4a19
5 changed files with 166 additions and 23 deletions
|
@ -350,15 +350,32 @@ impl RenderState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type::Text(text_content) => {
|
||||
self.surfaces.apply_mut(&[SurfaceId::Fills], |s| {
|
||||
s.canvas().concat(&matrix);
|
||||
});
|
||||
|
||||
let paragraphs = text_content.to_skia_paragraphs(&self.fonts.font_collection());
|
||||
let paragraphs = text_content.get_skia_paragraphs(&self.fonts.font_collection());
|
||||
|
||||
shadows::render_text_drop_shadows(self, &shape, ¶graphs, antialias);
|
||||
text::render(self, &shape, ¶graphs, None, None);
|
||||
|
||||
for stroke in shape.strokes().rev() {
|
||||
let stroke_paints = shape.get_text_stroke_paint(&stroke);
|
||||
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);
|
||||
text::render(
|
||||
self,
|
||||
&shape,
|
||||
&stroke_paragraphs,
|
||||
Some(SurfaceId::Strokes),
|
||||
None,
|
||||
);
|
||||
shadows::render_text_inner_shadows(self, &shape, &stroke_paragraphs, antialias);
|
||||
}
|
||||
|
||||
shadows::render_text_inner_shadows(self, &shape, ¶graphs, antialias);
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -86,7 +86,7 @@ pub fn render_stroke_inner_shadows(
|
|||
pub fn render_text_drop_shadows(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &[Paragraph],
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
antialias: bool,
|
||||
) {
|
||||
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
|
||||
|
@ -98,7 +98,7 @@ pub fn render_text_drop_shadow(
|
|||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
shadow: &Shadow,
|
||||
paragraphs: &[Paragraph],
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = &shadow.get_drop_shadow_paint(antialias);
|
||||
|
@ -115,7 +115,7 @@ pub fn render_text_drop_shadow(
|
|||
pub fn render_text_inner_shadows(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &[Paragraph],
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
antialias: bool,
|
||||
) {
|
||||
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
|
||||
|
@ -127,7 +127,7 @@ pub fn render_text_inner_shadow(
|
|||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
shadow: &Shadow,
|
||||
paragraphs: &[Paragraph],
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = &shadow.get_inner_shadow_paint(antialias);
|
||||
|
|
|
@ -4,11 +4,10 @@ use skia_safe::{self as skia, canvas::SaveLayerRec, paint, textlayout::Paragraph
|
|||
pub fn render(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &[Paragraph],
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
surface_id: Option<SurfaceId>,
|
||||
paint: Option<&paint::Paint>,
|
||||
) {
|
||||
let mut offset_y = 0.0;
|
||||
let default_paint = skia::Paint::default();
|
||||
let mask = SaveLayerRec::default().paint(&paint.unwrap_or(&default_paint));
|
||||
let canvas = render_state
|
||||
|
@ -16,10 +15,13 @@ pub fn render(
|
|||
.canvas(surface_id.unwrap_or(SurfaceId::Fills));
|
||||
|
||||
canvas.save_layer(&mask);
|
||||
for skia_paragraph in paragraphs {
|
||||
for group in paragraphs {
|
||||
let mut offset_y = 0.0;
|
||||
for skia_paragraph in group {
|
||||
let xy = (shape.selrect().x(), shape.selrect.y() + offset_y);
|
||||
skia_paragraph.paint(canvas, xy);
|
||||
offset_y += skia_paragraph.height();
|
||||
}
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use skia_safe::{self as skia};
|
||||
use skia_safe::{self as skia, paint::Paint};
|
||||
|
||||
use crate::render::BlendMode;
|
||||
use crate::uuid::Uuid;
|
||||
|
@ -811,6 +811,60 @@ impl Shape {
|
|||
pub fn has_fills(&self) -> bool {
|
||||
!self.fills.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_text_stroke_paint(&self, stroke: &Stroke) -> Vec<Paint> {
|
||||
let mut paints = Vec::new();
|
||||
|
||||
match stroke.kind {
|
||||
StrokeKind::InnerStroke => {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_blend_mode(skia::BlendMode::DstOver);
|
||||
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::SrcATop);
|
||||
paint.set_anti_alias(true);
|
||||
paint.set_stroke_width(stroke.width * 2.0);
|
||||
paint.set_color(match &stroke.fill {
|
||||
Fill::Solid(color) => *color,
|
||||
_ => Color::BLACK,
|
||||
});
|
||||
paints.push(paint);
|
||||
}
|
||||
StrokeKind::CenterStroke => {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_style(skia::PaintStyle::Stroke);
|
||||
paint.set_anti_alias(true);
|
||||
paint.set_stroke_width(stroke.width);
|
||||
paint.set_color(match &stroke.fill {
|
||||
Fill::Solid(color) => *color,
|
||||
_ => Color::BLACK,
|
||||
});
|
||||
paints.push(paint);
|
||||
}
|
||||
StrokeKind::OuterStroke => {
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_style(skia::PaintStyle::Stroke);
|
||||
paint.set_blend_mode(skia::BlendMode::DstOver);
|
||||
paint.set_anti_alias(true);
|
||||
paint.set_stroke_width(stroke.width * 2.0);
|
||||
paint.set_color(match &stroke.fill {
|
||||
Fill::Solid(color) => *color,
|
||||
_ => Color::BLACK,
|
||||
});
|
||||
paints.push(paint);
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_blend_mode(skia::BlendMode::Clear);
|
||||
paint.set_anti_alias(true);
|
||||
paints.push(paint);
|
||||
}
|
||||
}
|
||||
|
||||
paints
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::{
|
|||
};
|
||||
use skia_safe::{
|
||||
self as skia,
|
||||
paint::Paint,
|
||||
textlayout::{FontCollection, ParagraphBuilder, ParagraphStyle},
|
||||
};
|
||||
|
||||
|
@ -47,32 +48,90 @@ impl TextContent {
|
|||
self.paragraphs.push(paragraph);
|
||||
}
|
||||
|
||||
pub fn to_paragraphs(&self, fonts: &FontCollection) -> Vec<skia::textlayout::Paragraph> {
|
||||
self.paragraphs
|
||||
pub fn to_paragraphs(&self, fonts: &FontCollection) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
let mut paragraph_group = Vec::new();
|
||||
let paragraphs = self
|
||||
.paragraphs
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let paragraph_style = p.paragraph_to_style();
|
||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||
for leaf in &p.children {
|
||||
let text_style = leaf.to_style(&p);
|
||||
let text_style = leaf.to_style(p);
|
||||
let text = leaf.apply_text_transform(p.text_transform);
|
||||
|
||||
builder.push_style(&text_style);
|
||||
builder.add_text(&text);
|
||||
builder.pop();
|
||||
}
|
||||
builder.build()
|
||||
})
|
||||
.collect();
|
||||
paragraph_group.push(paragraphs);
|
||||
paragraph_group
|
||||
}
|
||||
|
||||
pub fn to_stroke_paragraphs(
|
||||
&self,
|
||||
fonts: &FontCollection,
|
||||
stroke_paints: &Vec<Paint>,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
let mut paragraph_group = Vec::new();
|
||||
|
||||
for stroke_paint in stroke_paints {
|
||||
let mut stroke_paragraphs = Vec::new();
|
||||
for paragraph in &self.paragraphs {
|
||||
let paragraph_style = paragraph.paragraph_to_style();
|
||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||
for leaf in ¶graph.children {
|
||||
let stroke_style = leaf.to_stroke_style(paragraph, &stroke_paint);
|
||||
let text: String = leaf.apply_text_transform(paragraph.text_transform);
|
||||
builder.push_style(&stroke_style);
|
||||
builder.add_text(&text);
|
||||
let p = builder.build();
|
||||
stroke_paragraphs.push(p);
|
||||
}
|
||||
builder.reset();
|
||||
}
|
||||
paragraph_group.push(stroke_paragraphs);
|
||||
}
|
||||
paragraph_group
|
||||
}
|
||||
|
||||
pub fn get_skia_paragraphs(
|
||||
&self,
|
||||
fonts: &FontCollection,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
self.to_paragraphs(fonts)
|
||||
.into_iter()
|
||||
.map(|group| {
|
||||
group
|
||||
.into_iter()
|
||||
.map(|mut paragraph| {
|
||||
paragraph.layout(self.width());
|
||||
paragraph
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn to_skia_paragraphs(&self, fonts: &FontCollection) -> Vec<skia::textlayout::Paragraph> {
|
||||
let mut paragraphs = Vec::new();
|
||||
for mut skia_paragraph in self.to_paragraphs(fonts) {
|
||||
skia_paragraph.layout(self.width());
|
||||
paragraphs.push(skia_paragraph);
|
||||
}
|
||||
paragraphs
|
||||
pub fn get_skia_stroke_paragraphs(
|
||||
&self,
|
||||
fonts: &FontCollection,
|
||||
paints: &Vec<Paint>,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
self.to_stroke_paragraphs(fonts, paints)
|
||||
.into_iter()
|
||||
.map(|group| {
|
||||
group
|
||||
.into_iter()
|
||||
.map(|mut paragraph| {
|
||||
paragraph.layout(self.width());
|
||||
paragraph
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,6 +275,7 @@ impl TextLeaf {
|
|||
3 => skia::textlayout::TextDecoration::OVERLINE,
|
||||
_ => skia::textlayout::TextDecoration::NO_DECORATION,
|
||||
});
|
||||
|
||||
style.set_font_families(&[
|
||||
self.serialized_font_family(),
|
||||
default_font(),
|
||||
|
@ -225,6 +285,16 @@ impl TextLeaf {
|
|||
style
|
||||
}
|
||||
|
||||
pub fn to_stroke_style(
|
||||
&self,
|
||||
paragraph: &Paragraph,
|
||||
stroke_paint: &Paint,
|
||||
) -> skia::textlayout::TextStyle {
|
||||
let mut style = self.to_style(paragraph);
|
||||
style.set_foreground_paint(stroke_paint);
|
||||
style
|
||||
}
|
||||
|
||||
fn serialized_font_family(&self) -> String {
|
||||
format!("{}", self.font_family)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue