From c69cddb0689d13efcc9da3b421f8db8dc6fb8b15 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 27 Oct 2024 17:17:47 -0700 Subject: [PATCH] Add decoding hooks --- src/hooks.rs | 45 +++++++++++++++++++++++++++ src/image_reader/image_reader_type.rs | 10 +++++- src/lib.rs | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/hooks.rs diff --git a/src/hooks.rs b/src/hooks.rs new file mode 100644 index 0000000000..0e6c9adebe --- /dev/null +++ b/src/hooks.rs @@ -0,0 +1,45 @@ +//! This module provides a way to register decoding hooks for image formats not directly supported +//! by this crate. + +use std::{ + collections::HashMap, + io::{BufReader, Read, Seek}, + sync::RwLock, +}; + +use crate::{ImageDecoder, ImageFormat, ImageResult}; + +pub(crate) trait ReadSeek: Read + Seek {} +impl ReadSeek for T {} + +pub(crate) static DECODING_HOOKS: RwLock>> = + RwLock::new(None); + +/// A wrapper around a type-erased trait object that implements `Read` and `Seek`. +pub struct BoxReadSeek<'a>(pub(crate) Box); +impl<'a> Read for BoxReadSeek<'a> { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.0.read(buf) + } +} +impl<'a> Seek for BoxReadSeek<'a> { + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + self.0.seek(pos) + } +} + +/// A function to produce an `ImageDecoder` for a given image format. +pub type DecodingHook = Box< + dyn for<'a> Fn(BufReader>) -> ImageResult> + + Send + + Sync, +>; + +/// Register a new decoding hook or replace an existing one. +pub fn register_decoding_hook(format: ImageFormat, hook: DecodingHook) { + let mut hooks = DECODING_HOOKS.write().unwrap(); + if hooks.is_none() { + *hooks = Some(HashMap::new()); + } + hooks.as_mut().unwrap().insert(format, hook); +} diff --git a/src/image_reader/image_reader_type.rs b/src/image_reader/image_reader_type.rs index 479316f191..ea5d23c4b0 100644 --- a/src/image_reader/image_reader_type.rs +++ b/src/image_reader/image_reader_type.rs @@ -4,6 +4,7 @@ use std::path::Path; use crate::dynimage::DynamicImage; use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind}; +use crate::hooks::{BoxReadSeek, DECODING_HOOKS}; use crate::image::ImageFormat; use crate::{ImageDecoder, ImageError, ImageResult}; @@ -178,9 +179,16 @@ impl<'a, R: 'a + BufRead + Seek> ImageReader { #[cfg(feature = "qoi")] ImageFormat::Qoi => Box::new(qoi::QoiDecoder::new(reader)?), format => { + let hooks = DECODING_HOOKS.read().unwrap(); + if let Some(hooks) = hooks.as_ref() { + if let Some(hook) = hooks.get(&format) { + return hook(BufReader::new(BoxReadSeek(Box::new(reader)))); + } + } + return Err(ImageError::Unsupported( ImageFormatHint::Exact(format).into(), - )) + )); } }) } diff --git a/src/lib.rs b/src/lib.rs index 32b25fb924..a70d5f1b7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,6 +287,7 @@ mod buffer_; mod buffer_par; mod color; mod dynimage; +pub mod hooks; mod image; mod image_reader; pub mod metadata;