image: Limit SVG zoom to RSVG limits

See <https://gitlab.gnome.org/GNOME/librsvg/-/issues/938>
for details about the problem.

Closes #88
This commit is contained in:
Sophie Herold 2023-03-09 21:27:51 +01:00 • committed by Christopher Davis
parent dfebfb7470
commit a0ce234946
4 changed files with 38 additions and 12 deletions

View file

@ -4,6 +4,6 @@ mod svg;
pub use heif::Heif;
pub use image_rs_other::ImageRsOther;
pub use svg::Svg;
pub use svg::{Svg, RSVG_MAX_SIZE};
use super::{Decoder, UpdateSender};

View file

@ -11,6 +11,10 @@ use gtk::prelude::*;
use std::path::PathBuf;
use std::sync::Arc;
/// Current librsvg limit on maximum dimensions. See
/// <https://gitlab.gnome.org/GNOME/librsvg/-/issues/938>
pub const RSVG_MAX_SIZE: u32 = 32_767;
#[derive(Debug)]
pub struct Svg {
thread_handle: std::thread::JoinHandle<()>,
@ -106,9 +110,7 @@ impl Svg {
tile_instructions.tiling.scaled_dimensions();
// librsvg does not currently support larger images
//
// TODO: We are only creating tiles, maybe this check in librsvg is wrong?
if total_height > 32_767 || total_width > 32_767 {
if total_height > RSVG_MAX_SIZE || total_width > RSVG_MAX_SIZE {
continue;
}

View file

@ -15,6 +15,8 @@ use gtk::graphene;
use std::io::Read;
use std::sync::Arc;
pub use formats::RSVG_MAX_SIZE;
#[derive(Clone, Copy, Debug)]
pub enum ImageFormat {
ImageRs(image_rs::ImageFormat),

View file

@ -87,6 +87,7 @@ mod imp {
pub file_monitor: RefCell<Option<gio::FileMonitor>>,
pub tiles: Arc<ArcSwap<tiling::TilingStore>>,
pub decoder: RefCell<Option<Arc<Decoder>>>,
pub format: RefCell<Option<decoder::ImageFormat>>,
/// Set to true when image is ready for displaying
pub is_loaded: Cell<bool>,
@ -710,8 +711,8 @@ impl LpImage {
DecoderUpdate::Error(err) => {
self.set_error(err);
}
DecoderUpdate::Format(_) => {
// TODO: Store and use in image properties
DecoderUpdate::Format(format) => {
imp.format.replace(Some(format));
}
}
}
@ -961,14 +962,29 @@ impl LpImage {
decoder::tiling::zoom_normalize(self.zoom())
}
/// Maximal zoom allowed for this image
fn max_zoom(&self) -> f64 {
if let Some(decoder::ImageFormat::Svg) = dbg!(self.format()) {
let (width, height) = self.original_dimensions();
// Avoid division by 0
let long_side = f64::max(1., i32::max(width, height) as f64);
// Limit to maz size supported by rsvg
f64::min(MAX_ZOOM_LEVEL, decoder::RSVG_MAX_SIZE as f64 / long_side)
} else {
MAX_ZOOM_LEVEL
}
}
/// Set zoom level aiming for given position or center if not available
fn set_zoom_aiming(&self, mut zoom: f64, aiming: Option<(f64, f64)>) {
let max_zoom = self.max_zoom();
// allow some deviation from max value for rubberbanding
if zoom > MAX_ZOOM_LEVEL {
let max_deviation = MAX_ZOOM_LEVEL * ZOOM_FACTOR_MAX_RUBBERBAND;
let deviation = zoom / MAX_ZOOM_LEVEL;
if zoom > max_zoom {
let max_deviation = max_zoom * ZOOM_FACTOR_MAX_RUBBERBAND;
let deviation = zoom / max_zoom;
zoom = f64::min(
MAX_ZOOM_LEVEL * deviation.powf(RUBBERBANDING_EXPONENT),
max_zoom * deviation.powf(RUBBERBANDING_EXPONENT),
max_deviation,
);
}
@ -1136,8 +1152,9 @@ impl LpImage {
/// Zoom to specific level with animation
pub fn zoom_to(&self, mut zoom: f64) {
if zoom >= MAX_ZOOM_LEVEL {
zoom = MAX_ZOOM_LEVEL;
let max_zoom = self.max_zoom();
if zoom >= max_zoom {
zoom = max_zoom;
self.set_max_zoom(true);
} else {
self.set_max_zoom(false);
@ -1413,6 +1430,11 @@ impl LpImage {
Some(gdk::ContentProvider::for_value(&list.to_value()))
}
/// Image format
pub fn format(&self) -> Option<decoder::ImageFormat> {
*self.imp().format.borrow()
}
/// Returns decoding error if one occured
pub fn error(&self) -> Option<String> {
self.imp().error.borrow().clone()