decoder: Use GFile instead of PathBuf
This allows to show images from locations that don't have a representation with path. This also works around not being able to read files in trash:/// Closes #56 Part-of: <https://gitlab.gnome.org/Incubator/loupe/-/merge_requests/174>
This commit is contained in:
parent
032707a210
commit
fad8bb7939
15 changed files with 119 additions and 107 deletions
|
|
@ -3,14 +3,14 @@ use super::*;
|
|||
use crate::decoder::tiling::{self, FrameBufferExt};
|
||||
use crate::deps::*;
|
||||
use crate::util::gettext::*;
|
||||
use gtk::prelude::*;
|
||||
use crate::util::ToBufRead;
|
||||
|
||||
use anyhow::Context;
|
||||
use arc_swap::ArcSwap;
|
||||
use gtk::prelude::*;
|
||||
use libheif_rs::{ColorSpace, HeifContext, LibHeif, Plane, RgbChroma};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -21,14 +21,24 @@ static LIBHEIF: Lazy<LibHeif> = Lazy::new(LibHeif::new);
|
|||
|
||||
impl Heif {
|
||||
pub fn new(
|
||||
path: PathBuf,
|
||||
file: gio::File,
|
||||
updater: UpdateSender,
|
||||
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
|
||||
) -> Self {
|
||||
log::debug!("Loading HEIF");
|
||||
updater.spawn_error_handled(move || {
|
||||
let ctx = HeifContext::read_from_file(&path.display().to_string())
|
||||
.context(gettext("Failed to read image"))?;
|
||||
let file_size = file
|
||||
.query_info(
|
||||
gio::FILE_ATTRIBUTE_STANDARD_SIZE,
|
||||
gio::FileQueryInfoFlags::NONE,
|
||||
gio::Cancellable::NONE,
|
||||
)
|
||||
.context(gettext("Failed to read image file information"))?
|
||||
.size();
|
||||
let buf_reader = file.to_buf_read()?;
|
||||
let stream_reader = libheif_rs::StreamReader::new(buf_reader, file_size as u64);
|
||||
let ctx = HeifContext::read_from_reader(Box::new(stream_reader))
|
||||
.context(gettext("Failed to decode image"))?;
|
||||
let handle = ctx.primary_image_handle()?;
|
||||
|
||||
tiles.set_original_dimensions((handle.width(), handle.height()));
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@ use super::*;
|
|||
use crate::decoder::tiling::{self, FrameBufferExt};
|
||||
use crate::deps::*;
|
||||
use crate::util::gettext::*;
|
||||
use crate::util::{BufReadSeek, ToBufRead};
|
||||
|
||||
use anyhow::Context;
|
||||
use arc_swap::ArcSwap;
|
||||
use gtk::prelude::*;
|
||||
use image_rs::AnimationDecoder;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -32,17 +30,17 @@ impl Drop for ImageRsOther {
|
|||
|
||||
impl ImageRsOther {
|
||||
fn reader(
|
||||
path: &Path,
|
||||
file: &gio::File,
|
||||
format: image_rs::ImageFormat,
|
||||
) -> anyhow::Result<image_rs::io::Reader<BufReader<File>>> {
|
||||
let mut reader =
|
||||
image_rs::io::Reader::open(path).context(gettext("Failed to open image"))?;
|
||||
) -> anyhow::Result<image_rs::io::Reader<Box<dyn BufReadSeek>>> {
|
||||
let buf_read = file.to_buf_read()?;
|
||||
let mut reader = image_rs::io::Reader::new(buf_read);
|
||||
reader.set_format(format);
|
||||
Ok(reader)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
path: PathBuf,
|
||||
file: gio::File,
|
||||
format: image_rs::ImageFormat,
|
||||
updater: UpdateSender,
|
||||
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
|
||||
|
|
@ -53,14 +51,14 @@ impl ImageRsOther {
|
|||
image_rs::ImageFormat::Gif
|
||||
| image_rs::ImageFormat::WebP
|
||||
| image_rs::ImageFormat::Png => Some(Self::new_animated(
|
||||
path,
|
||||
file,
|
||||
format,
|
||||
updater,
|
||||
tiles,
|
||||
abort.clone(),
|
||||
)),
|
||||
_ => {
|
||||
Self::new_static(path, format, updater, tiles);
|
||||
Self::new_static(file, format, updater, tiles);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
|
@ -72,20 +70,20 @@ impl ImageRsOther {
|
|||
}
|
||||
|
||||
fn new_static(
|
||||
path: PathBuf,
|
||||
file: gio::File,
|
||||
format: image_rs::ImageFormat,
|
||||
updater: UpdateSender,
|
||||
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
|
||||
) {
|
||||
updater.spawn_error_handled(move || {
|
||||
let reader = Self::reader(&path, format)?;
|
||||
let reader = Self::reader(&file, format)?;
|
||||
let dimensions = reader
|
||||
.into_dimensions()
|
||||
.context("Failed to read image dimensions")?;
|
||||
|
||||
tiles.set_original_dimensions(dimensions);
|
||||
|
||||
let mut reader = Self::reader(&path, format)?;
|
||||
let mut reader = Self::reader(&file, format)?;
|
||||
|
||||
reader.limits(image_rs::io::Limits::no_limits());
|
||||
|
||||
|
|
@ -107,7 +105,7 @@ impl ImageRsOther {
|
|||
}
|
||||
|
||||
fn new_animated(
|
||||
path: PathBuf,
|
||||
file: gio::File,
|
||||
format: image_rs::ImageFormat,
|
||||
updater: UpdateSender,
|
||||
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
|
||||
|
|
@ -119,30 +117,30 @@ impl ImageRsOther {
|
|||
// We are currently decoding for each repetition of the animation
|
||||
// TODO: Check if/how that can be solved differently
|
||||
loop {
|
||||
let file = std::fs::File::open(&path)?;
|
||||
let buf_reader = file.to_buf_read()?;
|
||||
|
||||
let (frames, animated_format) = match format {
|
||||
image_rs::ImageFormat::Gif => (
|
||||
image_rs::codecs::gif::GifDecoder::new(file)?.into_frames(),
|
||||
image_rs::codecs::gif::GifDecoder::new(buf_reader)?.into_frames(),
|
||||
ImageFormat::AnimatedGif,
|
||||
),
|
||||
image_rs::ImageFormat::WebP => {
|
||||
let decoder = image_rs::codecs::webp::WebPDecoder::new(file)?;
|
||||
let decoder = image_rs::codecs::webp::WebPDecoder::new(buf_reader)?;
|
||||
if decoder.has_animation() {
|
||||
(decoder.into_frames(), ImageFormat::AnimatedWebP)
|
||||
} else {
|
||||
// Static WebP images need a different decoder
|
||||
Self::new_static(path, format, updater, tiles);
|
||||
Self::new_static(file, format, updater, tiles);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
image_rs::ImageFormat::Png => {
|
||||
let decoder = image_rs::codecs::png::PngDecoder::new(file)?;
|
||||
let decoder = image_rs::codecs::png::PngDecoder::new(buf_reader)?;
|
||||
if decoder.is_apng() {
|
||||
(decoder.apng().into_frames(), ImageFormat::AnimatedPng)
|
||||
} else {
|
||||
// Static PNG images need a different decoder
|
||||
Self::new_static(path, format, updater, tiles);
|
||||
Self::new_static(file, format, updater, tiles);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use anyhow::Context;
|
|||
use arc_swap::ArcSwap;
|
||||
use gtk::prelude::*;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Current librsvg limit on maximum dimensions. See
|
||||
|
|
@ -45,7 +44,7 @@ impl Drop for Svg {
|
|||
|
||||
impl Svg {
|
||||
pub fn new(
|
||||
path: PathBuf,
|
||||
file: gio::File,
|
||||
updater: UpdateSender,
|
||||
tiles: Arc<ArcSwap<tiling::FrameBuffer>>,
|
||||
) -> Self {
|
||||
|
|
@ -53,7 +52,7 @@ impl Svg {
|
|||
let request_store = current_request.clone();
|
||||
|
||||
let thread_handle = updater.spawn_error_handled(move || {
|
||||
let handle = rsvg::Loader::new().read_path(path)?;
|
||||
let handle = rsvg::Loader::new().read_file(&file, gio::Cancellable::NONE)?;
|
||||
let renderer = rsvg::CairoRenderer::new(&handle);
|
||||
|
||||
let (original_width, original_height) = svg_dimensions(&renderer);
|
||||
|
|
@ -85,7 +84,7 @@ impl Svg {
|
|||
tile_size: tiling::TILE_SIZE,
|
||||
original_dimensions: (original_width, original_height),
|
||||
zoom: tile_request.zoom,
|
||||
bleed: 2,
|
||||
bleed: 0,
|
||||
};
|
||||
|
||||
let relevant_tiles = tiling.relevant_tiles(&tile_request.viewport);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue