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:
parent
dfebfb7470
commit
a0ce234946
4 changed files with 38 additions and 12 deletions
|
|
@ -4,6 +4,6 @@ mod svg;
|
||||||
|
|
||||||
pub use heif::Heif;
|
pub use heif::Heif;
|
||||||
pub use image_rs_other::ImageRsOther;
|
pub use image_rs_other::ImageRsOther;
|
||||||
pub use svg::Svg;
|
pub use svg::{Svg, RSVG_MAX_SIZE};
|
||||||
|
|
||||||
use super::{Decoder, UpdateSender};
|
use super::{Decoder, UpdateSender};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ use gtk::prelude::*;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Svg {
|
pub struct Svg {
|
||||||
thread_handle: std::thread::JoinHandle<()>,
|
thread_handle: std::thread::JoinHandle<()>,
|
||||||
|
|
@ -106,9 +110,7 @@ impl Svg {
|
||||||
tile_instructions.tiling.scaled_dimensions();
|
tile_instructions.tiling.scaled_dimensions();
|
||||||
|
|
||||||
// librsvg does not currently support larger images
|
// librsvg does not currently support larger images
|
||||||
//
|
if total_height > RSVG_MAX_SIZE || total_width > RSVG_MAX_SIZE {
|
||||||
// TODO: We are only creating tiles, maybe this check in librsvg is wrong?
|
|
||||||
if total_height > 32_767 || total_width > 32_767 {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ use gtk::graphene;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub use formats::RSVG_MAX_SIZE;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum ImageFormat {
|
pub enum ImageFormat {
|
||||||
ImageRs(image_rs::ImageFormat),
|
ImageRs(image_rs::ImageFormat),
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ mod imp {
|
||||||
pub file_monitor: RefCell<Option<gio::FileMonitor>>,
|
pub file_monitor: RefCell<Option<gio::FileMonitor>>,
|
||||||
pub tiles: Arc<ArcSwap<tiling::TilingStore>>,
|
pub tiles: Arc<ArcSwap<tiling::TilingStore>>,
|
||||||
pub decoder: RefCell<Option<Arc<Decoder>>>,
|
pub decoder: RefCell<Option<Arc<Decoder>>>,
|
||||||
|
pub format: RefCell<Option<decoder::ImageFormat>>,
|
||||||
|
|
||||||
/// Set to true when image is ready for displaying
|
/// Set to true when image is ready for displaying
|
||||||
pub is_loaded: Cell<bool>,
|
pub is_loaded: Cell<bool>,
|
||||||
|
|
@ -710,8 +711,8 @@ impl LpImage {
|
||||||
DecoderUpdate::Error(err) => {
|
DecoderUpdate::Error(err) => {
|
||||||
self.set_error(err);
|
self.set_error(err);
|
||||||
}
|
}
|
||||||
DecoderUpdate::Format(_) => {
|
DecoderUpdate::Format(format) => {
|
||||||
// TODO: Store and use in image properties
|
imp.format.replace(Some(format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -961,14 +962,29 @@ impl LpImage {
|
||||||
decoder::tiling::zoom_normalize(self.zoom())
|
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
|
/// Set zoom level aiming for given position or center if not available
|
||||||
fn set_zoom_aiming(&self, mut zoom: f64, aiming: Option<(f64, f64)>) {
|
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
|
// allow some deviation from max value for rubberbanding
|
||||||
if zoom > MAX_ZOOM_LEVEL {
|
if zoom > max_zoom {
|
||||||
let max_deviation = MAX_ZOOM_LEVEL * ZOOM_FACTOR_MAX_RUBBERBAND;
|
let max_deviation = max_zoom * ZOOM_FACTOR_MAX_RUBBERBAND;
|
||||||
let deviation = zoom / MAX_ZOOM_LEVEL;
|
let deviation = zoom / max_zoom;
|
||||||
zoom = f64::min(
|
zoom = f64::min(
|
||||||
MAX_ZOOM_LEVEL * deviation.powf(RUBBERBANDING_EXPONENT),
|
max_zoom * deviation.powf(RUBBERBANDING_EXPONENT),
|
||||||
max_deviation,
|
max_deviation,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1136,8 +1152,9 @@ impl LpImage {
|
||||||
|
|
||||||
/// Zoom to specific level with animation
|
/// Zoom to specific level with animation
|
||||||
pub fn zoom_to(&self, mut zoom: f64) {
|
pub fn zoom_to(&self, mut zoom: f64) {
|
||||||
if zoom >= MAX_ZOOM_LEVEL {
|
let max_zoom = self.max_zoom();
|
||||||
zoom = MAX_ZOOM_LEVEL;
|
if zoom >= max_zoom {
|
||||||
|
zoom = max_zoom;
|
||||||
self.set_max_zoom(true);
|
self.set_max_zoom(true);
|
||||||
} else {
|
} else {
|
||||||
self.set_max_zoom(false);
|
self.set_max_zoom(false);
|
||||||
|
|
@ -1413,6 +1430,11 @@ impl LpImage {
|
||||||
Some(gdk::ContentProvider::for_value(&list.to_value()))
|
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
|
/// Returns decoding error if one occured
|
||||||
pub fn error(&self) -> Option<String> {
|
pub fn error(&self) -> Option<String> {
|
||||||
self.imp().error.borrow().clone()
|
self.imp().error.borrow().clone()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue