From e1c5cd6640972e1c4d2db27373ae93b1d211eb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Fri, 13 Dec 2024 13:03:23 +0100 Subject: [PATCH] :bug: Fix path rotation --- render-wasm/src/shapes.rs | 17 +++++- render-wasm/src/shapes/images.rs | 68 --------------------- render-wasm/src/shapes/matrix.rs | 11 +++- render-wasm/src/shapes/renderable.rs | 90 ++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 76 deletions(-) diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 78ff1f413..1b0cb4529 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -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 { + 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)] diff --git a/render-wasm/src/shapes/images.rs b/render-wasm/src/shapes/images.rs index 50980027a..bbf5d600c 100644 --- a/render-wasm/src/shapes/images.rs +++ b/render-wasm/src/shapes/images.rs @@ -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(); -} diff --git a/render-wasm/src/shapes/matrix.rs b/render-wasm/src/shapes/matrix.rs index bd353b910..294b9da06 100644 --- a/render-wasm/src/shapes/matrix.rs +++ b/render-wasm/src/shapes/matrix.rs @@ -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) } diff --git a/render-wasm/src/shapes/renderable.rs b/render-wasm/src/shapes/renderable.rs index ef124c272..907489593 100644 --- a/render-wasm/src/shapes/renderable.rs +++ b/render-wasm/src/shapes/renderable.rs @@ -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(); +}