1use bitflags::bitflags;
2use imgref::*;
3use rgb::alt::GRAY8;
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/// Image format: `Rgb8`, `Rgba8`, `Gray8`.
20#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
21pub enum PixelFormat {
22 Rgb8,
23 Rgba8,
24 Gray8,
25}
26
27bitflags! {
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]
43pub 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
51impl 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
75impl<'a> From<ImgRef<'a, RGB8>> for ImageSource<'a> {
76 fn from(src: ImgRef<'a, RGB8>) -> Self {
77 Self::Rgb(src)
78 }
79}
80
81impl<'a> From<ImgRef<'a, RGBA8>> for ImageSource<'a> {
82 fn from(src: ImgRef<'a, RGBA8>) -> Self {
83 Self::Rgba(src)
84 }
85}
86
87impl<'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")]
94impl<'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")]
101impl<'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)]
127pub struct Size {
128 pub width: usize,
129 pub height: usize,
130}
131
132impl 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)]
140pub struct ImageInfo {
141 flags: ImageFlags,
142 size: Size,
143 format: PixelFormat,
144}
145
146impl 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
185pub struct ImageStore<T>(SlotMap<DefaultKey, (ImageInfo, T)>);
186
187impl<T> Default for ImageStore<T> {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193impl<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]
277pub enum ImageFilter {
278 /// The filter shall be a gaussian blur with given sigma as standard deviation.
279 GaussianBlur { sigma: f32 },
280}
281