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