mirror of
https://github.com/penpot/penpot.git
synced 2025-07-19 01:57:20 +02:00
🐛 Fix wrong aspect ratio on oriented image
This commit is contained in:
parent
70f3988046
commit
833546d754
4 changed files with 71 additions and 90 deletions
|
@ -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,
|
||||
|
|
|
@ -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<u8>),
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -115,10 +115,6 @@ impl ImageFill {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (i32, i32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Uuid {
|
||||
self.id
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue