1#[cfg(all(
2 not(all(target_arch = "wasm32", not(target_os = "wasi"))),
3 feature = "image"
4))]
5use image::{DynamicImage, GenericImageView};
6
7use super::{Drawable, PointCollection};
8use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
9
10use plotters_bitmap::bitmap_pixel::{PixelFormat, RGBPixel};
11
12#[cfg(all(
13 not(all(target_arch = "wasm32", not(target_os = "wasi"))),
14 feature = "image"
15))]
16use plotters_bitmap::bitmap_pixel::BGRXPixel;
17
18use plotters_bitmap::BitMapBackend;
19
20use std::borrow::Borrow;
21use std::marker::PhantomData;
22
23enum Buffer<'a> {
24 Owned(Vec<u8>),
25 Borrowed(&'a [u8]),
26 BorrowedMut(&'a mut [u8]),
27}
28
29impl<'a> Borrow<[u8]> for Buffer<'a> {
30 fn borrow(&self) -> &[u8] {
31 self.as_ref()
32 }
33}
34
35impl AsRef<[u8]> for Buffer<'_> {
36 fn as_ref(&self) -> &[u8] {
37 match self {
38 Buffer::Owned(owned) => owned.as_ref(),
39 Buffer::Borrowed(target) => target,
40 Buffer::BorrowedMut(target) => target,
41 }
42 }
43}
44
45impl<'a> Buffer<'a> {
46 fn to_mut(&mut self) -> &mut [u8] {
47 let owned = match self {
48 Buffer::Owned(owned) => return &mut owned[..],
49 Buffer::BorrowedMut(target) => return target,
50 Buffer::Borrowed(target) => {
51 let mut value = 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
63pub struct BitMapElement<'a, Coord, P: PixelFormat = RGBPixel> {
64 image: Buffer<'a>,
65 size: (u32, u32),
66 pos: Coord,
67 phantom: PhantomData<P>,
68}
69
70impl<'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))]
177impl<'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))]
194impl<'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
207impl<'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
215impl<'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, y)) = 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