1 | use bitflags::bitflags; |
2 | use imgref::*; |
3 | use rgb::alt::Gray; |
4 | use rgb::*; |
5 | use slotmap::{DefaultKey, SlotMap}; |
6 | |
7 | #[cfg (feature = "image-loading" )] |
8 | use ::image::DynamicImage; |
9 | |
10 | #[cfg (feature = "image-loading" )] |
11 | use std::convert::TryFrom; |
12 | |
13 | use crate::{ErrorKind, Renderer}; |
14 | |
15 | /// An image handle. |
16 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] |
17 | pub struct ImageId(DefaultKey); |
18 | |
19 | /// Specifies the format of an image's pixels. |
20 | #[derive (Copy, Clone, Debug, Eq, PartialEq, Hash)] |
21 | pub 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 | |
30 | bitflags! { |
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 ] |
52 | pub 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 | |
64 | impl 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 | |
88 | impl<'a> From<ImgRef<'a, RGB8>> for ImageSource<'a> { |
89 | fn from(src: ImgRef<'a, RGB8>) -> Self { |
90 | Self::Rgb(src) |
91 | } |
92 | } |
93 | |
94 | impl<'a> From<ImgRef<'a, RGBA8>> for ImageSource<'a> { |
95 | fn from(src: ImgRef<'a, RGBA8>) -> Self { |
96 | Self::Rgba(src) |
97 | } |
98 | } |
99 | |
100 | impl<'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" )] |
107 | impl<'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" )] |
114 | impl<'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)] |
139 | pub struct Size { |
140 | pub width: usize, |
141 | pub height: usize, |
142 | } |
143 | |
144 | impl 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)] |
152 | pub struct ImageInfo { |
153 | flags: ImageFlags, |
154 | size: Size, |
155 | format: PixelFormat, |
156 | } |
157 | |
158 | impl 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 | |
199 | pub struct ImageStore<T>(SlotMap<DefaultKey, (ImageInfo, T)>); |
200 | |
201 | impl<T> Default for ImageStore<T> { |
202 | fn default() -> Self { |
203 | Self::new() |
204 | } |
205 | } |
206 | |
207 | impl<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 ] |
288 | pub 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 | |