1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
//! Image handling.
use std::collections::{hash_map::Entry, HashMap};
use std::fmt::{self, Debug, Formatter};
use std::io::Cursor;
use image::io::Reader as ImageReader;
use image::{DynamicImage, GenericImageView, ImageFormat};
use serde::{Deserialize, Serialize};
use crate::loading::Loader;
/// A loaded image.
pub struct Image {
/// The original format the image was encoded in.
pub format: ImageFormat,
/// The decoded image.
pub buf: DynamicImage,
}
impl Image {
/// Parse an image from raw data in a supported format (PNG or JPEG).
///
/// The image format is determined automatically.
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 Image {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Image")
.field("format", &self.format)
.field("color", &self.buf.color())
.field("width", &self.width())
.field("height", &self.height())
.finish()
}
}
/// Caches decoded images.
pub struct ImageCache {
/// Loaded images indexed by [`ImageId`].
images: Vec<Image>,
/// Maps from paths to loaded images.
paths: HashMap<String, ImageId>,
/// Callback for loaded images.
on_load: Option<Box<dyn Fn(ImageId, &Image)>>,
}
impl ImageCache {
/// Create a new, empty image cache.
pub fn new() -> Self {
Self {
images: vec![],
paths: HashMap::new(),
on_load: None,
}
}
/// Load and decode an image file from a path.
pub fn load(&mut self, loader: &mut dyn Loader, path: &str) -> Option<ImageId> {
Some(match self.paths.entry(path.to_string()) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let buffer = loader.load_file(path)?;
let image = Image::parse(&buffer)?;
let id = ImageId(self.images.len() as u32);
if let Some(callback) = &self.on_load {
callback(id, &image);
}
self.images.push(image);
*entry.insert(id)
}
})
}
/// Get a reference to a loaded image.
///
/// This panics if no image with this id was loaded. This function should
/// only be called with ids returned by [`load()`](Self::load).
#[track_caller]
pub fn get(&self, id: ImageId) -> &Image {
&self.images[id.0 as usize]
}
/// Register a callback which is invoked each time an image is loaded.
pub fn on_load<F>(&mut self, f: F)
where
F: Fn(ImageId, &Image) + 'static,
{
self.on_load = Some(Box::new(f));
}
}
/// A unique identifier for a loaded image.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ImageId(u32);
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
}
}
|