summaryrefslogtreecommitdiff
path: root/src/env
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-05-14 11:14:28 +0200
committerLaurenz <laurmaedje@gmail.com>2021-05-14 11:14:28 +0200
commite65c2b949c61fde471e03881359a2946845b554f (patch)
tree912633376c8dfc9ab63bde24951886df455604b8 /src/env
parent33733fd1efda760d65ff9124b6d143a147edbd11 (diff)
Remove resource abstraction and handle images natively
Diffstat (limited to 'src/env')
-rw-r--r--src/env/fs.rs6
-rw-r--r--src/env/image.rs33
-rw-r--r--src/env/mod.rs120
3 files changed, 97 insertions, 62 deletions
diff --git a/src/env/fs.rs b/src/env/fs.rs
index 5f18191b..4b77f109 100644
--- a/src/env/fs.rs
+++ b/src/env/fs.rs
@@ -12,7 +12,7 @@ use walkdir::WalkDir;
use super::{Buffer, Loader};
use crate::font::{FaceInfo, FontStretch, FontStyle, FontVariant, FontWeight};
-/// Loads fonts and resources from the local file system.
+/// Loads fonts and images from the local file system.
///
/// _This is only available when the `fs` feature is enabled._
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
@@ -175,8 +175,8 @@ impl Loader for FsLoader {
load(&mut self.cache, &self.files[idx])
}
- fn load_file(&mut self, url: &str) -> Option<Buffer> {
- load(&mut self.cache, Path::new(url))
+ fn load_file(&mut self, path: &str) -> Option<Buffer> {
+ load(&mut self.cache, Path::new(path))
}
}
diff --git a/src/env/image.rs b/src/env/image.rs
index 4bdb5483..365ff312 100644
--- a/src/env/image.rs
+++ b/src/env/image.rs
@@ -4,37 +4,44 @@ use std::io::Cursor;
use image::io::Reader as ImageReader;
use image::{DynamicImage, GenericImageView, ImageFormat};
-use super::Buffer;
-
-/// A loaded image resource.
-pub struct ImageResource {
+/// A loaded image.
+pub struct Image {
/// The original format the image was encoded in.
pub format: ImageFormat,
/// The decoded image.
pub buf: DynamicImage,
}
-impl ImageResource {
- /// Parse an image resource from raw data in a supported format.
+impl Image {
+ /// Parse an image from raw data in a supported format.
///
/// The image format is determined automatically.
- pub fn parse(data: Buffer) -> Option<Self> {
- let cursor = Cursor::new(data.as_ref());
+ pub fn parse(data: &[u8]) -> Option<Self> {
+ let cursor = Cursor::new(data);
let reader = ImageReader::new(cursor).with_guessed_format().ok()?;
let format = reader.format()?;
let buf = reader.decode().ok()?;
Some(Self { format, buf })
}
+
+ /// The width of the image.
+ pub fn width(&self) -> u32 {
+ self.buf.width()
+ }
+
+ /// The height of the image.
+ pub fn height(&self) -> u32 {
+ self.buf.height()
+ }
}
-impl Debug for ImageResource {
+impl Debug for Image {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- let (width, height) = self.buf.dimensions();
- f.debug_struct("ImageResource")
+ f.debug_struct("Image")
.field("format", &self.format)
.field("color", &self.buf.color())
- .field("width", &width)
- .field("height", &height)
+ .field("width", &self.width())
+ .field("height", &self.height())
.finish()
}
}
diff --git a/src/env/mod.rs b/src/env/mod.rs
index c8ba46ec..84be3e81 100644
--- a/src/env/mod.rs
+++ b/src/env/mod.rs
@@ -1,4 +1,4 @@
-//! Font and resource loading.
+//! Font and image loading.
#[cfg(feature = "fs")]
mod fs;
@@ -8,7 +8,6 @@ pub use self::image::*;
#[cfg(feature = "fs")]
pub use fs::*;
-use std::any::Any;
use std::collections::{hash_map::Entry, HashMap};
use std::rc::Rc;
@@ -16,18 +15,22 @@ use serde::{Deserialize, Serialize};
use crate::font::{Face, FaceInfo, FontVariant};
-/// Handles font and resource loading.
+/// Handles font and image loading.
pub struct Env {
/// The loader that serves the font face and file buffers.
loader: Box<dyn Loader>,
- /// Loaded resources indexed by [`ResourceId`].
- resources: Vec<Box<dyn Any>>,
- /// Maps from URL to loaded resource.
- urls: HashMap<String, ResourceId>,
/// Faces indexed by [`FaceId`]. `None` if not yet loaded.
faces: Vec<Option<Face>>,
/// Maps a family name to the ids of all faces that are part of the family.
families: HashMap<String, Vec<FaceId>>,
+ /// Loaded images indexed by [`ImageId`].
+ images: Vec<Image>,
+ /// Maps from paths to loaded images.
+ paths: HashMap<String, ImageId>,
+ /// Callback for loaded font faces.
+ on_face_load: Option<Box<dyn Fn(FaceId, &Face)>>,
+ /// Callback for loaded images.
+ on_image_load: Option<Box<dyn Fn(ImageId, &Image)>>,
}
impl Env {
@@ -49,10 +52,12 @@ impl Env {
Self {
loader: Box::new(loader),
- resources: vec![],
- urls: HashMap::new(),
faces,
families,
+ images: vec![],
+ paths: HashMap::new(),
+ on_face_load: None,
+ on_image_load: None,
}
}
@@ -113,62 +118,75 @@ impl Env {
}
// Load the face if it's not already loaded.
- let idx = best?.0 as usize;
+ let id = best?;
+ let idx = id.0 as usize;
let slot = &mut self.faces[idx];
if slot.is_none() {
let index = infos[idx].index;
let buffer = self.loader.load_face(idx)?;
let face = Face::new(buffer, index)?;
+ if let Some(callback) = &self.on_face_load {
+ callback(id, &face);
+ }
*slot = Some(face);
}
best
}
- /// Load a file from a local or remote URL, parse it into a cached resource
- /// and return a unique identifier that allows to retrieve the parsed
- /// resource through [`resource()`](Self::resource).
- pub fn load_resource<F, R>(&mut self, url: &str, parse: F) -> Option<ResourceId>
+ /// Get a reference to a queried face.
+ ///
+ /// This panics if no face with this id was loaded. This function should
+ /// only be called with ids returned by [`query_face()`](Self::query_face).
+ #[track_caller]
+ pub fn face(&self, id: FaceId) -> &Face {
+ self.faces[id.0 as usize].as_ref().expect("font face was not loaded")
+ }
+
+ /// Register a callback which is invoked when a font face was loaded.
+ pub fn on_face_load<F>(&mut self, f: F)
where
- F: FnOnce(Buffer) -> Option<R>,
- R: 'static,
+ F: Fn(FaceId, &Face) + 'static,
{
- Some(match self.urls.entry(url.to_string()) {
+ self.on_face_load = Some(Box::new(f));
+ }
+
+ /// Load and decode an image file from a path.
+ pub fn load_image(&mut self, path: &str) -> Option<ImageId> {
+ Some(match self.paths.entry(path.to_string()) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
- let buffer = self.loader.load_file(url)?;
- let resource = parse(buffer)?;
- let len = self.resources.len();
- self.resources.push(Box::new(resource));
- *entry.insert(ResourceId(len as u32))
+ let buffer = self.loader.load_file(path)?;
+ let image = Image::parse(&buffer)?;
+ let id = ImageId(self.images.len() as u32);
+ if let Some(callback) = &self.on_image_load {
+ callback(id, &image);
+ }
+ self.images.push(image);
+ *entry.insert(id)
}
})
}
- /// Get a reference to a queried face.
+ /// Get a reference to a loaded image.
///
- /// # Panics
- /// This panics if no face with this id was loaded. This function should
- /// only be called with ids returned by [`query_face()`](Self::query_face).
+ /// This panics if no image with this id was loaded. This function should
+ /// only be called with ids returned by [`load_image()`](Self::load_image).
#[track_caller]
- pub fn face(&self, id: FaceId) -> &Face {
- self.faces[id.0 as usize].as_ref().expect("font face was not loaded")
+ pub fn image(&self, id: ImageId) -> &Image {
+ &self.images[id.0 as usize]
}
- /// Get a reference to a loaded resource.
- ///
- /// This panics if no resource with this id was loaded. This function should
- /// only be called with ids returned by
- /// [`load_resource()`](Self::load_resource).
- #[track_caller]
- pub fn resource<R: 'static>(&self, id: ResourceId) -> &R {
- self.resources[id.0 as usize]
- .downcast_ref()
- .expect("bad resource type")
+ /// Register a callback which is invoked when an image was loaded.
+ pub fn on_image_load<F>(&mut self, f: F)
+ where
+ F: Fn(ImageId, &Image) + 'static,
+ {
+ self.on_image_load = Some(Box::new(f));
}
}
-/// Loads fonts and resources from a remote or local source.
+/// Loads fonts and images from a remote or local source.
pub trait Loader {
/// Descriptions of all font faces this loader serves.
fn faces(&self) -> &[FaceInfo];
@@ -176,8 +194,8 @@ pub trait Loader {
/// Load the font face with the given index in [`faces()`](Self::faces).
fn load_face(&mut self, idx: usize) -> Option<Buffer>;
- /// Load a file from a URL.
- fn load_file(&mut self, url: &str) -> Option<Buffer>;
+ /// Load a file from a path.
+ fn load_file(&mut self, path: &str) -> Option<Buffer>;
}
/// A shared byte buffer.
@@ -205,11 +223,21 @@ impl FaceId {
}
}
-/// A unique identifier for a loaded resource.
+/// A unique identifier for a loaded image.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
-pub struct ResourceId(u32);
+pub struct ImageId(u32);
-impl ResourceId {
- /// A blank initialization value.
- pub const MAX: Self = Self(u32::MAX);
+impl ImageId {
+ /// Create an image id from the raw underlying value.
+ ///
+ /// This should only be called with values returned by
+ /// [`into_raw`](Self::into_raw).
+ pub fn from_raw(v: u32) -> Self {
+ Self(v)
+ }
+
+ /// Convert into the raw underlying value.
+ pub fn into_raw(self) -> u32 {
+ self.0
+ }
}