| 1 | #[cfg (all( |
| 2 | not(all(target_arch = "wasm32" , not(target_os = "wasi" ))), |
| 3 | feature = "image" |
| 4 | ))] |
| 5 | use image::{DynamicImage, GenericImageView}; |
| 6 | |
| 7 | use super::{Drawable, PointCollection}; |
| 8 | use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; |
| 9 | |
| 10 | use plotters_bitmap::bitmap_pixel::{PixelFormat, RGBPixel}; |
| 11 | |
| 12 | #[cfg (all( |
| 13 | not(all(target_arch = "wasm32" , not(target_os = "wasi" ))), |
| 14 | feature = "image" |
| 15 | ))] |
| 16 | use plotters_bitmap::bitmap_pixel::BGRXPixel; |
| 17 | |
| 18 | use plotters_bitmap::BitMapBackend; |
| 19 | |
| 20 | use std::borrow::Borrow; |
| 21 | use std::marker::PhantomData; |
| 22 | |
| 23 | enum Buffer<'a> { |
| 24 | Owned(Vec<u8>), |
| 25 | Borrowed(&'a [u8]), |
| 26 | BorrowedMut(&'a mut [u8]), |
| 27 | } |
| 28 | |
| 29 | impl<'a> Borrow<[u8]> for Buffer<'a> { |
| 30 | fn borrow(&self) -> &[u8] { |
| 31 | self.as_ref() |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | impl AsRef<[u8]> for Buffer<'_> { |
| 36 | fn as_ref(&self) -> &[u8] { |
| 37 | match self { |
| 38 | Buffer::Owned(owned: &Vec) => owned.as_ref(), |
| 39 | Buffer::Borrowed(target: &&[u8]) => target, |
| 40 | Buffer::BorrowedMut(target: &&mut [u8]) => target, |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | impl<'a> Buffer<'a> { |
| 46 | fn to_mut(&mut self) -> &mut [u8] { |
| 47 | let owned: Vec = match self { |
| 48 | Buffer::Owned(owned: &mut Vec) => return &mut owned[..], |
| 49 | Buffer::BorrowedMut(target: &mut &mut [u8]) => return target, |
| 50 | Buffer::Borrowed(target: &mut &[u8]) => { |
| 51 | let mut value: Vec = vec![]; |
| 52 | value.extend_from_slice(target); |
| 53 | value |
| 54 | } |
| 55 | }; |
| 56 | |
| 57 | *self = Buffer::Owned(owned); |
| 58 | self.to_mut() |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | /// The element that contains a bitmap on it |
| 63 | pub struct BitMapElement<'a, Coord, P: PixelFormat = RGBPixel> { |
| 64 | image: Buffer<'a>, |
| 65 | size: (u32, u32), |
| 66 | pos: Coord, |
| 67 | phantom: PhantomData<P>, |
| 68 | } |
| 69 | |
| 70 | impl<'a, Coord, P: PixelFormat> BitMapElement<'a, Coord, P> { |
| 71 | /// Create a new empty bitmap element. This can be use as |
| 72 | /// the draw and blit pattern. |
| 73 | /// |
| 74 | /// - `pos`: The left upper coordinate for the element |
| 75 | /// - `size`: The size of the bitmap |
| 76 | pub fn new(pos: Coord, size: (u32, u32)) -> Self { |
| 77 | Self { |
| 78 | image: Buffer::Owned(vec![0; (size.0 * size.1) as usize * P::PIXEL_SIZE]), |
| 79 | size, |
| 80 | pos, |
| 81 | phantom: PhantomData, |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | /// Create a new bitmap element with an pre-allocated owned buffer, this function will |
| 86 | /// take the ownership of the buffer. |
| 87 | /// |
| 88 | /// - `pos`: The left upper coordinate of the elelent |
| 89 | /// - `size`: The size of the bitmap |
| 90 | /// - `buf`: The buffer to use |
| 91 | /// - **returns**: The newly created image element, if the buffer isn't fit the image |
| 92 | /// dimension, this will returns an `None`. |
| 93 | pub fn with_owned_buffer(pos: Coord, size: (u32, u32), buf: Vec<u8>) -> Option<Self> { |
| 94 | if buf.len() < (size.0 * size.1) as usize * P::PIXEL_SIZE { |
| 95 | return None; |
| 96 | } |
| 97 | |
| 98 | Some(Self { |
| 99 | image: Buffer::Owned(buf), |
| 100 | size, |
| 101 | pos, |
| 102 | phantom: PhantomData, |
| 103 | }) |
| 104 | } |
| 105 | |
| 106 | /// Create a new bitmap element with a mut borrow to an existing buffer |
| 107 | /// |
| 108 | /// - `pos`: The left upper coordinate of the elelent |
| 109 | /// - `size`: The size of the bitmap |
| 110 | /// - `buf`: The buffer to use |
| 111 | /// - **returns**: The newly created image element, if the buffer isn't fit the image |
| 112 | /// dimension, this will returns an `None`. |
| 113 | pub fn with_mut(pos: Coord, size: (u32, u32), buf: &'a mut [u8]) -> Option<Self> { |
| 114 | if buf.len() < (size.0 * size.1) as usize * P::PIXEL_SIZE { |
| 115 | return None; |
| 116 | } |
| 117 | |
| 118 | Some(Self { |
| 119 | image: Buffer::BorrowedMut(buf), |
| 120 | size, |
| 121 | pos, |
| 122 | phantom: PhantomData, |
| 123 | }) |
| 124 | } |
| 125 | |
| 126 | /// Create a new bitmap element with a shared borrowed buffer. This means if we want to modify |
| 127 | /// the content of the image, the buffer is automatically copied |
| 128 | /// |
| 129 | /// - `pos`: The left upper coordinate of the elelent |
| 130 | /// - `size`: The size of the bitmap |
| 131 | /// - `buf`: The buffer to use |
| 132 | /// - **returns**: The newly created image element, if the buffer isn't fit the image |
| 133 | /// dimension, this will returns an `None`. |
| 134 | pub fn with_ref(pos: Coord, size: (u32, u32), buf: &'a [u8]) -> Option<Self> { |
| 135 | if buf.len() < (size.0 * size.1) as usize * P::PIXEL_SIZE { |
| 136 | return None; |
| 137 | } |
| 138 | |
| 139 | Some(Self { |
| 140 | image: Buffer::Borrowed(buf), |
| 141 | size, |
| 142 | pos, |
| 143 | phantom: PhantomData, |
| 144 | }) |
| 145 | } |
| 146 | |
| 147 | /// Copy the existing bitmap element to another location |
| 148 | /// |
| 149 | /// - `pos`: The new location to copy |
| 150 | pub fn copy_to<Coord2>(&self, pos: Coord2) -> BitMapElement<Coord2, P> { |
| 151 | BitMapElement { |
| 152 | image: Buffer::Borrowed(self.image.borrow()), |
| 153 | size: self.size, |
| 154 | pos, |
| 155 | phantom: PhantomData, |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | /// Move the existing bitmap element to a new position |
| 160 | /// |
| 161 | /// - `pos`: The new position |
| 162 | pub fn move_to(&mut self, pos: Coord) { |
| 163 | self.pos = pos; |
| 164 | } |
| 165 | |
| 166 | /// Make the bitmap element as a bitmap backend, so that we can use |
| 167 | /// plotters drawing functionality on the bitmap element |
| 168 | pub fn as_bitmap_backend(&mut self) -> BitMapBackend<P> { |
| 169 | BitMapBackend::with_buffer_and_format(self.image.to_mut(), self.size).unwrap() |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | #[cfg (all( |
| 174 | not(all(target_arch = "wasm32" , not(target_os = "wasi" ))), |
| 175 | feature = "image" |
| 176 | ))] |
| 177 | impl<'a, Coord> From<(Coord, DynamicImage)> for BitMapElement<'a, Coord, RGBPixel> { |
| 178 | fn from((pos, image): (Coord, DynamicImage)) -> Self { |
| 179 | let (w, h) = image.dimensions(); |
| 180 | let rgb_image = image.to_rgb8().into_raw(); |
| 181 | Self { |
| 182 | pos, |
| 183 | image: Buffer::Owned(rgb_image), |
| 184 | size: (w, h), |
| 185 | phantom: PhantomData, |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | #[cfg (all( |
| 191 | not(all(target_arch = "wasm32" , not(target_os = "wasi" ))), |
| 192 | feature = "image" |
| 193 | ))] |
| 194 | impl<'a, Coord> From<(Coord, DynamicImage)> for BitMapElement<'a, Coord, BGRXPixel> { |
| 195 | fn from((pos, image): (Coord, DynamicImage)) -> Self { |
| 196 | let (w, h) = image.dimensions(); |
| 197 | let rgb_image = image.to_rgb8().into_raw(); |
| 198 | Self { |
| 199 | pos, |
| 200 | image: Buffer::Owned(rgb_image), |
| 201 | size: (w, h), |
| 202 | phantom: PhantomData, |
| 203 | } |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | impl<'a, 'b, Coord> PointCollection<'a, Coord> for &'a BitMapElement<'b, Coord> { |
| 208 | type Point = &'a Coord; |
| 209 | type IntoIter = std::iter::Once<&'a Coord>; |
| 210 | fn point_iter(self) -> Self::IntoIter { |
| 211 | std::iter::once(&self.pos) |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | impl<'a, Coord, DB: DrawingBackend> Drawable<DB> for BitMapElement<'a, Coord> { |
| 216 | fn draw<I: Iterator<Item = BackendCoord>>( |
| 217 | &self, |
| 218 | mut points: I, |
| 219 | backend: &mut DB, |
| 220 | _: (u32, u32), |
| 221 | ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { |
| 222 | if let Some((x: i32, y: i32)) = points.next() { |
| 223 | // TODO: convert the pixel format when needed |
| 224 | return backend.blit_bitmap((x, y), self.size, self.image.as_ref()); |
| 225 | } |
| 226 | Ok(()) |
| 227 | } |
| 228 | } |
| 229 | |