plotters_bitmap/
bitmap.rs

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 occur (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 manipulates 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 split 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;