0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-21 06:02:32 -05:00
This commit is contained in:
Alejandro Alonso 2024-12-31 19:04:48 +01:00
parent c855b9ed4c
commit f2619461bc
4 changed files with 231 additions and 52 deletions

View file

@ -219,9 +219,12 @@
color (:stroke-color stroke)
gradient (:stroke-color-gradient stroke)
image (:stroke-image stroke)
width (:stroke-width stroke)]
(println "-------_add_shape_center_stroke" stroke)
(h/call internal-module "_add_shape_center_stroke" width)
width (:stroke-width stroke)
align (:stroke-alignment stroke)]
(case align
:inner (h/call internal-module "_add_shape_inner_stroke" width)
:outer (h/call internal-module "_add_shape_outer_stroke" width)
(h/call internal-module "_add_shape_center_stroke" width))
(cond
(some? gradient)
@ -266,7 +269,7 @@
(dm/get-prop image :height))
(when (== cached-image? 0)
(store-image id)))
(some? color)
(let [rgba (rgba-from-hex color opacity)]
(h/call internal-module "_add_shape_stroke_solid_fill" rgba)))))

View file

@ -354,12 +354,30 @@ pub extern "C" fn add_shape_center_stroke(width: f32) {
}
}
#[no_mangle]
pub extern "C" fn add_shape_inner_stroke(width: f32) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() {
shape.add_stroke(shapes::Stroke::new_inner_stroke(width))
}
}
#[no_mangle]
pub extern "C" fn add_shape_outer_stroke(width: f32) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() {
shape.add_stroke(shapes::Stroke::new_outer_stroke(width))
}
}
#[no_mangle]
pub extern "C" fn add_shape_stroke_solid_fill(raw_color: u32) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() {
let color = skia::Color::new(raw_color);
shape.set_stroke_fill(shapes::Fill::Solid(color));
shape
.set_stroke_fill(shapes::Fill::Solid(color))
.expect("could not add stroke solid fill");
}
}
@ -373,11 +391,13 @@ pub extern "C" fn add_shape_stroke_linear_fill(
) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() {
shape.set_stroke_fill(shapes::Fill::new_linear_gradient(
(start_x, start_y),
(end_x, end_y),
opacity,
));
shape
.set_stroke_fill(shapes::Fill::new_linear_gradient(
(start_x, start_y),
(end_x, end_y),
opacity,
))
.expect("could not add stroke linear fill");
}
}
@ -392,12 +412,14 @@ pub extern "C" fn add_shape_stroke_radial_fill(
) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() {
shape.set_stroke_fill(shapes::Fill::new_radial_gradient(
(start_x, start_y),
(end_x, end_y),
opacity,
width,
));
shape
.set_stroke_fill(shapes::Fill::new_radial_gradient(
(start_x, start_y),
(end_x, end_y),
opacity,
width,
))
.expect("could not add stroke radial fill");
}
}
@ -431,11 +453,13 @@ pub extern "C" fn add_shape_image_stroke(
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let id = uuid_from_u32_quartet(a, b, c, d);
if let Some(shape) = state.current_shape() {
shape.set_stroke_fill(shapes::Fill::new_image_fill(
id,
(alpha * 0xff as f32).floor() as u8,
(width, height),
));
shape
.set_stroke_fill(shapes::Fill::new_image_fill(
id,
(alpha * 0xff as f32).floor() as u8,
(width, height),
))
.expect("could not add stroke image fill");
}
}

View file

@ -84,7 +84,7 @@ fn render_fill(
(Fill::Image(image_fill), kind) => {
let image = images.get(&image_fill.id());
if let Some(image) = image {
draw_image_in_container(
draw_image_fill_in_container(
surface.canvas(),
&image,
image_fill.size(),
@ -121,12 +121,12 @@ fn render_stroke(
if let Fill::Image(image_fill) = stroke.clone().fill() {
let image = images.get(&image_fill.id());
if let Some(image) = image {
draw_image_in_container(
draw_image_stroke_in_container(
surface.canvas(),
&image,
image_fill.size(),
kind,
&stroke.to_paint(&selrect),
stroke.clone().width(),
&selrect,
path_transform,
);
@ -134,22 +134,44 @@ fn render_stroke(
} else {
match kind {
Kind::Rect(rect) => {
surface.canvas().draw_rect(rect, &stroke.to_paint(&selrect));
let stroke_rect = stroke.clone().outer_rect(rect);
surface
.canvas()
.draw_rect(&stroke_rect, &stroke.to_paint(&selrect));
}
Kind::Circle(rect) => {
surface.canvas().draw_oval(rect, &stroke.to_paint(&selrect));
let stroke_rect = stroke.clone().outer_rect(rect);
surface
.canvas()
.draw_oval(&stroke_rect, &stroke.to_paint(&selrect));
}
Kind::Path(path) => {
//TODO
surface.canvas().save();
let mut stroke_rect = stroke.clone().outer_rect(&selrect);
let mut fill_paint = skia::Paint::default();
fill_paint.set_blend_mode(skia::BlendMode::SrcOver);
fill_paint.set_anti_alias(true);
surface.canvas().draw_path(
&path.to_skia_path().transform(path_transform.unwrap()),
&stroke.to_paint(&selrect),
&fill_paint,
);
let mut path_paint = skia::Paint::default();
path_paint.set_blend_mode(skia::BlendMode::SrcOut);
path_paint.set_style(skia::PaintStyle::Stroke);
path_paint.set_stroke_width(2. * stroke.clone().width());
path_paint.set_anti_alias(true);
surface.canvas().draw_path(
&path.to_skia_path().transform(path_transform.unwrap()),
&path_paint,
);
surface.canvas().restore();
}
}
}
}
pub fn draw_image_in_container(
pub fn draw_image_fill_in_container(
canvas: &skia::Canvas,
image: &Image,
size: (i32, i32),
@ -158,8 +180,73 @@ pub fn draw_image_in_container(
container: &Rect,
path_transform: Option<&skia::Matrix>,
) {
// TODO: param
let border_width = 20.0; // Width of the border
let width = size.0 as f32;
let height = size.1 as f32;
let image_aspect_ratio = width / height;
// Container size
let container_width = container.width();
let container_height = container.height();
let container_aspect_ratio = container_width / container_height;
// Calculate scale to ensure the image covers the container
let scale = if image_aspect_ratio > container_aspect_ratio {
// Image is wider, scale based on height to cover container
container_height / height
} else {
// Image is taller, scale based on width to cover container
container_width / width
};
// Scaled size of the image
let scaled_width = width * scale;
let scaled_height = height * scale;
let dest_rect = Rect::from_xywh(
container.left - (scaled_width - container_width) / 2.0,
container.top - (scaled_height - container_height) / 2.0,
scaled_width,
scaled_height,
);
// Save the current canvas state
canvas.save();
// Set the clipping rectangle to the container bounds
match kind {
Kind::Rect(_) => {
canvas.clip_rect(container, skia::ClipOp::Intersect, true);
}
Kind::Circle(_) => {
let mut oval_path = skia::Path::new();
oval_path.add_oval(container, None);
canvas.clip_path(&oval_path, skia::ClipOp::Intersect, true);
}
Kind::Path(p) => {
canvas.clip_path(
&p.to_skia_path().transform(path_transform.unwrap()),
skia::ClipOp::Intersect,
true,
);
}
}
// Draw the image with the calculated destination rectangle
canvas.draw_image_rect(image, None, dest_rect, &paint);
// Restore the canvas to remove the clipping
canvas.restore();
}
pub fn draw_image_stroke_in_container(
canvas: &skia::Canvas,
image: &Image,
size: (i32, i32),
kind: &Kind,
border_width: f32,
container: &Rect,
path_transform: Option<&skia::Matrix>,
) {
let width = size.0 as f32;
let height = size.1 as f32;
let image_aspect_ratio = width / height;
@ -200,9 +287,9 @@ pub fn draw_image_in_container(
canvas.save();
let mut path_paint = skia::Paint::default();
path_paint.set_stroke_width(border_width); // Ancho del contorno
path_paint.set_style(skia::paint::Style::Stroke); // Solo contorno
path_paint.set_blend_mode(skia::BlendMode::SrcOver); // Recortar la imagen según el path
path_paint.set_stroke_width(border_width);
path_paint.set_style(skia::paint::Style::Stroke);
path_paint.set_blend_mode(skia::BlendMode::SrcOver);
path_paint.set_anti_alias(true);
// Set the clipping rectangle to the container bounds

View file

@ -5,20 +5,20 @@ use skia_safe as skia;
#[derive(Debug, Clone, PartialEq)]
pub enum StrokeStyle {
Solid,
Dotted,
Dashed,
Mixed,
// Dotted,
// Dashed,
// Mixed,
}
#[derive(Debug, Clone, PartialEq)]
pub enum StrokeCap {
None,
Line,
Triangle,
Circle,
Diamond,
Round,
Square,
// Line,
// Triangle,
// Circle,
// Diamond,
// Round,
// Square,
}
#[derive(Debug, Clone, PartialEq)]
@ -49,6 +49,28 @@ impl Stroke {
})
}
pub fn new_inner_stroke(width: f32) -> Self {
let transparent = skia::Color::from_argb(0, 0, 0, 0);
Self::InnerStroke(StrokeAttrs {
fill: Fill::Solid(transparent),
width: width,
style: StrokeStyle::Solid,
cap_end: StrokeCap::None,
cap_start: StrokeCap::None,
})
}
pub fn new_outer_stroke(width: f32) -> Self {
let transparent = skia::Color::from_argb(0, 0, 0, 0);
Self::OuterStroke(StrokeAttrs {
fill: Fill::Solid(transparent),
width: width,
style: StrokeStyle::Solid,
cap_end: StrokeCap::None,
cap_start: StrokeCap::None,
})
}
pub fn fill(self) -> Fill {
match self {
Stroke::InnerStroke(attrs) => attrs.fill,
@ -57,6 +79,47 @@ impl Stroke {
}
}
pub fn outer_rect(self, rect: &math::Rect) -> math::Rect {
match self {
Stroke::InnerStroke(attrs) => math::Rect::from_xywh(
rect.left + (attrs.width / 2.),
rect.top + (attrs.width / 2.),
rect.width() - attrs.width,
rect.height() - attrs.width,
),
Stroke::CenterStroke(attrs) => {
math::Rect::from_xywh(rect.left, rect.top, rect.width(), rect.height())
}
Stroke::OuterStroke(attrs) => math::Rect::from_xywh(
rect.left - (attrs.width / 2.),
rect.top - (attrs.width / 2.),
rect.width() + attrs.width,
rect.height() + attrs.width,
),
}
}
pub fn outer_path(self, path: &skia::Path) -> skia::Path {
// match self {
// Stroke::InnerStroke(attrs) => math::Rect::from_xywh(
// rect.left + (attrs.width / 2.),
// rect.top + (attrs.width / 2.),
// rect.width() - attrs.width,
// rect.height() - attrs.width,
// ),
// Stroke::CenterStroke(attrs) => {
// math::Rect::from_xywh(rect.left, rect.top, rect.width(), rect.height())
// }
// Stroke::OuterStroke(attrs) => math::Rect::from_xywh(
// rect.left - (attrs.width / 2.),
// rect.top - (attrs.width / 2.),
// rect.width() + attrs.width,
// rect.height() + attrs.width,
// ),
// }
path.clone()
}
pub fn fill_mut(&mut self) -> &mut Fill {
match self {
Stroke::InnerStroke(attrs) => &mut attrs.fill,
@ -74,6 +137,7 @@ impl Stroke {
}
pub fn to_paint(&self, rect: &math::Rect) -> skia::Paint {
// TODO antialias
match self {
Stroke::InnerStroke(attrs) => {
let mut paint = attrs.fill.to_paint(rect);
@ -85,17 +149,18 @@ impl Stroke {
paint.set_stroke_cap(skia::paint::Cap::Butt); // Termina la línea sin agregar espacio adicional
paint
}
Stroke::OuterStroke(attrs) => {
let mut paint = attrs.fill.to_paint(rect);
paint.set_style(skia::PaintStyle::Stroke);
paint.set_stroke_width(attrs.width);
paint.set_blend_mode(skia::BlendMode::SrcOver);
paint.set_stroke_join(skia::paint::Join::Miter); // Unión en pico
paint.set_stroke_miter(10.0); // Longitud de la muesca para picos más pronunciados
paint.set_stroke_cap(skia::paint::Cap::Butt); // Termina la línea sin agregar espacio adicional
paint
}
Stroke::CenterStroke(attrs) => {
let mut paint = attrs.fill.to_paint(rect);
paint.set_style(skia::PaintStyle::Stroke);
paint.set_stroke_width(attrs.width);
paint.set_blend_mode(skia::BlendMode::SrcOver);
paint.set_stroke_join(skia::paint::Join::Miter); // Unión en pico
paint.set_stroke_miter(10.0); // Longitud de la muesca para picos más pronunciados
paint.set_stroke_cap(skia::paint::Cap::Butt); // Termina la línea sin agregar espacio adicional
paint
}
Stroke::OuterStroke(attrs) => {
let mut paint = attrs.fill.to_paint(rect);
paint.set_style(skia::PaintStyle::Stroke);
paint.set_stroke_width(attrs.width);