plotters/element/
image.rs

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 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))]
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}