Loupe/src/decoder.rs

236 lines
6.7 KiB
Rust
Raw Normal View History

2024-01-01 22:41:56 +01:00
// Copyright (c) 2023-2024 Sophie Herold
2023-11-29 21:15:36 +01:00
// Copyright (c) 2023 Julian Hofer
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//! Decodes several image formats
pub mod formats;
pub mod tiling;
use std::sync::Arc;
use anyhow::Result;
2024-09-27 02:40:21 +02:00
use async_channel as mpsc;
2024-01-01 22:41:56 +01:00
pub use formats::RSVG_MAX_SIZE;
use formats::*;
use self::tiling::SharedFrameBuffer;
use crate::deps::*;
2024-01-01 22:41:56 +01:00
use crate::metadata::Metadata;
#[derive(Clone, Copy, Debug)]
/// Renderer requests new tiles
///
/// This happens for initial loading or when zoom level or viewing area changes.
pub struct TileRequest {
pub viewport: graphene::Rect,
pub area: graphene::Rect,
pub zoom: f64,
}
#[derive(Debug, Clone)]
/// Signals update to the renderer
pub struct UpdateSender {
2024-09-27 02:40:21 +02:00
sender: mpsc::Sender<DecoderUpdate>,
}
#[derive(Debug)]
/// Signals for renderer (LpImage)
pub enum DecoderUpdate {
/// Dimensions of image in `TilingSore` available/updated
2024-01-01 22:41:56 +01:00
Dimensions,
/// Metadata available
2024-01-01 22:41:56 +01:00
Metadata(Box<Metadata>),
/// New image data available, redraw
Redraw,
/// Start animation
Animated,
2024-07-19 23:19:34 +02:00
SpecificError(DecoderError),
2023-11-30 21:58:26 +01:00
/// And error occurred during decoding
2024-07-19 23:19:34 +02:00
GenericError(anyhow::Error),
}
#[derive(glib::Enum, Debug, Clone, Copy, Default, PartialEq, Eq)]
#[enum_type(name = "LpDecoderError")]
//#[enum_dynamic(lazy_registration = true)]
#[repr(i32)]
pub enum DecoderError {
/// Image format not supported or unknown
UnsupportedFormat,
/// No glycin-loaders installed
NoLoadersConfigured,
/// Memory limit exceeded
OutOfMemory,
2024-07-19 23:19:34 +02:00
#[default]
None,
}
impl DecoderError {
pub fn is_err(self) -> bool {
matches!(self, Self::None)
}
pub fn is_ok(self) -> bool {
!matches!(self, Self::None)
}
}
impl glib::value::ToValueOptional for DecoderError {
fn to_value_optional(s: Option<&Self>) -> glib::Value {
s.unwrap_or(&Self::None).into()
}
}
impl UpdateSender {
pub fn send(&self, update: DecoderUpdate) {
2024-09-27 02:40:21 +02:00
let result = self.sender.force_send(update);
2024-09-27 02:40:21 +02:00
match result {
2025-12-18 23:15:46 +01:00
Err(err) => tracing::error!("Failed to send update: {err}"),
Ok(Some(msg)) => tracing::error!("Unexpectedly replaced the message {msg:?}"),
2024-09-27 02:40:21 +02:00
_ => {}
}
}
2023-11-30 21:58:26 +01:00
/// Send occurring errors to renderer
pub fn spawn_error_handled<F>(&self, f: F) -> glib::JoinHandle<()>
where
2024-07-18 01:51:51 +02:00
F: std::future::Future<Output = Result<(), glycin::ErrorCtx>> + Send + 'static,
{
let update_sender = self.clone();
glib::spawn_future(async move {
let update_sender = update_sender.clone();
2024-07-18 01:51:51 +02:00
let result: Result<(), glycin::ErrorCtx> = f.await;
if let Err(err) = result {
update_sender.send_error(err);
}
})
}
pub fn send_error(&self, err: glycin::ErrorCtx) {
if let Some(mime_type) = err.unsupported_format() {
let mut metadata = Metadata::default();
metadata.set_mime_type(mime_type);
self.send(DecoderUpdate::Metadata(Box::new(metadata)));
self.send(DecoderUpdate::SpecificError(
DecoderError::UnsupportedFormat,
));
}
if err.is_out_of_memory() {
self.send(DecoderUpdate::SpecificError(DecoderError::OutOfMemory));
} else if matches!(err.error(), glycin::Error::NoLoadersConfigured(_)) {
self.send(DecoderUpdate::SpecificError(
DecoderError::NoLoadersConfigured,
));
}
self.send(DecoderUpdate::GenericError(err.into()));
}
}
#[derive(Debug)]
pub struct Decoder {
decoder: FormatDecoder,
update_sender: UpdateSender,
}
#[derive(Debug)]
enum FormatDecoder {
Glycin(Glycin),
Svg(Svg),
Failed,
}
impl Decoder {
/// Get new image decoder
///
/// The textures will be stored in the passed `TiledImage`.
/// The renderer should listen to updates from the returned receiver.
pub async fn new(
file: gio::File,
tiles: Arc<SharedFrameBuffer>,
2024-09-27 02:40:21 +02:00
) -> (Self, mpsc::Receiver<DecoderUpdate>) {
let (sender, receiver) = mpsc::unbounded();
let update_sender = UpdateSender { sender };
tiles.set_update_sender(update_sender.clone());
2025-12-18 23:15:46 +01:00
tracing::trace!("Setting up loader");
let mut loader = glycin::Loader::new(file);
loader.apply_transformations(false);
let image = match loader.load().await {
Ok(image) => image,
Err(err) => {
update_sender.send_error(err);
return (
Self {
decoder: FormatDecoder::Failed,
update_sender,
},
receiver,
);
}
};
let format_decoder = Self::format_decoder(update_sender.clone(), image, tiles);
let decoder = Self {
decoder: format_decoder,
update_sender,
};
(decoder, receiver)
}
fn format_decoder(
update_sender: UpdateSender,
image: glycin::Image,
tiles: Arc<SharedFrameBuffer>,
) -> FormatDecoder {
let mime_type = image.mime_type().to_string();
// Known things we want to match here are
// - image/svg+xml
// - image/svg+xml-compressed
if mime_type.split('+').next() == Some("image/svg") {
2025-12-04 05:35:00 +01:00
FormatDecoder::Svg(Svg::new(image, update_sender, tiles))
} else {
FormatDecoder::Glycin(Glycin::new(image, update_sender, tiles))
}
}
/// Request missing tiles
pub fn request(&self, tile_request: TileRequest) {
if let FormatDecoder::Svg(svg) = &self.decoder {
if let Err(err) = svg.request(tile_request) {
2024-07-19 23:19:34 +02:00
self.update_sender.send(DecoderUpdate::GenericError(err));
}
}
}
pub fn fill_frame_buffer(&self) {
if let FormatDecoder::Glycin(decoder) = &self.decoder {
decoder.fill_frame_buffer();
} else {
2025-12-18 23:15:46 +01:00
tracing::error!("Trying to fill frame buffer for decoder without animation support");
}
}
}