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