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

@ -0,0 +1,156 @@
///! Decode using image-rs
use super::*;
use crate::decoder::tiling::{self, TilingStoreExt};
use crate::deps::*;
use anyhow::Context;
use arc_swap::ArcSwap;
use gtk::prelude::*;
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Debug)]
pub struct ImageRsOther;
impl ImageRsOther {
pub fn new(
path: PathBuf,
updater: UpdateSender,
tiles: Arc<ArcSwap<tiling::TilingStore>>,
) -> Self {
updater.spawn_error_handled(move || {
let dimensions = image_rs::image_dimensions(&path).context("Failed to read image")?;
tiles.set_original_dimensions(dimensions);
let mut reader = image_rs::io::Reader::open(&path).context("Failed to open image")?;
// TODO: Set something huge?
reader.limits(image_rs::io::Limits::no_limits());
let reader = reader
.with_guessed_format()
.context("Failed to open image")?;
let dynamic_image = reader.decode().context("Failed to decode image")?;
let decoded_image = Decoded { dynamic_image };
let tile = tiling::Tile {
position: (0, 0),
zoom_level: tiling::zoom_to_level(1.),
bleed: 0,
texture: decoded_image.into_texture(),
};
tiles.push(tile);
Ok(())
});
ImageRsOther
}
}
pub struct Decoded {
dynamic_image: image_rs::DynamicImage,
}
impl Decoded {
pub fn into_texture(self) -> gdk::Texture {
let (n_bytes, memory_format, layout, data) = match self.dynamic_image {
img @ image_rs::DynamicImage::ImageLuma8(_) => {
let buffer = img.to_rgb8();
(
1,
gdk::MemoryFormat::R8g8b8,
buffer.sample_layout(),
buffer.into_raw(),
)
}
img @ image_rs::DynamicImage::ImageLumaA8(_) => {
let buffer = img.to_rgba8();
(
1,
gdk::MemoryFormat::R8g8b8a8,
buffer.sample_layout(),
buffer.into_raw(),
)
}
image_rs::DynamicImage::ImageRgb8(buffer) => (
1,
gdk::MemoryFormat::R8g8b8,
buffer.sample_layout(),
buffer.into_raw(),
),
image_rs::DynamicImage::ImageRgba8(buffer) => (
1,
gdk::MemoryFormat::R8g8b8a8,
buffer.sample_layout(),
buffer.into_raw(),
),
img @ image_rs::DynamicImage::ImageLuma16(_) => {
let buffer = img.to_rgb16();
(
2,
gdk::MemoryFormat::R16g16b16,
buffer.sample_layout(),
image_rs::DynamicImage::from(buffer).into_bytes(),
)
}
img @ image_rs::DynamicImage::ImageLumaA16(_) => {
let buffer = img.to_rgba16();
(
2,
gdk::MemoryFormat::R16g16b16a16,
buffer.sample_layout(),
image_rs::DynamicImage::from(buffer).into_bytes(),
)
}
image_rs::DynamicImage::ImageRgb16(buffer) => (
2,
gdk::MemoryFormat::R16g16b16,
buffer.sample_layout(),
image_rs::DynamicImage::from(buffer).into_bytes(),
),
image_rs::DynamicImage::ImageRgba16(buffer) => (
2,
gdk::MemoryFormat::R16g16b16a16,
buffer.sample_layout(),
image_rs::DynamicImage::from(buffer).into_bytes(),
),
image_rs::DynamicImage::ImageRgb32F(buffer) => (
4,
gdk::MemoryFormat::R32g32b32Float,
buffer.sample_layout(),
image_rs::DynamicImage::from(buffer).into_bytes(),
),
image_rs::DynamicImage::ImageRgba32F(buffer) => (
4,
gdk::MemoryFormat::R32g32b32a32Float,
buffer.sample_layout(),
image_rs::DynamicImage::from(buffer).into_bytes(),
),
img => {
let buffer = img.to_rgba16();
(
2,
gdk::MemoryFormat::R16g16b16a16,
buffer.sample_layout(),
safe_transmute::transmute_vec(buffer.into_raw()).unwrap(),
)
}
};
let bytes = glib::Bytes::from_owned(data);
gdk::MemoryTexture::new(
layout.width as i32,
layout.height as i32,
memory_format,
&bytes,
layout.height_stride * n_bytes,
)
.upcast()
}
}