1use bitflags::bitflags;
2use generational_arena::{
3 Arena,
4 Index,
5};
6use imgref::*;
7use rgb::alt::GRAY8;
8use rgb::*;
9
10#[cfg(feature = "image-loading")]
11use ::image::DynamicImage;
12
13#[cfg(feature = "image-loading")]
14use std::convert::TryFrom;
15
16use crate::{
17 ErrorKind,
18 Renderer,
19};
20
21#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
23pub struct ImageId(pub Index);
24
25#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
27pub enum PixelFormat {
28 Rgb8,
30 Rgba8,
32 Gray8,
34}
35
36bitflags! {
37 pub struct ImageFlags: u32 {
39 const GENERATE_MIPMAPS = 1;
41 const REPEAT_X = 1 << 1;
43 const REPEAT_Y = 1 << 2;
45 const FLIP_Y = 1 << 3;
47 const PREMULTIPLIED = 1 << 4;
49 const NEAREST = 1 << 5;
51 }
52}
53
54#[derive(Copy, Clone, Debug)]
56#[non_exhaustive]
57pub enum ImageSource<'a> {
58 Rgb(ImgRef<'a, RGB8>),
60 Rgba(ImgRef<'a, RGBA8>),
62 Gray(ImgRef<'a, GRAY8>),
64 #[cfg(target_arch = "wasm32")]
65 HtmlImageElement(&'a web_sys::HtmlImageElement),
66}
67
68impl ImageSource<'_> {
69 pub fn format(&self) -> PixelFormat {
71 match self {
72 Self::Rgb(_) => PixelFormat::Rgb8,
73 Self::Rgba(_) => PixelFormat::Rgba8,
74 Self::Gray(_) => PixelFormat::Gray8,
75 #[cfg(target_arch = "wasm32")]
76 Self::HtmlImageElement(_) => PixelFormat::Rgba8,
77 }
78 }
79
80 pub fn dimensions(&self) -> (usize, usize) {
82 match self {
85 Self::Rgb(imgref) => (imgref.width(), imgref.height()),
86 Self::Rgba(imgref) => (imgref.width(), imgref.height()),
87 Self::Gray(imgref) => (imgref.width(), imgref.height()),
88 #[cfg(target_arch = "wasm32")]
89 Self::HtmlImageElement(element) => (element.width() as usize, element.height() as usize),
90 }
91 }
92}
93
94impl<'a> From<ImgRef<'a, RGB8>> for ImageSource<'a> {
95 fn from(src: ImgRef<'a, RGB8>) -> Self {
96 Self::Rgb(src)
97 }
98}
99
100impl<'a> From<ImgRef<'a, RGBA8>> for ImageSource<'a> {
101 fn from(src: ImgRef<'a, RGBA8>) -> Self {
102 Self::Rgba(src)
103 }
104}
105
106impl<'a> From<ImgRef<'a, GRAY8>> for ImageSource<'a> {
107 fn from(src: ImgRef<'a, GRAY8>) -> Self {
108 Self::Gray(src)
109 }
110}
111
112#[cfg(target_arch = "wasm32")]
113impl<'a> From<&'a web_sys::HtmlImageElement> for ImageSource<'a> {
114 fn from(src: &'a web_sys::HtmlImageElement) -> Self {
115 Self::HtmlImageElement(src)
116 }
117}
118
119#[cfg(feature = "image-loading")]
120impl<'a> TryFrom<&'a DynamicImage> for ImageSource<'a> {
121 type Error = ErrorKind;
122
123 fn try_from(src: &'a DynamicImage) -> Result<Self, ErrorKind> {
124 match src {
125 ::image::DynamicImage::ImageLuma8(img) => {
126 let src: Img<&[GRAY8]> =
127 Img::new(img.as_ref().as_pixels(), img.width() as usize, img.height() as usize);
128
129 Ok(ImageSource::from(src))
130 }
131 ::image::DynamicImage::ImageRgb8(img) => {
132 let src = Img::new(img.as_ref().as_rgb(), img.width() as usize, img.height() as usize);
133 Ok(ImageSource::from(src))
134 }
135 ::image::DynamicImage::ImageRgba8(img) => {
136 let src = Img::new(img.as_ref().as_rgba(), img.width() as usize, img.height() as usize);
137 Ok(ImageSource::from(src))
138 }
139 _ => Err(ErrorKind::UnsuportedImageFromat),
142 }
143 }
144}
145
146#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
148pub struct ImageInfo {
149 flags: ImageFlags,
150 width: usize,
151 height: usize,
152 format: PixelFormat,
153}
154
155impl ImageInfo {
156 pub fn new(flags: ImageFlags, width: usize, height: usize, format: PixelFormat) -> Self {
158 Self {
159 flags,
160 width,
161 height,
162 format,
163 }
164 }
165
166 pub fn flags(&self) -> ImageFlags {
168 self.flags
169 }
170
171 pub fn width(&self) -> usize {
173 self.width
174 }
175
176 pub fn height(&self) -> usize {
178 self.height
179 }
180
181 pub fn format(&self) -> PixelFormat {
183 self.format
184 }
185
186 pub fn set_format(&mut self, format: PixelFormat) {
188 self.format = format;
189 }
190}
191
192pub struct ImageStore<T>(Arena<(ImageInfo, T)>);
194
195impl<T> Default for ImageStore<T> {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201impl<T> ImageStore<T> {
202 pub fn new() -> Self {
204 Self(Arena::new())
205 }
206
207 pub fn alloc<R: Renderer<Image = T>>(&mut self, renderer: &mut R, info: ImageInfo) -> Result<ImageId, ErrorKind> {
209 let image = renderer.alloc_image(info)?;
210
211 Ok(ImageId(self.0.insert((info, image))))
212 }
213
214 pub fn realloc<R: Renderer<Image = T>>(
218 &mut self,
219 renderer: &mut R,
220 id: ImageId,
221 info: ImageInfo,
222 ) -> Result<(), ErrorKind> {
223 if let Some(old) = self.0.get_mut(id.0) {
224 let new = renderer.alloc_image(info)?;
225 old.0 = info;
226 old.1 = new;
227 Ok(())
228 } else {
229 Err(ErrorKind::ImageIdNotFound)
230 }
231 }
232
233 pub fn get(&self, id: ImageId) -> Option<&T> {
235 self.0.get(id.0).map(|inner| &inner.1)
236 }
237
238 pub fn get_mut(&mut self, id: ImageId) -> Option<&mut T> {
240 self.0.get_mut(id.0).map(|inner| &mut inner.1)
241 }
242
243 pub fn update<R: Renderer<Image = T>>(
245 &mut self,
246 renderer: &mut R,
247 id: ImageId,
248 data: ImageSource,
249 x: usize,
250 y: usize,
251 ) -> Result<(), ErrorKind> {
252 if let Some(image) = self.0.get_mut(id.0) {
253 renderer.update_image(&mut image.1, data, x, y)?;
254 Ok(())
255 } else {
256 Err(ErrorKind::ImageIdNotFound)
257 }
258 }
259
260 pub fn info(&self, id: ImageId) -> Option<ImageInfo> {
262 self.0.get(id.0).map(|inner| inner.0)
263 }
264
265 pub fn remove<R: Renderer<Image = T>>(&mut self, renderer: &mut R, id: ImageId) {
267 if let Some(image) = self.0.remove(id.0) {
268 renderer.delete_image(image.1, id);
269 }
270 }
271
272 pub fn clear<R: Renderer<Image = T>>(&mut self, renderer: &mut R) {
274 for (idx, image) in self.0.drain() {
275 renderer.delete_image(image.1, ImageId(idx));
276 }
277 }
278}
279
280#[derive(Clone, Copy, Debug)]
282#[non_exhaustive]
283pub enum ImageFilter {
284 GaussianBlur {
286 sigma: f32,
288 },
289}