mirror of
https://github.com/penpot/penpot.git
synced 2025-05-16 15:56:17 +02:00
🎉 Stroke caps support for wasm render
This commit is contained in:
parent
4bd1e32462
commit
13ec04dd65
5 changed files with 288 additions and 35 deletions
|
@ -1,7 +1,7 @@
|
|||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{Fill, Image, Kind, Path, Shape, Stroke, StrokeKind};
|
||||
use super::{Fill, Image, Kind, Path, Shape, Stroke, StrokeCap, StrokeKind};
|
||||
use crate::math::Rect;
|
||||
use crate::render::{ImageStore, Renderable};
|
||||
|
||||
|
@ -155,6 +155,195 @@ fn draw_stroke_on_circle(canvas: &skia::Canvas, stroke: &Stroke, rect: &Rect, se
|
|||
canvas.draw_oval(&stroke_rect, &stroke.to_paint(selrect));
|
||||
}
|
||||
|
||||
fn handle_stroke_cap(
|
||||
canvas: &skia::Canvas,
|
||||
cap: StrokeCap,
|
||||
width: f32,
|
||||
paint: &mut skia::Paint,
|
||||
p1: &skia::Point,
|
||||
p2: &skia::Point,
|
||||
) {
|
||||
paint.set_style(skia::PaintStyle::Fill);
|
||||
paint.set_blend_mode(skia::BlendMode::Src);
|
||||
match cap {
|
||||
StrokeCap::None => {}
|
||||
StrokeCap::Line => {
|
||||
paint.set_style(skia::PaintStyle::Stroke);
|
||||
draw_arrow_cap(canvas, &paint, p1, p2, width * 4.);
|
||||
}
|
||||
StrokeCap::Triangle => {
|
||||
draw_triangle_cap(canvas, &paint, p1, p2, width * 4.);
|
||||
}
|
||||
StrokeCap::Rectangle => {
|
||||
draw_square_cap(canvas, &paint, p1, p2, width * 4., 0.);
|
||||
}
|
||||
StrokeCap::Circle => {
|
||||
canvas.draw_circle((p1.x, p1.y), width * 2., &paint);
|
||||
}
|
||||
StrokeCap::Diamond => {
|
||||
draw_square_cap(canvas, &paint, p1, p2, width * 4., 45.);
|
||||
}
|
||||
StrokeCap::Round => {
|
||||
canvas.draw_circle((p1.x, p1.y), width / 2.0, &paint);
|
||||
}
|
||||
StrokeCap::Square => {
|
||||
draw_square_cap(canvas, &paint, p1, p2, width, 0.);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_stroke_caps(
|
||||
path: &mut skia::Path,
|
||||
stroke: &Stroke,
|
||||
selrect: &Rect,
|
||||
canvas: &skia::Canvas,
|
||||
is_open: bool,
|
||||
) {
|
||||
let points_count = path.count_points();
|
||||
let mut points = vec![skia::Point::default(); points_count];
|
||||
let c_points = path.get_points(&mut points);
|
||||
|
||||
// Closed shapes don't have caps
|
||||
if c_points >= 2 && is_open {
|
||||
let first_point = points.first().unwrap();
|
||||
let last_point = points.last().unwrap();
|
||||
|
||||
let kind = stroke.render_kind(is_open);
|
||||
let mut paint_stroke = stroke.to_stroked_paint(kind.clone(), selrect);
|
||||
|
||||
handle_stroke_cap(
|
||||
canvas,
|
||||
stroke.cap_start,
|
||||
stroke.width,
|
||||
&mut paint_stroke,
|
||||
first_point,
|
||||
&points[1],
|
||||
);
|
||||
handle_stroke_cap(
|
||||
canvas,
|
||||
stroke.cap_end,
|
||||
stroke.width,
|
||||
&mut paint_stroke,
|
||||
last_point,
|
||||
&points[points_count - 2],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_square_cap(
|
||||
canvas: &skia::Canvas,
|
||||
paint: &skia::Paint,
|
||||
center: &skia::Point,
|
||||
direction: &skia::Point,
|
||||
size: f32,
|
||||
extra_rotation: f32,
|
||||
) {
|
||||
let dx = direction.x - center.x;
|
||||
let dy = direction.y - center.y;
|
||||
let angle = dy.atan2(dx);
|
||||
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_rotate(
|
||||
angle.to_degrees() + extra_rotation,
|
||||
skia::Point::new(center.x, center.y),
|
||||
);
|
||||
|
||||
let half_size = size / 2.0;
|
||||
let rect = skia::Rect::from_xywh(center.x - half_size, center.y - half_size, size, size);
|
||||
|
||||
let points = [
|
||||
skia::Point::new(rect.left(), rect.top()),
|
||||
skia::Point::new(rect.right(), rect.top()),
|
||||
skia::Point::new(rect.right(), rect.bottom()),
|
||||
skia::Point::new(rect.left(), rect.bottom()),
|
||||
];
|
||||
|
||||
let mut transformed_points = points.clone();
|
||||
matrix.map_points(&mut transformed_points, &points);
|
||||
|
||||
let mut path = skia::Path::new();
|
||||
path.move_to(skia::Point::new(center.x, center.y));
|
||||
path.move_to(transformed_points[0]);
|
||||
path.line_to(transformed_points[1]);
|
||||
path.line_to(transformed_points[2]);
|
||||
path.line_to(transformed_points[3]);
|
||||
path.close();
|
||||
canvas.draw_path(&path, paint);
|
||||
}
|
||||
|
||||
fn draw_arrow_cap(
|
||||
canvas: &skia::Canvas,
|
||||
paint: &skia::Paint,
|
||||
center: &skia::Point,
|
||||
direction: &skia::Point,
|
||||
size: f32,
|
||||
) {
|
||||
let dx = direction.x - center.x;
|
||||
let dy = direction.y - center.y;
|
||||
let angle = dy.atan2(dx);
|
||||
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_rotate(
|
||||
angle.to_degrees() - 90.,
|
||||
skia::Point::new(center.x, center.y),
|
||||
);
|
||||
|
||||
let half_height = size / 2.;
|
||||
let points = [
|
||||
skia::Point::new(center.x, center.y - half_height),
|
||||
skia::Point::new(center.x - size, center.y + half_height),
|
||||
skia::Point::new(center.x + size, center.y + half_height),
|
||||
];
|
||||
|
||||
let mut transformed_points = points.clone();
|
||||
matrix.map_points(&mut transformed_points, &points);
|
||||
|
||||
let mut path = skia::Path::new();
|
||||
path.move_to(transformed_points[1]);
|
||||
path.line_to(transformed_points[0]);
|
||||
path.line_to(transformed_points[2]);
|
||||
path.move_to(skia::Point::new(center.x, center.y));
|
||||
path.line_to(transformed_points[0]);
|
||||
|
||||
canvas.draw_path(&path, paint);
|
||||
}
|
||||
|
||||
fn draw_triangle_cap(
|
||||
canvas: &skia::Canvas,
|
||||
paint: &skia::Paint,
|
||||
center: &skia::Point,
|
||||
direction: &skia::Point,
|
||||
size: f32,
|
||||
) {
|
||||
let dx = direction.x - center.x;
|
||||
let dy = direction.y - center.y;
|
||||
let angle = dy.atan2(dx);
|
||||
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_rotate(
|
||||
angle.to_degrees() - 90.,
|
||||
skia::Point::new(center.x, center.y),
|
||||
);
|
||||
|
||||
let half_height = size / 2.;
|
||||
let points = [
|
||||
skia::Point::new(center.x, center.y - half_height),
|
||||
skia::Point::new(center.x - size, center.y + half_height),
|
||||
skia::Point::new(center.x + size, center.y + half_height),
|
||||
];
|
||||
|
||||
let mut transformed_points = points.clone();
|
||||
matrix.map_points(&mut transformed_points, &points);
|
||||
|
||||
let mut path = skia::Path::new();
|
||||
path.move_to(transformed_points[0]);
|
||||
path.line_to(transformed_points[1]);
|
||||
path.line_to(transformed_points[2]);
|
||||
path.close();
|
||||
|
||||
canvas.draw_path(&path, paint);
|
||||
}
|
||||
|
||||
fn draw_stroke_on_path(
|
||||
canvas: &skia::Canvas,
|
||||
stroke: &Stroke,
|
||||
|
@ -177,6 +366,7 @@ fn draw_stroke_on_path(
|
|||
// For center stroke we don't need to do anything extra
|
||||
StrokeKind::CenterStroke => {
|
||||
canvas.draw_path(&skia_path, &paint_stroke);
|
||||
handle_stroke_caps(&mut skia_path, stroke, selrect, canvas, path.is_open());
|
||||
}
|
||||
// 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::OuterStroke => {
|
||||
|
@ -295,6 +485,7 @@ pub fn draw_image_stroke_in_container(
|
|||
}
|
||||
let paint = stroke.to_stroked_paint(stroke_kind, &outer_rect);
|
||||
canvas.draw_path(&path, &paint);
|
||||
handle_stroke_caps(&mut path, stroke, &outer_rect, canvas, p.is_open());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ pub enum StrokeStyle {
|
|||
Mixed,
|
||||
}
|
||||
|
||||
impl From<i32> for StrokeStyle {
|
||||
fn from(value: i32) -> Self {
|
||||
impl From<u8> for StrokeStyle {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => StrokeStyle::Dotted,
|
||||
2 => StrokeStyle::Dashed,
|
||||
|
@ -21,15 +21,31 @@ impl From<i32> for StrokeStyle {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum StrokeCap {
|
||||
None,
|
||||
// Line,
|
||||
// Triangle,
|
||||
// Circle,
|
||||
// Diamond,
|
||||
// Round,
|
||||
// Square,
|
||||
Line,
|
||||
Triangle,
|
||||
Rectangle,
|
||||
Circle,
|
||||
Diamond,
|
||||
Round,
|
||||
Square,
|
||||
}
|
||||
|
||||
impl From<u8> for StrokeCap {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => StrokeCap::Line,
|
||||
2 => StrokeCap::Triangle,
|
||||
3 => StrokeCap::Rectangle,
|
||||
4 => StrokeCap::Circle,
|
||||
5 => StrokeCap::Diamond,
|
||||
6 => StrokeCap::Round,
|
||||
7 => StrokeCap::Square,
|
||||
_ => StrokeCap::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
@ -59,38 +75,38 @@ impl Stroke {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_center_stroke(width: f32, style: i32) -> Self {
|
||||
pub fn new_center_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) -> Self {
|
||||
let transparent = skia::Color::from_argb(0, 0, 0, 0);
|
||||
Stroke {
|
||||
fill: Fill::Solid(transparent),
|
||||
width: width,
|
||||
style: StrokeStyle::from(style),
|
||||
cap_end: StrokeCap::None,
|
||||
cap_start: StrokeCap::None,
|
||||
cap_end: StrokeCap::from(cap_end),
|
||||
cap_start: StrokeCap::from(cap_start),
|
||||
kind: StrokeKind::CenterStroke,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_inner_stroke(width: f32, style: i32) -> Self {
|
||||
pub fn new_inner_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) -> Self {
|
||||
let transparent = skia::Color::from_argb(0, 0, 0, 0);
|
||||
Stroke {
|
||||
fill: Fill::Solid(transparent),
|
||||
width: width,
|
||||
style: StrokeStyle::from(style),
|
||||
cap_end: StrokeCap::None,
|
||||
cap_start: StrokeCap::None,
|
||||
cap_end: StrokeCap::from(cap_end),
|
||||
cap_start: StrokeCap::from(cap_start),
|
||||
kind: StrokeKind::InnerStroke,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_outer_stroke(width: f32, style: i32) -> Self {
|
||||
pub fn new_outer_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) -> Self {
|
||||
let transparent = skia::Color::from_argb(0, 0, 0, 0);
|
||||
Stroke {
|
||||
fill: Fill::Solid(transparent),
|
||||
width: width,
|
||||
style: StrokeStyle::from(style),
|
||||
cap_end: StrokeCap::None,
|
||||
cap_start: StrokeCap::None,
|
||||
cap_end: StrokeCap::from(cap_end),
|
||||
cap_start: StrokeCap::from(cap_start),
|
||||
kind: StrokeKind::OuterStroke,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue