mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-03 10:23:32 +00:00
Fix the rendering size of svg
- On the web, to return the image size, we need to use the natural size
of the image, and not its dom size, as the later get modified since
commit b727aba4a0
- The target size did not take in account the image fit, that's because
former version of resvg could only render by respecting the aspect
ratio. But since the web don't have this limitation, we now need to
take it into account. And new version of resvg can also scale with
any aspect ratio
This commit is contained in:
parent
1fcbae5309
commit
2e08b7dd1e
8 changed files with 56 additions and 31 deletions
|
@ -1150,25 +1150,32 @@ impl QtItemRenderer<'_> {
|
|||
debug_assert!(target_height.get() > 0.);
|
||||
|
||||
let pixmap: qttypes::QPixmap = self.cache.get_or_update_cache_entry(item_rc, || {
|
||||
// Query target_width/height here again to ensure that changes will invalidate the item rendering cache.
|
||||
let target_width = target_width.get() as f64;
|
||||
let target_height = target_height.get() as f64;
|
||||
let source = source_property.get();
|
||||
let origin = source.size();
|
||||
let source: &ImageInner = (&source).into();
|
||||
|
||||
let has_source_clipping = source_rect.map_or(false, |rect| {
|
||||
rect.is_valid()
|
||||
&& (rect.x != 0.
|
||||
|| rect.y != 0.
|
||||
|| !rect.width.approx_eq(&target_width)
|
||||
|| !rect.height.approx_eq(&target_height))
|
||||
});
|
||||
let source_size = if !has_source_clipping {
|
||||
Some(euclid::size2(target_width as u32, target_height as u32))
|
||||
// Query target_width/height here again to ensure that changes will invalidate the item rendering cache.
|
||||
let t = euclid::size2(target_width.get(), target_height.get()).cast::<f64>();
|
||||
|
||||
let source_size = if source.is_svg() {
|
||||
let has_source_clipping = source_rect.map_or(false, |rect| {
|
||||
rect.is_valid()
|
||||
&& (rect.x != 0.
|
||||
|| rect.y != 0.
|
||||
|| !rect.width.approx_eq(&t.width)
|
||||
|| !rect.height.approx_eq(&t.height))
|
||||
});
|
||||
if has_source_clipping {
|
||||
// Source size & clipping is not implemented yet
|
||||
None
|
||||
} else {
|
||||
Some(i_slint_core::graphics::fit_size(image_fit, t.cast(), origin).cast())
|
||||
}
|
||||
} else {
|
||||
// Source size & clipping is not implemented yet
|
||||
None
|
||||
};
|
||||
|
||||
image_to_pixmap((&source_property.get()).into(), source_size).map_or_else(
|
||||
image_to_pixmap(source, source_size).map_or_else(
|
||||
Default::default,
|
||||
|mut pixmap: qttypes::QPixmap| {
|
||||
let colorize = colorize_property.map_or(Brush::default(), |c| c.get());
|
||||
|
|
|
@ -1178,11 +1178,12 @@ impl<'a> GLItemRenderer<'a> {
|
|||
let target_size_for_scalable_source = image_inner.is_svg().then(|| {
|
||||
// get the scale factor as a property again, to ensure the cache is invalidated when the scale factor changes
|
||||
let scale_factor = ScaleFactor::new(self.window.scale_factor());
|
||||
PhysicalSize::from_lengths(
|
||||
LogicalLength::new(target_width.get()) * scale_factor,
|
||||
LogicalLength::new(target_height.get()) * scale_factor,
|
||||
)
|
||||
.cast()
|
||||
let t = LogicalSize::from_lengths(
|
||||
LogicalLength::new(target_width.get()),
|
||||
LogicalLength::new(target_height.get()),
|
||||
) * scale_factor;
|
||||
|
||||
i_slint_core::graphics::fit_size(image_fit, t, image.size()).cast()
|
||||
});
|
||||
|
||||
TextureCacheKey::new(image_inner, target_size_for_scalable_source, image_rendering)
|
||||
|
|
|
@ -5,6 +5,7 @@ use i_slint_core::graphics::{
|
|||
cache as core_cache, Image, ImageCacheKey, ImageInner, IntSize, OpaqueImage, OpaqueImageVTable,
|
||||
SharedImageBuffer,
|
||||
};
|
||||
use i_slint_core::items::ImageFit;
|
||||
use i_slint_core::lengths::{LogicalSize, ScaleFactor};
|
||||
|
||||
struct SkiaCachedImage {
|
||||
|
@ -30,6 +31,7 @@ pub(crate) fn as_skia_image(
|
|||
image: Image,
|
||||
target_width: std::pin::Pin<&i_slint_core::Property<f32>>,
|
||||
target_height: std::pin::Pin<&i_slint_core::Property<f32>>,
|
||||
image_fit: ImageFit,
|
||||
scale_factor: ScaleFactor,
|
||||
) -> Option<skia_safe::Image> {
|
||||
let image_inner: &ImageInner = (&image).into();
|
||||
|
@ -51,6 +53,7 @@ pub(crate) fn as_skia_image(
|
|||
// Query target_width/height here again to ensure that changes will invalidate the item rendering cache.
|
||||
let target_size =
|
||||
LogicalSize::new(target_width.get(), target_height.get()) * scale_factor;
|
||||
let target_size = i_slint_core::graphics::fit_size(image_fit, target_size, svg.size());
|
||||
let pixels = match svg.render(target_size.cast()).ok()? {
|
||||
SharedImageBuffer::RGB8(_) => unreachable!(),
|
||||
SharedImageBuffer::RGBA8(_) => unreachable!(),
|
||||
|
|
|
@ -153,6 +153,7 @@ impl<'a> SkiaRenderer<'a> {
|
|||
image,
|
||||
target_width,
|
||||
target_height,
|
||||
image_fit,
|
||||
self.scale_factor,
|
||||
)
|
||||
.and_then(|skia_image| {
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::slice::Slice;
|
|||
use crate::{SharedString, SharedVector};
|
||||
|
||||
use super::{IntRect, IntSize};
|
||||
use crate::items::ImageFit;
|
||||
|
||||
#[cfg(feature = "image-decoders")]
|
||||
pub mod cache;
|
||||
|
@ -664,6 +665,21 @@ fn test_image_size_from_buffer_without_backend() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return an size that can be used to render an image in a buffer that matches a given ImageFit
|
||||
pub fn fit_size(
|
||||
image_fit: ImageFit,
|
||||
target: euclid::Size2D<f32, PhysicalPx>,
|
||||
origin: IntSize,
|
||||
) -> euclid::Size2D<f32, PhysicalPx> {
|
||||
let o = origin.cast::<f32>();
|
||||
let ratio = match image_fit {
|
||||
ImageFit::Fill => return target,
|
||||
ImageFit::Contain => f32::min(target.width / o.width, target.height / o.height),
|
||||
ImageFit::Cover => f32::max(target.width / o.width, target.height / o.height),
|
||||
};
|
||||
euclid::Size2D::from_untyped(o * ratio)
|
||||
}
|
||||
|
||||
#[cfg(feature = "ffi")]
|
||||
pub(crate) mod ffi {
|
||||
#![allow(unsafe_code)]
|
||||
|
|
|
@ -46,7 +46,10 @@ impl HTMLImage {
|
|||
pub fn size(&self) -> Option<IntSize> {
|
||||
match self.image_load_pending.as_ref().get() {
|
||||
true => None,
|
||||
false => Some(IntSize::new(self.dom_element.width(), self.dom_element.height())),
|
||||
false => Some(IntSize::new(
|
||||
self.dom_element.natural_width(),
|
||||
self.dom_element.natural_height(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,9 +45,7 @@ impl ParsedSVG {
|
|||
size: euclid::Size2D<u32, PhysicalPx>,
|
||||
) -> Result<SharedImageBuffer, usvg::Error> {
|
||||
let tree = &self.svg_tree;
|
||||
// resvg doesn't support scaling to width/height, just fit to width.
|
||||
// FIXME: the fit should actually depends on the image-fit property
|
||||
let fit = usvg::FitTo::Width(size.width);
|
||||
let fit = usvg::FitTo::Size(size.width, size.height);
|
||||
let size =
|
||||
fit.fit_to(tree.svg_node().size.to_screen_size()).ok_or(usvg::Error::InvalidSize)?;
|
||||
let mut buffer = SharedPixelBuffer::new(size.width(), size.height());
|
||||
|
|
|
@ -939,13 +939,9 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
let img_src_size = source.size().cast::<f32>();
|
||||
let img_src_size = source.size();
|
||||
if let Some(buffer) = image_inner.render_to_buffer(Some(
|
||||
euclid::size2(
|
||||
phys_size.width * img_src_size.width / size.width as f32,
|
||||
phys_size.height * img_src_size.height / size.height as f32,
|
||||
)
|
||||
.cast(),
|
||||
crate::graphics::fit_size(image_fit, phys_size, img_src_size).cast(),
|
||||
)) {
|
||||
if let Some(clipped_relative_source_rect) = renderer_clip_in_source_rect_space
|
||||
.intersection(&euclid::rect(
|
||||
|
@ -971,8 +967,8 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> {
|
|||
.to_vector(),
|
||||
)
|
||||
.scale(
|
||||
buf_size.width / img_src_size.width,
|
||||
buf_size.height / img_src_size.height,
|
||||
buf_size.width / img_src_size.width as f32,
|
||||
buf_size.height / img_src_size.height as f32,
|
||||
)
|
||||
.cast(),
|
||||
colorize,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue