From 833546d75477509129b101b56b88e1e025da628a Mon Sep 17 00:00:00 2001 From: Aitor Moreno Date: Fri, 20 Jun 2025 20:16:29 +0200 Subject: [PATCH] :bug: Fix wrong aspect ratio on oriented image --- render-wasm/src/render/fills.rs | 40 +++---------------- render-wasm/src/render/images.rs | 66 ++++++++++++++++++++++++++++--- render-wasm/src/render/strokes.rs | 51 +++--------------------- render-wasm/src/shapes/fills.rs | 4 -- 4 files changed, 71 insertions(+), 90 deletions(-) diff --git a/render-wasm/src/render/fills.rs b/render-wasm/src/render/fills.rs index 61fec8866c..634181b645 100644 --- a/render-wasm/src/render/fills.rs +++ b/render-wasm/src/render/fills.rs @@ -1,7 +1,7 @@ use skia_safe::{self as skia, Paint, RRect}; use super::{RenderState, SurfaceId}; -use crate::math::Rect as MathRect; +use crate::render::get_source_rect; use crate::shapes::{Fill, Frame, ImageFill, Rect, Shape, Type}; fn draw_image_fill( @@ -16,43 +16,13 @@ fn draw_image_fill( return; } - let size = image_fill.size(); + let size = image.unwrap().dimensions(); let canvas = render_state.surfaces.canvas(SurfaceId::Fills); let container = &shape.selrect; let path_transform = shape.to_path_transform(); - let width = size.0 as f32; - let height = size.1 as f32; - - // Container size - let container_width = container.width(); - let container_height = container.height(); - - let mut scaled_width = container_width; - let mut scaled_height = container_height; - - if image_fill.keep_aspect_ratio() { - // Calculate scale to ensure the image covers the container - let image_aspect_ratio = width / height; - let container_aspect_ratio = container_width / container_height; - 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 - scaled_width = width * scale; - scaled_height = height * scale; - } - - let dest_rect = MathRect::from_xywh( - container.left - (scaled_width - container_width) / 2.0, - container.top - (scaled_height - container_height) / 2.0, - scaled_width, - scaled_height, - ); + let src_rect = get_source_rect(size, container, image_fill); + let dest_rect = container; // Save the current canvas state canvas.save(); @@ -99,7 +69,7 @@ fn draw_image_fill( if let Some(image) = image { canvas.draw_image_rect_with_sampling_options( image, - None, + Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)), dest_rect, render_state.sampling_options, paint, diff --git a/render-wasm/src/render/images.rs b/render-wasm/src/render/images.rs index 90f2674c63..0b3e2283f0 100644 --- a/render-wasm/src/render/images.rs +++ b/render-wasm/src/render/images.rs @@ -1,12 +1,57 @@ use crate::math::Rect as MathRect; +use crate::shapes::ImageFill; use crate::uuid::Uuid; -use skia_safe as skia; use skia_safe::gpu::{surfaces, Budgeted, DirectContext}; +use skia_safe::{self as skia, Codec, ISize}; use std::collections::HashMap; pub type Image = skia::Image; +pub fn get_dest_rect(container: &MathRect, delta: f32) -> MathRect { + MathRect::from_ltrb( + container.left - delta, + container.top - delta, + container.right + delta, + container.bottom + delta, + ) +} + +pub fn get_source_rect(size: ISize, container: &MathRect, image_fill: &ImageFill) -> MathRect { + let image_width = size.width as f32; + let image_height = size.height as f32; + + // Container size + let container_width = container.width(); + let container_height = container.height(); + + let mut source_width = image_width; + let mut source_height = image_height; + let mut source_x = 0.; + let mut source_y = 0.; + + let source_scale_y = image_height / container_height; + let source_scale_x = image_width / container_width; + + if image_fill.keep_aspect_ratio() { + // Calculate scale to ensure the image covers the container + let image_aspect_ratio = image_width / image_height; + let container_aspect_ratio = container_width / container_height; + + if image_aspect_ratio > container_aspect_ratio { + // Image is taller, scale based on width to cover container + source_width = container_width * source_scale_y; + source_x = (image_width - source_width) / 2.0; + } else { + // Image is wider, scale based on height to cover container + source_height = container_height * source_scale_x; + source_y = (image_height - source_height) / 2.0; + }; + } + + MathRect::from_xywh(source_x, source_y, source_width, source_height) +} + enum StoredImage { Raw(Vec), Gpu(Image), @@ -47,12 +92,16 @@ impl ImageStore { StoredImage::Raw(raw_data) => { // Decode and upload to GPU let data = unsafe { skia::Data::new_bytes(raw_data) }; - let image = Image::from_encoded(data)?; + let codec = Codec::from_data(data.clone())?; + let image = Image::from_encoded(data.clone())?; - let width = image.width(); - let height = image.height(); + let mut dimensions = codec.dimensions(); + if codec.origin().swaps_width_height() { + dimensions.width = codec.dimensions().height; + dimensions.height = codec.dimensions().width; + } - let image_info = skia::ImageInfo::new_n32_premul((width, height), None); + let image_info = skia::ImageInfo::new_n32_premul(dimensions, None); let mut surface = surfaces::render_target( &mut self.context, @@ -65,7 +114,12 @@ impl ImageStore { false, )?; - let dest_rect = MathRect::from_xywh(0.0, 0.0, width as f32, height as f32); + let dest_rect: MathRect = MathRect::from_xywh( + 0.0, + 0.0, + dimensions.width as f32, + dimensions.height as f32, + ); surface.canvas().draw_image_rect( &image, diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index 0d32a937e4..824aeb1237 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -7,6 +7,7 @@ use skia_safe::{self as skia, textlayout::Paragraph, ImageFilter, RRect}; use super::{RenderState, SurfaceId}; use crate::render::text::{self}; +use crate::render::{get_dest_rect, get_source_rect}; // FIXME: See if we can simplify these arguments #[allow(clippy::too_many_arguments)] @@ -345,42 +346,6 @@ fn draw_triangle_cap( canvas.draw_path(&path, paint); } -fn calculate_scaled_rect( - size: (i32, i32), - container: &Rect, - delta: f32, - keep_aspect_ratio: bool, -) -> Rect { - let (width, height) = (size.0 as f32, size.1 as f32); - - // Container size - let container_width = container.width(); - let container_height = container.height(); - - let mut scaled_width = container_width; - let mut scaled_height = container_height; - - if keep_aspect_ratio { - let image_aspect_ratio = width / height; - let container_aspect_ratio = container_width / container_height; - let scale = if image_aspect_ratio > container_aspect_ratio { - container_height / height - } else { - container_width / width - }; - - scaled_width = width * scale; - scaled_height = height * scale; - } - - Rect::from_xywh( - container.left - delta - (scaled_width - container_width) / 2.0, - container.top - delta - (scaled_height - container_height) / 2.0, - scaled_width + (2. * delta) + (scaled_width - container_width), - scaled_height + (2. * delta) + (scaled_width - container_width), - ) -} - fn draw_image_stroke_in_container( render_state: &mut RenderState, shape: &Shape, @@ -394,7 +359,7 @@ fn draw_image_stroke_in_container( return; } - let size = image_fill.size(); + let size = image.unwrap().dimensions(); let canvas = render_state.surfaces.canvas(SurfaceId::Strokes); let container = &shape.selrect; let path_transform = shape.to_path_transform(); @@ -485,17 +450,13 @@ fn draw_image_stroke_in_container( image_paint.set_blend_mode(skia::BlendMode::SrcIn); image_paint.set_anti_alias(antialias); - // Compute scaled rect and clip to it - let dest_rect = calculate_scaled_rect( - size, - container, - stroke.delta(), - image_fill.keep_aspect_ratio(), - ); + let src_rect = get_source_rect(size, container, image_fill); + let dest_rect = get_dest_rect(container, stroke.delta()); + canvas.clip_rect(dest_rect, skia::ClipOp::Intersect, antialias); canvas.draw_image_rect_with_sampling_options( image.unwrap(), - None, + Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)), dest_rect, render_state.sampling_options, &image_paint, diff --git a/render-wasm/src/shapes/fills.rs b/render-wasm/src/shapes/fills.rs index 9b282fd60d..3c90a625f8 100644 --- a/render-wasm/src/shapes/fills.rs +++ b/render-wasm/src/shapes/fills.rs @@ -115,10 +115,6 @@ impl ImageFill { } } - pub fn size(&self) -> (i32, i32) { - (self.width, self.height) - } - pub fn id(&self) -> Uuid { self.id }