| 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 | |