decoder: Use image-rs, libheif-rs, librsvg

This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.

Closes #34, #40, #41, #49

Basis for fixing the following issues #7, #27, #36, #43, #68, #71
This commit is contained in:
Sophie Herold 2023-03-04 03:14:52 +01:00 • committed by Christopher Davis
parent 4d4bc4bb38
commit f80ec7db56
20 changed files with 3023 additions and 370 deletions

View file

@ -27,7 +27,6 @@ use gtk::CompositeTemplate;
use gtk_macros::spawn;
use once_cell::sync::Lazy;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
mod imp {
@ -52,9 +51,6 @@ mod imp {
pub(super) right_click_gesture: TemplateChild<gtk::GestureClick>,
#[template_child]
pub(super) press_gesture: TemplateChild<gtk::GestureLongPress>,
/// set if an error has occurred and is shown on error_page
pub(super) error: RefCell<Option<String>>,
}
#[glib::object_subclass]
@ -75,12 +71,9 @@ mod imp {
impl ObjectImpl for LpImagePage {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecObject::builder::<LpImage>("image")
.read_only()
.build(),
glib::ParamSpecString::builder("error").read_only().build(),
]
vec![glib::ParamSpecObject::builder::<LpImage>("image")
.read_only()
.build()]
});
PROPERTIES.as_ref()
@ -90,7 +83,6 @@ mod imp {
let obj = self.obj();
match pspec.name() {
"image" => obj.image().to_value(),
"error" => obj.error().to_value(),
name => unimplemented!("property {name}"),
}
}
@ -112,6 +104,30 @@ mod imp {
obj.show_popover_at(x, y);
gesture.set_state(gtk::EventSequenceState::Claimed);
}));
obj.image().connect_notify_local(
Some("is-loaded"),
clone!(@weak obj => move |_,_| {
if obj.image().is_loaded() {
obj.imp()
.stack
.set_visible_child(&*obj.imp().scrolled_window);
}
}),
);
obj.image().connect_notify_local(
Some("error"),
clone!(@weak obj => move |_,_| {
if obj.image().error().is_some() {
obj.imp().stack.set_visible_child(&*obj.imp().error_page);
}
}),
);
// Do not waste CPU on spinner if it is not visible
self.spinner.connect_map(|s| s.start());
self.spinner.connect_unmap(|s| s.stop());
}
}
@ -139,19 +155,7 @@ impl LpImagePage {
spawn!(clone!(@weak obj, @strong path => async move {
let imp = obj.imp();
match imp.image.load(&path).await {
Ok(()) => {
imp.stack.set_visible_child(&*imp.scrolled_window);
imp.spinner.set_spinning(false);
},
Err(e) => {
imp.error.replace(Some(e.to_string()));
imp.stack.set_visible_child(&*imp.error_page);
obj.notify("error");
imp.spinner.set_spinning(false);
log::error!("Could not load image: {e}");
}
}
imp.image.load(&path).await;
}));
obj
@ -169,10 +173,6 @@ impl LpImagePage {
self.imp().image.texture()
}
pub fn error(&self) -> Option<String> {
self.imp().error.borrow().clone()
}
pub fn content_provider(&self) -> Option<gdk::ContentProvider> {
self.imp().image.content_provider()
}