mirror of
https://github.com/penpot/penpot.git
synced 2025-01-02 04:40:09 -05:00
🐛 Fix path rotation
This commit is contained in:
parent
d4b829ed19
commit
e1c5cd6640
4 changed files with 110 additions and 76 deletions
|
@ -2,7 +2,7 @@ use crate::math;
|
|||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::render::BlendMode;
|
||||
use crate::render::{BlendMode, Renderable};
|
||||
|
||||
mod fills;
|
||||
mod images;
|
||||
|
@ -138,6 +138,21 @@ impl Shape {
|
|||
pub fn set_blend_mode(&mut self, mode: BlendMode) {
|
||||
self.blend_mode = mode;
|
||||
}
|
||||
|
||||
fn to_path_transform(&self) -> Option<skia::Matrix> {
|
||||
match self.kind {
|
||||
Kind::Path(_) => {
|
||||
let center = self.bounds().center();
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_translate(center);
|
||||
matrix.pre_concat(&self.transform.no_translation().to_skia_matrix().invert()?);
|
||||
matrix.pre_translate(-center);
|
||||
|
||||
Some(matrix)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,71 +1,3 @@
|
|||
use crate::math;
|
||||
use crate::shapes::Kind;
|
||||
use skia_safe as skia;
|
||||
|
||||
pub type Image = skia::Image;
|
||||
|
||||
pub fn draw_image_in_container(
|
||||
canvas: &skia::Canvas,
|
||||
image: &Image,
|
||||
size: (i32, i32),
|
||||
kind: &Kind,
|
||||
paint: &skia::Paint,
|
||||
) {
|
||||
let width = size.0 as f32;
|
||||
let height = size.1 as f32;
|
||||
let image_aspect_ratio = width / height;
|
||||
|
||||
let container = match kind {
|
||||
Kind::Rect(r) => r.to_owned(),
|
||||
Kind::Circle(r) => r.to_owned(),
|
||||
Kind::Path(p) => p.to_skia_path().bounds().to_owned(),
|
||||
};
|
||||
|
||||
// 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 widther, 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;
|
||||
|
||||
// Calculate offset to center the image in the container
|
||||
let offset_x = container.left + (container_width - scaled_width) / 2.0;
|
||||
let offset_y = container.top + (container_height - scaled_height) / 2.0;
|
||||
|
||||
let dest_rect = math::Rect::from_xywh(offset_x, offset_y, 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(), 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();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use skia_safe as skia;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Matrix {
|
||||
pub a: f32,
|
||||
pub b: f32,
|
||||
|
@ -47,6 +47,15 @@ impl Matrix {
|
|||
res
|
||||
}
|
||||
|
||||
pub fn no_translation(&self) -> Self {
|
||||
let mut res = Self::identity();
|
||||
res.c = self.c;
|
||||
res.b = self.b;
|
||||
res.a = self.a;
|
||||
res.d = self.d;
|
||||
res
|
||||
}
|
||||
|
||||
fn translation(&self) -> (f32, f32) {
|
||||
(self.e, self.f)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{draw_image_in_container, Fill, Kind, Shape};
|
||||
use super::{Fill, Image, Kind, Shape};
|
||||
use crate::math::Rect;
|
||||
use crate::render::{ImageStore, Renderable};
|
||||
|
||||
|
@ -10,7 +10,7 @@ impl Renderable for Shape {
|
|||
let transform = self.transform.to_skia_matrix();
|
||||
|
||||
// Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc
|
||||
let center = self.selrect.center();
|
||||
let center = self.bounds().center();
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_translate(center);
|
||||
matrix.pre_concat(&transform);
|
||||
|
@ -19,7 +19,14 @@ impl Renderable for Shape {
|
|||
surface.canvas().concat(&matrix);
|
||||
|
||||
for fill in self.fills().rev() {
|
||||
render_fill(surface, images, fill, self.selrect, &self.kind);
|
||||
render_fill(
|
||||
surface,
|
||||
images,
|
||||
fill,
|
||||
self.selrect,
|
||||
&self.kind,
|
||||
self.to_path_transform().as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
|
@ -60,6 +67,7 @@ fn render_fill(
|
|||
fill: &Fill,
|
||||
selrect: Rect,
|
||||
kind: &Kind,
|
||||
path_transform: Option<&skia::Matrix>,
|
||||
) {
|
||||
match (fill, kind) {
|
||||
(Fill::Image(image_fill), kind) => {
|
||||
|
@ -71,6 +79,8 @@ fn render_fill(
|
|||
image_fill.size(),
|
||||
kind,
|
||||
&fill.to_paint(&selrect),
|
||||
&selrect,
|
||||
path_transform,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -81,9 +91,77 @@ fn render_fill(
|
|||
surface.canvas().draw_oval(rect, &fill.to_paint(&selrect));
|
||||
}
|
||||
(_, Kind::Path(path)) => {
|
||||
surface
|
||||
.canvas()
|
||||
.draw_path(&path.to_skia_path(), &fill.to_paint(&selrect));
|
||||
surface.canvas().draw_path(
|
||||
&path.to_skia_path().transform(path_transform.unwrap()),
|
||||
&fill.to_paint(&selrect),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_image_in_container(
|
||||
canvas: &skia::Canvas,
|
||||
image: &Image,
|
||||
size: (i32, i32),
|
||||
kind: &Kind,
|
||||
paint: &skia::Paint,
|
||||
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;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue