mirror of
https://github.com/penpot/penpot.git
synced 2025-07-17 18:37:12 +02:00
🔧 Add methods to render text as path (#6624)
* 🔧 Refactor text strokes drawing * 🔧 Add text to path methods for future usage * 📚 Add text as paths internal documentation
This commit is contained in:
parent
2d36a1f3e0
commit
4869373a43
8 changed files with 462 additions and 63 deletions
|
@ -3,9 +3,10 @@ use std::collections::HashMap;
|
|||
use crate::math::{Matrix, Point, Rect};
|
||||
|
||||
use crate::shapes::{Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, Type};
|
||||
use skia_safe::{self as skia, ImageFilter, RRect};
|
||||
use skia_safe::{self as skia, textlayout::Paragraph, ImageFilter, RRect};
|
||||
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::render::text::{self};
|
||||
|
||||
// FIXME: See if we can simplify these arguments
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -69,6 +70,41 @@ fn draw_stroke_on_circle(
|
|||
canvas.draw_oval(stroke_rect, &paint);
|
||||
}
|
||||
|
||||
fn draw_outer_stroke_path(
|
||||
canvas: &skia::Canvas,
|
||||
path: &skia::Path,
|
||||
paint: &skia::Paint,
|
||||
antialias: bool,
|
||||
) {
|
||||
let mut outer_paint = skia::Paint::default();
|
||||
outer_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
outer_paint.set_anti_alias(antialias);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&outer_paint);
|
||||
canvas.save_layer(&layer_rec);
|
||||
canvas.draw_path(path, paint);
|
||||
|
||||
let mut clear_paint = skia::Paint::default();
|
||||
clear_paint.set_blend_mode(skia::BlendMode::Clear);
|
||||
clear_paint.set_anti_alias(antialias);
|
||||
canvas.draw_path(path, &clear_paint);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
// For inner stroke we draw a center stroke (with double width) and clip to the original path (that way the extra outer stroke is removed)
|
||||
fn draw_inner_stroke_path(
|
||||
canvas: &skia::Canvas,
|
||||
path: &skia::Path,
|
||||
paint: &skia::Paint,
|
||||
antialias: bool,
|
||||
) {
|
||||
canvas.save();
|
||||
canvas.clip_path(path, skia::ClipOp::Intersect, antialias);
|
||||
canvas.draw_path(path, paint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
// For outer stroke we draw a center stroke (with double width) and use another path with blend mode clear to remove the inner stroke added
|
||||
// FIXME: See if we can simplify these arguments
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn draw_stroke_on_path(
|
||||
|
@ -93,34 +129,15 @@ pub fn draw_stroke_on_path(
|
|||
paint.set_image_filter(filter.clone());
|
||||
}
|
||||
|
||||
// Draw the different kind of strokes for a path requires different strategies:
|
||||
match stroke.render_kind(is_open) {
|
||||
// For inner stroke we draw a center stroke (with double width) and clip to the original path (that way the extra outer stroke is removed)
|
||||
StrokeKind::Inner => {
|
||||
canvas.save(); // As we are using clear for surfaces we use save and restore here to still be able to clean the full surface
|
||||
canvas.clip_path(&skia_path, skia::ClipOp::Intersect, antialias);
|
||||
canvas.draw_path(&skia_path, &paint);
|
||||
canvas.restore();
|
||||
draw_inner_stroke_path(canvas, &skia_path, &paint, antialias);
|
||||
}
|
||||
// For center stroke we don't need to do anything extra
|
||||
StrokeKind::Center => {
|
||||
canvas.draw_path(&skia_path, &paint);
|
||||
}
|
||||
// For outer stroke we draw a center stroke (with double width) and use another path with blend mode clear to remove the inner stroke added
|
||||
StrokeKind::Outer => {
|
||||
let mut outer_paint = skia::Paint::default();
|
||||
outer_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
outer_paint.set_anti_alias(antialias);
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&outer_paint);
|
||||
canvas.save_layer(&layer_rec);
|
||||
canvas.draw_path(&skia_path, &paint);
|
||||
|
||||
let mut clear_paint = skia::Paint::default();
|
||||
clear_paint.set_blend_mode(skia::BlendMode::Clear);
|
||||
clear_paint.set_anti_alias(antialias);
|
||||
canvas.draw_path(&skia_path, &clear_paint);
|
||||
|
||||
canvas.restore();
|
||||
draw_outer_stroke_path(canvas, &skia_path, &paint, antialias);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,15 +517,13 @@ fn draw_image_stroke_in_container(
|
|||
canvas.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* This SHOULD be the only public function in this module.
|
||||
*/
|
||||
pub fn render(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
stroke: &Stroke,
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadow: Option<&ImageFilter>,
|
||||
paragraphs: Option<&[Vec<Paragraph>]>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let scale = render_state.get_scale();
|
||||
|
@ -541,6 +556,14 @@ pub fn render(
|
|||
Type::Circle => draw_stroke_on_circle(
|
||||
canvas, stroke, &selrect, &selrect, svg_attrs, scale, shadow, antialias,
|
||||
),
|
||||
Type::Text(_) => {
|
||||
text::render(
|
||||
render_state,
|
||||
shape,
|
||||
paragraphs.expect("Text shapes should have paragraphs"),
|
||||
Some(SurfaceId::Strokes),
|
||||
);
|
||||
}
|
||||
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
||||
if let Some(path) = shape_type.path() {
|
||||
draw_stroke_on_path(
|
||||
|
@ -560,3 +583,46 @@ pub fn render(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render text paths (unused)
|
||||
#[allow(dead_code)]
|
||||
pub fn render_text_paths(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
stroke: &Stroke,
|
||||
paths: &Vec<(skia::Path, skia::Paint)>,
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadow: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let scale = render_state.get_scale();
|
||||
let canvas = render_state
|
||||
.surfaces
|
||||
.canvas(surface_id.unwrap_or(SurfaceId::Strokes));
|
||||
let selrect = &shape.selrect;
|
||||
let svg_attrs = &shape.svg_attrs;
|
||||
let mut paint: skia_safe::Handle<_> =
|
||||
stroke.to_text_stroked_paint(false, selrect, svg_attrs, scale, antialias);
|
||||
|
||||
if let Some(filter) = shadow {
|
||||
paint.set_image_filter(filter.clone());
|
||||
}
|
||||
|
||||
match stroke.render_kind(false) {
|
||||
StrokeKind::Inner => {
|
||||
for (path, _) in paths {
|
||||
draw_inner_stroke_path(canvas, path, &paint, antialias);
|
||||
}
|
||||
}
|
||||
StrokeKind::Center => {
|
||||
for (path, _) in paths {
|
||||
canvas.draw_path(path, &paint);
|
||||
}
|
||||
}
|
||||
StrokeKind::Outer => {
|
||||
for (path, _) in paths {
|
||||
draw_outer_stroke_path(canvas, path, &paint, antialias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue