diff options
Diffstat (limited to 'src/element/image.rs')
-rw-r--r-- | src/element/image.rs | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/src/element/image.rs b/src/element/image.rs new file mode 100644 index 0000000..12f3f30 --- /dev/null +++ b/src/element/image.rs @@ -0,0 +1,213 @@ +#[cfg(all(not(target_arch = "wasm32"), feature = "image"))] +use image::{DynamicImage, GenericImageView}; + +use super::{Drawable, PointCollection}; +use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; +use crate::drawing::bitmap_pixel::{PixelFormat, RGBPixel}; + +use crate::drawing::BitMapBackend; +use std::borrow::Borrow; +use std::marker::PhantomData; + +enum Buffer<'a> { + Owned(Vec<u8>), + Borrowed(&'a [u8]), + BorrowedMut(&'a mut [u8]), +} + +impl<'a> Borrow<[u8]> for Buffer<'a> { + fn borrow(&self) -> &[u8] { + self.as_ref() + } +} + +impl AsRef<[u8]> for Buffer<'_> { + fn as_ref(&self) -> &[u8] { + match self { + Buffer::Owned(owned) => owned.as_ref(), + Buffer::Borrowed(target) => target, + Buffer::BorrowedMut(target) => target, + } + } +} + +impl<'a> Buffer<'a> { + fn to_mut(&mut self) -> &mut [u8] { + let owned = match self { + Buffer::Owned(owned) => return &mut owned[..], + Buffer::BorrowedMut(target) => return target, + Buffer::Borrowed(target) => { + let mut value = vec![]; + value.extend_from_slice(target); + value + } + }; + + *self = Buffer::Owned(owned); + self.to_mut() + } +} + +/// The element that contains a bitmap on it +pub struct BitMapElement<'a, Coord, P: PixelFormat = RGBPixel> { + image: Buffer<'a>, + size: (u32, u32), + pos: Coord, + phantom: PhantomData<P>, +} + +impl<'a, Coord, P: PixelFormat> BitMapElement<'a, Coord, P> { + /// Create a new empty bitmap element. This can be use as + /// the draw and blit pattern. + /// + /// - `pos`: The left upper coordinate for the element + /// - `size`: The size of the bitmap + pub fn new(pos: Coord, size: (u32, u32)) -> Self { + Self { + image: Buffer::Owned(vec![0; (size.0 * size.1) as usize * P::PIXEL_SIZE]), + size, + pos, + phantom: PhantomData, + } + } + + /// Create a new bitmap element with an pre-allocated owned buffer, this function will + /// take the ownership of the buffer. + /// + /// - `pos`: The left upper coordinate of the elelent + /// - `size`: The size of the bitmap + /// - `buf`: The buffer to use + /// - **returns**: The newly created image element, if the buffer isn't fit the image + /// dimension, this will returns an `None`. + pub fn with_owned_buffer(pos: Coord, size: (u32, u32), buf: Vec<u8>) -> Option<Self> { + if buf.len() < (size.0 * size.1) as usize * P::PIXEL_SIZE { + return None; + } + + Some(Self { + image: Buffer::Owned(buf), + size, + pos, + phantom: PhantomData, + }) + } + + /// Create a new bitmap element with a mut borrow to an existing buffer + /// + /// - `pos`: The left upper coordinate of the elelent + /// - `size`: The size of the bitmap + /// - `buf`: The buffer to use + /// - **returns**: The newly created image element, if the buffer isn't fit the image + /// dimension, this will returns an `None`. + pub fn with_mut(pos: Coord, size: (u32, u32), buf: &'a mut [u8]) -> Option<Self> { + if buf.len() < (size.0 * size.1) as usize * P::PIXEL_SIZE { + return None; + } + + Some(Self { + image: Buffer::BorrowedMut(buf), + size, + pos, + phantom: PhantomData, + }) + } + + /// Create a new bitmap element with a shared borrowed buffer. This means if we want to modifiy + /// the content of the image, the buffer is automatically copied + /// + /// - `pos`: The left upper coordinate of the elelent + /// - `size`: The size of the bitmap + /// - `buf`: The buffer to use + /// - **returns**: The newly created image element, if the buffer isn't fit the image + /// dimension, this will returns an `None`. + pub fn with_ref(pos: Coord, size: (u32, u32), buf: &'a [u8]) -> Option<Self> { + if buf.len() < (size.0 * size.1) as usize * P::PIXEL_SIZE { + return None; + } + + Some(Self { + image: Buffer::Borrowed(buf), + size, + pos, + phantom: PhantomData, + }) + } + + /// Copy the existing bitmap element to another location + /// + /// - `pos`: The new location to copy + pub fn copy_to<Coord2>(&self, pos: Coord2) -> BitMapElement<Coord2, P> { + BitMapElement { + image: Buffer::Borrowed(self.image.borrow()), + size: self.size, + pos, + phantom: PhantomData, + } + } + + /// Move the existing bitmap element to a new position + /// + /// - `pos`: The new position + pub fn move_to(&mut self, pos: Coord) { + self.pos = pos; + } + + /// Make the bitmap element as a bitmap backend, so that we can use + /// plotters drawing functionality on the bitmap element + pub fn as_bitmap_backend(&mut self) -> BitMapBackend<P> { + BitMapBackend::with_buffer_and_format(self.image.to_mut(), self.size).unwrap() + } +} + +#[cfg(all(not(target_arch = "wasm32"), feature = "image"))] +impl<'a, Coord> From<(Coord, DynamicImage)> for BitMapElement<'a, Coord, RGBPixel> { + fn from((pos, image): (Coord, DynamicImage)) -> Self { + let (w, h) = image.dimensions(); + let rgb_image = image.to_rgb().into_raw(); + Self { + pos, + image: Buffer::Owned(rgb_image), + size: (w, h), + phantom: PhantomData, + } + } +} + +#[cfg(all(not(target_arch = "wasm32"), feature = "image"))] +impl<'a, Coord> From<(Coord, DynamicImage)> + for BitMapElement<'a, Coord, crate::drawing::bitmap_pixel::BGRXPixel> +{ + fn from((pos, image): (Coord, DynamicImage)) -> Self { + let (w, h) = image.dimensions(); + let rgb_image = image.to_bgra().into_raw(); + Self { + pos, + image: Buffer::Owned(rgb_image), + size: (w, h), + phantom: PhantomData, + } + } +} + +impl<'a, 'b, Coord> PointCollection<'a, Coord> for &'a BitMapElement<'b, Coord> { + type Borrow = &'a Coord; + type IntoIter = std::iter::Once<&'a Coord>; + fn point_iter(self) -> Self::IntoIter { + std::iter::once(&self.pos) + } +} + +impl<'a, Coord, DB: DrawingBackend> Drawable<DB> for BitMapElement<'a, Coord> { + fn draw<I: Iterator<Item = BackendCoord>>( + &self, + mut points: I, + backend: &mut DB, + _: (u32, u32), + ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { + if let Some((x, y)) = points.next() { + // TODO: convert the pixel format when needed + return backend.blit_bitmap((x, y), self.size, self.image.as_ref()); + } + Ok(()) + } +} |