1use bitflags::bitflags;
2use imgref::*;
3use rgb::alt::Gray;
4use rgb::*;
5use slotmap::{DefaultKey, SlotMap};
6
7#[cfg(feature = "image-loading")]
8use ::image::DynamicImage;
9
10#[cfg(feature = "image-loading")]
11use std::convert::TryFrom;
12
13use crate::{ErrorKind, Renderer};
14
15/// An image handle.
16#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
17pub struct ImageId(DefaultKey);
18
19/// Specifies the format of an image's pixels.
20#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
21pub enum PixelFormat {
22 /// 24-bit RGB image format (8 bits per channel)
23 Rgb8,
24 /// 32-bit RGBA image format (8 bits per channel, including alpha)
25 Rgba8,
26 /// 8-bit grayscale image format
27 Gray8,
28}
29
30bitflags! {
31 /// Represents a set of flags that modify the behavior of an image.
32 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33 pub struct ImageFlags: u32 {
34 /// Generates mipmaps during the creation of the image.
35 const GENERATE_MIPMAPS = 1;
36 /// Repeats the image in the X direction when rendered.
37 const REPEAT_X = 1 << 1;
38 /// Repeats the image in the Y direction when rendered.
39 const REPEAT_Y = 1 << 2;
40 /// Flips (inverses) the image in the Y direction when rendered.
41 const FLIP_Y = 1 << 3;
42 /// Indicates that the image data has premultiplied alpha.
43 const PREMULTIPLIED = 1 << 4;
44 /// Uses nearest-neighbor interpolation instead of linear interpolation when rendering the image.
45 const NEAREST = 1 << 5;
46 }
47}
48
49/// Represents the source of an image.
50#[derive(Copy, Clone, Debug)]
51#[non_exhaustive]
52pub enum ImageSource<'a> {
53 /// Image source with RGB image format (8 bits per channel)
54 Rgb(ImgRef<'a, RGB8>),
55 /// Image source with RGBA image format (8 bits per channel, including alpha)
56 Rgba(ImgRef<'a, RGBA8>),
57 /// Image source with 8-bit grayscale image format
58 Gray(ImgRef<'a, Gray<u8>>),
59 /// Image source referencing a HTML image element (only available on `wasm32` target)
60 #[cfg(target_arch = "wasm32")]
61 HtmlImageElement(&'a web_sys::HtmlImageElement),
62}
63
64impl ImageSource<'_> {
65 /// Returns the format of the image source.
66 pub fn format(&self) -> PixelFormat {
67 match self {
68 Self::Rgb(_) => PixelFormat::Rgb8,
69 Self::Rgba(_) => PixelFormat::Rgba8,
70 Self::Gray(_) => PixelFormat::Gray8,
71 #[cfg(target_arch = "wasm32")]
72 Self::HtmlImageElement(_) => PixelFormat::Rgba8,
73 }
74 }
75
76 /// Returns the dimensions (width and height) of the image source.
77 pub fn dimensions(&self) -> Size {
78 match self {
79 Self::Rgb(imgref: &Img<&[Rgb]>) => Size::new(imgref.width(), imgref.height()),
80 Self::Rgba(imgref: &Img<&[Rgba]>) => Size::new(imgref.width(), imgref.height()),
81 Self::Gray(imgref: &Img<&[Gray_v08]>) => Size::new(imgref.width(), imgref.height()),
82 #[cfg(target_arch = "wasm32")]
83 Self::HtmlImageElement(element) => Size::new(element.width() as usize, element.height() as usize),
84 }
85 }
86}
87
88impl<'a> From<ImgRef<'a, RGB8>> for ImageSource<'a> {
89 fn from(src: ImgRef<'a, RGB8>) -> Self {
90 Self::Rgb(src)
91 }
92}
93
94impl<'a> From<ImgRef<'a, RGBA8>> for ImageSource<'a> {
95 fn from(src: ImgRef<'a, RGBA8>) -> Self {
96 Self::Rgba(src)
97 }
98}
99
100impl<'a> From<ImgRef<'a, Gray<u8>>> for ImageSource<'a> {
101 fn from(src: ImgRef<'a, Gray<u8>>) -> Self {
102 Self::Gray(src)
103 }
104}
105
106#[cfg(target_arch = "wasm32")]
107impl<'a> From<&'a web_sys::HtmlImageElement> for ImageSource<'a> {
108 fn from(src: &'a web_sys::HtmlImageElement) -> Self {
109 Self::HtmlImageElement(src)
110 }
111}
112
113#[cfg(feature = "image-loading")]
114impl<'a> TryFrom<&'a DynamicImage> for ImageSource<'a> {
115 type Error = ErrorKind;
116
117 fn try_from(src: &'a DynamicImage) -> Result<Self, ErrorKind> {
118 Ok(match src {
119 ::image::DynamicImage::ImageLuma8(img: &ImageBuffer, Vec<…>>) => {
120 let src: Img<&[Gray<u8>]> = Img::new(buf:img.as_pixels(), img.width() as usize, img.height() as usize);
121 ImageSource::from(src)
122 }
123 ::image::DynamicImage::ImageRgb8(img: &ImageBuffer, Vec<…>>) => {
124 let src: Img<&[Rgb]> = Img::new(buf:img.as_rgb(), img.width() as usize, img.height() as usize);
125 ImageSource::from(src)
126 }
127 ::image::DynamicImage::ImageRgba8(img: &ImageBuffer, Vec<…>>) => {
128 let src: Img<&[Rgba]> = Img::new(buf:img.as_rgba(), img.width() as usize, img.height() as usize);
129 ImageSource::from(src)
130 }
131 // TODO: if format is not supported maybe we should convert it here,
132 // But that is an expensive operation on the render thread that will remain hidden from the user
133 _ => return Err(ErrorKind::UnsupportedImageFormat),
134 })
135 }
136}
137
138#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
139pub struct Size {
140 pub width: usize,
141 pub height: usize,
142}
143
144impl Size {
145 pub fn new(width: usize, height: usize) -> Self {
146 Self { width, height }
147 }
148}
149
150/// Information about an image.
151#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
152pub struct ImageInfo {
153 flags: ImageFlags,
154 size: Size,
155 format: PixelFormat,
156}
157
158impl ImageInfo {
159 /// Creates a new `ImageInfo` with the specified flags, width, height, and format.
160 pub fn new(flags: ImageFlags, width: usize, height: usize, format: PixelFormat) -> Self {
161 Self {
162 flags,
163 size: Size { width, height },
164 format,
165 }
166 }
167
168 /// Returns the image flags.
169 pub fn flags(&self) -> ImageFlags {
170 self.flags
171 }
172
173 /// Returns the image width in pixels.
174 pub fn width(&self) -> usize {
175 self.size.width
176 }
177
178 /// Returns the image height in pixels.
179 pub fn height(&self) -> usize {
180 self.size.height
181 }
182
183 /// Returns the image size (width and height) in pixels.
184 pub fn size(&self) -> Size {
185 self.size
186 }
187
188 /// Returns the image format.
189 pub fn format(&self) -> PixelFormat {
190 self.format
191 }
192
193 /// Sets the image format.
194 pub fn set_format(&mut self, format: PixelFormat) {
195 self.format = format;
196 }
197}
198
199pub struct ImageStore<T>(SlotMap<DefaultKey, (ImageInfo, T)>);
200
201impl<T> Default for ImageStore<T> {
202 fn default() -> Self {
203 Self::new()
204 }
205}
206
207impl<T> ImageStore<T> {
208 pub fn new() -> Self {
209 Self(SlotMap::new())
210 }
211
212 pub fn alloc<R: Renderer<Image = T>>(&mut self, renderer: &mut R, info: ImageInfo) -> Result<ImageId, ErrorKind> {
213 let image = renderer.alloc_image(info)?;
214 Ok(ImageId(self.0.insert((info, image))))
215 }
216
217 pub fn register_native_texture<R: Renderer<Image = T>>(
218 &mut self,
219 renderer: &mut R,
220 texture: R::NativeTexture,
221 info: ImageInfo,
222 ) -> Result<ImageId, ErrorKind> {
223 let image = renderer.create_image_from_native_texture(texture, info)?;
224 Ok(ImageId(self.0.insert((info, image))))
225 }
226
227 // Reallocates the image without changing the id.
228 pub fn realloc<R: Renderer<Image = T>>(
229 &mut self,
230 renderer: &mut R,
231 id: ImageId,
232 info: ImageInfo,
233 ) -> Result<(), ErrorKind> {
234 if let Some(old) = self.0.get_mut(id.0) {
235 let new = renderer.alloc_image(info)?;
236 old.0 = info;
237 old.1 = new;
238 Ok(())
239 } else {
240 Err(ErrorKind::ImageIdNotFound)
241 }
242 }
243
244 pub fn get(&self, id: ImageId) -> Option<&T> {
245 self.0.get(id.0).map(|inner| &inner.1)
246 }
247
248 pub fn get_mut(&mut self, id: ImageId) -> Option<&mut T> {
249 self.0.get_mut(id.0).map(|inner| &mut inner.1)
250 }
251
252 pub fn update<R: Renderer<Image = T>>(
253 &mut self,
254 renderer: &mut R,
255 id: ImageId,
256 data: ImageSource,
257 x: usize,
258 y: usize,
259 ) -> Result<(), ErrorKind> {
260 if let Some(image) = self.0.get_mut(id.0) {
261 renderer.update_image(&mut image.1, data, x, y)?;
262 Ok(())
263 } else {
264 Err(ErrorKind::ImageIdNotFound)
265 }
266 }
267
268 pub fn info(&self, id: ImageId) -> Option<ImageInfo> {
269 self.0.get(id.0).map(|inner| inner.0)
270 }
271
272 pub fn remove<R: Renderer<Image = T>>(&mut self, renderer: &mut R, id: ImageId) {
273 if let Some(image) = self.0.remove(id.0) {
274 renderer.delete_image(image.1, id);
275 }
276 }
277
278 pub fn clear<R: Renderer<Image = T>>(&mut self, renderer: &mut R) {
279 for (idx, image) in self.0.drain() {
280 renderer.delete_image(image.1, ImageId(idx));
281 }
282 }
283}
284
285/// Specifies the type of filter to apply to images with `crate::Canvas::filter_image`.
286#[derive(Clone, Copy, Debug)]
287#[non_exhaustive]
288pub enum ImageFilter {
289 /// Applies a Gaussian blur filter with the specified standard deviation.
290 GaussianBlur {
291 /// The standard deviation of the Gaussian blur filter.
292 sigma: f32,
293 },
294}
295