1use plotters_backend::{
2 BackendColor, BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind,
3};
4use std::marker::PhantomData;
5
6use crate::bitmap_pixel::{PixelFormat, RGBPixel};
7use crate::error::BitMapBackendError;
8
9#[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
10mod image_encoding_support {
11 pub(super) use image::{ImageBuffer, Rgb};
12 pub(super) use std::path::Path;
13 pub(super) type BorrowedImage<'a> = ImageBuffer<Rgb<u8>, &'a mut [u8]>;
14}
15
16#[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
17use image_encoding_support::*;
18
19mod target;
20
21use target::{Buffer, Target};
22
23/// The backend that drawing a bitmap
24///
25/// # Warning
26///
27/// You should call [`.present()?`](plotters_backend::DrawingBackend::present) on a
28/// `BitMapBackend`, not just `drop` it or allow it to go out of scope.
29///
30/// If the `BitMapBackend` is just dropped, it will make a best effort attempt to write the
31/// generated charts to the output file, but any errors that occcur (such as inability to
32/// create the output file) will be silently ignored.
33pub struct BitMapBackend<'a, P: PixelFormat = RGBPixel> {
34 /// The path to the image
35 #[allow(dead_code)]
36 target: Target<'a>,
37 /// The size of the image
38 size: (u32, u32),
39 /// The data buffer of the image
40 buffer: Buffer<'a>,
41 /// Flag indicates if the bitmap has been saved
42 saved: bool,
43 _pantomdata: PhantomData<P>,
44}
45
46impl<'a, P: PixelFormat> BitMapBackend<'a, P> {
47 /// The number of bytes per pixel
48 const PIXEL_SIZE: usize = P::PIXEL_SIZE;
49}
50
51impl<'a> BitMapBackend<'a, RGBPixel> {
52 /// Create a new bitmap backend
53 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
54 pub fn new<T: AsRef<Path> + ?Sized>(path: &'a T, (w, h): (u32, u32)) -> Self {
55 Self {
56 target: Target::File(path.as_ref()),
57 size: (w, h),
58 buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
59 saved: false,
60 _pantomdata: PhantomData,
61 }
62 }
63
64 /// Create a new bitmap backend that generate GIF animation
65 ///
66 /// When this is used, the bitmap backend acts similar to a real-time rendering backend.
67 /// When the program finished drawing one frame, use `present` function to flush the frame
68 /// into the GIF file.
69 ///
70 /// - `path`: The path to the GIF file to create
71 /// - `dimension`: The size of the GIF image
72 /// - `speed`: The amount of time for each frame to display
73 #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
74 pub fn gif<T: AsRef<Path>>(
75 path: T,
76 (w, h): (u32, u32),
77 frame_delay: u32,
78 ) -> Result<Self, BitMapBackendError> {
79 Ok(Self {
80 target: Target::Gif(Box::new(crate::gif_support::GifFile::new(
81 path,
82 (w, h),
83 frame_delay,
84 )?)),
85 size: (w, h),
86 buffer: Buffer::Owned(vec![0; Self::PIXEL_SIZE * (w * h) as usize]),
87 saved: false,
88 _pantomdata: PhantomData,
89 })
90 }
91
92 /// Create a new bitmap backend which only lives in-memory
93 ///
94 /// When this is used, the bitmap backend will write to a user provided [u8] array (or Vec<u8>)
95 /// in RGB pixel format.
96 ///
97 /// Note: This function provides backward compatibility for those code that assumes Plotters
98 /// uses RGB pixel format and maniuplates the in-memory framebuffer.
99 /// For more pixel format option, use `with_buffer_and_format` instead.
100 ///
101 /// - `buf`: The buffer to operate
102 /// - `dimension`: The size of the image in pixels
103 /// - **returns**: The newly created bitmap backend
104 pub fn with_buffer(buf: &'a mut [u8], (w, h): (u32, u32)) -> Self {
105 Self::with_buffer_and_format(buf, (w, h)).expect("Wrong buffer size")
106 }
107}
108
109impl<'a, P: PixelFormat> BitMapBackend<'a, P> {
110 /// Create a new bitmap backend with a in-memory buffer with specific pixel format.
111 ///
112 /// Note: This can be used as a way to manipulate framebuffer, `mmap` can be used on the top of this
113 /// as well.
114 ///
115 /// - `buf`: The buffer to operate
116 /// - `dimension`: The size of the image in pixels
117 /// - **returns**: The newly created bitmap backend
118 pub fn with_buffer_and_format(
119 buf: &'a mut [u8],
120 (w, h): (u32, u32),
121 ) -> Result<Self, BitMapBackendError> {
122 if (w * h) as usize * Self::PIXEL_SIZE > buf.len() {
123 return Err(BitMapBackendError::InvalidBuffer);
124 }
125
126 Ok(Self {
127 target: Target::Buffer(PhantomData),
128 size: (w, h),
129 buffer: Buffer::Borrowed(buf),
130 saved: false,
131 _pantomdata: PhantomData,
132 })
133 }
134
135 #[inline(always)]
136 pub(crate) fn get_raw_pixel_buffer(&mut self) -> &mut [u8] {
137 self.buffer.borrow_buffer()
138 }
139
140 /// Split a bitmap backend vertically into several sub drawing area which allows
141 /// multi-threading rendering.
142 ///
143 /// - `area_size`: The size of the area
144 /// - **returns**: The splitted backends that can be rendered in parallel
145 pub fn split(&mut self, area_size: &[u32]) -> Vec<BitMapBackend<P>> {
146 let (w, h) = self.get_size();
147 let buf = self.get_raw_pixel_buffer();
148
149 let base_addr = &mut buf[0] as *mut u8;
150 let mut split_points = vec![0];
151 for size in area_size {
152 let next = split_points.last().unwrap() + size;
153 if next >= h {
154 break;
155 }
156 split_points.push(next);
157 }
158 split_points.push(h);
159
160 split_points
161 .iter()
162 .zip(split_points.iter().skip(1))
163 .map(|(begin, end)| {
164 let actual_buf = unsafe {
165 std::slice::from_raw_parts_mut(
166 base_addr.offset((begin * w) as isize * Self::PIXEL_SIZE as isize),
167 ((end - begin) * w) as usize * Self::PIXEL_SIZE,
168 )
169 };
170 Self::with_buffer_and_format(actual_buf, (w, end - begin)).unwrap()
171 })
172 .collect()
173 }
174}
175
176impl<'a, P: PixelFormat> DrawingBackend for BitMapBackend<'a, P> {
177 type ErrorType = BitMapBackendError;
178
179 fn get_size(&self) -> (u32, u32) {
180 self.size
181 }
182
183 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
184 self.saved = false;
185 Ok(())
186 }
187
188 #[cfg(any(target_arch = "wasm32", not(feature = "image")))]
189 fn present(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
190 Ok(())
191 }
192
193 #[cfg(all(not(target_arch = "wasm32"), feature = "image"))]
194 fn present(&mut self) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
195 if !P::can_be_saved() {
196 return Ok(());
197 }
198 let (w, h) = self.get_size();
199 match &mut self.target {
200 Target::File(path) => {
201 if let Some(img) = BorrowedImage::from_raw(w, h, self.buffer.borrow_buffer()) {
202 img.save(&path).map_err(|x| {
203 DrawingErrorKind::DrawingError(BitMapBackendError::ImageError(x))
204 })?;
205 self.saved = true;
206 Ok(())
207 } else {
208 Err(DrawingErrorKind::DrawingError(
209 BitMapBackendError::InvalidBuffer,
210 ))
211 }
212 }
213 Target::Buffer(_) => Ok(()),
214
215 #[cfg(all(feature = "gif", not(target_arch = "wasm32"), feature = "image"))]
216 Target::Gif(target) => {
217 target
218 .flush_frame(self.buffer.borrow_buffer())
219 .map_err(DrawingErrorKind::DrawingError)?;
220 self.saved = true;
221 Ok(())
222 }
223 }
224 }
225
226 fn draw_pixel(
227 &mut self,
228 point: BackendCoord,
229 color: BackendColor,
230 ) -> Result<(), DrawingErrorKind<BitMapBackendError>> {
231 if point.0 < 0
232 || point.1 < 0
233 || point.0 as u32 >= self.size.0
234 || point.1 as u32 >= self.size.1
235 {
236 return Ok(());
237 }
238
239 let alpha = color.alpha;
240 let rgb = color.rgb;
241
242 P::draw_pixel(self, point, rgb, alpha);
243
244 Ok(())
245 }
246
247 fn draw_line<S: BackendStyle>(
248 &mut self,
249 from: (i32, i32),
250 to: (i32, i32),
251 style: &S,
252 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
253 let alpha = style.color().alpha;
254 let (r, g, b) = style.color().rgb;
255
256 if (from.0 == to.0 || from.1 == to.1) && style.stroke_width() == 1 {
257 if alpha >= 1.0 {
258 if from.1 == to.1 {
259 P::fill_rect_fast(self, from, (to.0 + 1, to.1 + 1), r, g, b);
260 } else {
261 P::fill_vertical_line_fast(self, from.0, (from.1, to.1), r, g, b);
262 }
263 } else {
264 P::blend_rect_fast(self, from, (to.0 + 1, to.1 + 1), r, g, b, alpha);
265 }
266 return Ok(());
267 }
268
269 plotters_backend::rasterizer::draw_line(self, from, to, style)
270 }
271
272 fn draw_rect<S: BackendStyle>(
273 &mut self,
274 upper_left: (i32, i32),
275 bottom_right: (i32, i32),
276 style: &S,
277 fill: bool,
278 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
279 let alpha = style.color().alpha;
280 let (r, g, b) = style.color().rgb;
281 if fill {
282 if alpha >= 1.0 {
283 P::fill_rect_fast(self, upper_left, bottom_right, r, g, b);
284 } else {
285 P::blend_rect_fast(self, upper_left, bottom_right, r, g, b, alpha);
286 }
287 return Ok(());
288 }
289 plotters_backend::rasterizer::draw_rect(self, upper_left, bottom_right, style, fill)
290 }
291
292 fn blit_bitmap(
293 &mut self,
294 pos: BackendCoord,
295 (sw, sh): (u32, u32),
296 src: &[u8],
297 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
298 let (dw, dh) = self.get_size();
299
300 let (x0, y0) = pos;
301 let (x1, y1) = (x0 + sw as i32, y0 + sh as i32);
302
303 let (x0, y0, x1, y1) = (x0.max(0), y0.max(0), x1.min(dw as i32), y1.min(dh as i32));
304
305 if x0 == x1 || y0 == y1 {
306 return Ok(());
307 }
308
309 let mut chunk_size = (x1 - x0) as usize;
310 let mut num_chunks = (y1 - y0) as usize;
311 let dst_gap = dw as usize - chunk_size;
312 let src_gap = sw as usize - chunk_size;
313
314 let dst_start = Self::PIXEL_SIZE * (y0 as usize * dw as usize + x0 as usize);
315
316 let mut dst = &mut self.get_raw_pixel_buffer()[dst_start..];
317
318 let src_start =
319 Self::PIXEL_SIZE * ((sh as i32 + y0 - y1) * sw as i32 + (sw as i32 + x0 - x1)) as usize;
320 let mut src = &src[src_start..];
321
322 if src_gap == 0 && dst_gap == 0 {
323 chunk_size *= num_chunks;
324 num_chunks = 1;
325 }
326 for i in 0..num_chunks {
327 dst[0..(chunk_size * Self::PIXEL_SIZE)]
328 .copy_from_slice(&src[0..(chunk_size * Self::PIXEL_SIZE)]);
329 if i != num_chunks - 1 {
330 dst = &mut dst[((chunk_size + dst_gap) * Self::PIXEL_SIZE)..];
331 src = &src[((chunk_size + src_gap) * Self::PIXEL_SIZE)..];
332 }
333 }
334
335 Ok(())
336 }
337}
338
339impl<P: PixelFormat> Drop for BitMapBackend<'_, P> {
340 fn drop(&mut self) {
341 if !self.saved {
342 // drop should not panic, so we ignore a failed present
343 let _ = self.present();
344 }
345 }
346}
347
348#[cfg(test)]
349mod test;
350