1 /*!
2   The Plotters backend API crate. This is a part of Plotters, the Rust drawing and plotting library, for more details regarding the entire
3   Plotters project, please check the [main crate](https://crates.io/crates/plotters).
4 
5   This is the crate that used as the connector between Plotters and different backend crates. Since Plotters 0.3, all the backends has been
6   hosted as seperate crates for the usability and maintainability reasons.
7 
8   At the same time, Plotters is now supporting third-party backends and all the backends are now supports "plug-and-play":
9   To use a external backend, just depends on both the Plotters main crate and the third-party backend crate.
10 
11   # Notes for implementing Backend for Plotters
12 
13   To create a new Plotters backend, this crate should be imported to the crate and the trait [DrawingBackend](trait.DrawingBackend.html) should
14   be implemented. It's highly recommended that the third-party backend uses `plotters-backend` by version specification `^x.y.*`.
15   For more details, see the [compatibility note](#compatibility-note).
16 
17   If the backend only implements [DrawingBackend::draw_pixel](trait.DrawingBackend.html#tymethod.draw_pixel), the default CPU rasterizer will be
18   used to give your backend ability of drawing different shapes. For those backend that supports advanced drawing instructions, such as, GPU
19   acelerated shape drawing, all the provided trait method can be overriden from the specific backend code.
20 
21   If your backend have text rendering ability, you may want to override the [DrawingBackend::estimate_text_size](trait.DrawingBackend.html#tymethod.estimate_text_size)
22   to avoid wrong spacing, since the Plotters default text handling code may behaves differently from the backend in terms of text rendering.
23 
24   ## Animated or Realtime Rendering
25   Backend might render the image realtimely/animated, for example, a GTK backend for realtime display or a GIF image rendering. To support these
26   features, you need to play with `ensure_prepared` and `present` method. The following figure illustrates how Plotters operates a drawing backend.
27 
28   - `ensure_prepared` - Called before each time when plotters want to draw. This function should initialize the backend for current frame, if the backend is already prepared
29      for a frame, this function should simply do nothing.
30   - `present` - Called when plotters want to finish current frame drawing
31 
32 
33   ```text
34                                         .ensure_prepared() &&
35     +-------------+    +-------------+    .draw_pixels()             +--------------+   drop
36     |Start drwaing|--->|Ready to draw| ------------------------+---->|Finish 1 frame| --------->
37     +-------------+    +-------------+                         |     +--------------+
38            ^                  ^                                |            |
39            |                  +------------------------------- +            |
40            |                            continue drawing                    |
41            +----------------------------------------------------------------+
42                                 start render the next frame
43                                         .present()
44   ```
45   - For both animated and static drawing, `DrawingBackend::present` indicates current frame should be flushed.
46   - For both animated and static drawing, `DrawingBackend::ensure_prepared` is called every time when plotters need to draw.
47   - For static drawing, the `DrawingBackend::present` is only called once manually, or from the Drop impl for the backend.
48   - For dynamic drawing, frames are defined by invocation of `DrawingBackend::present`, everything prior the invocation should belongs to previous frame
49 
50   # Compatibility Note
51   Since Plotters v0.3, plotters use the "plug-and-play" schema to import backends, this requires both Plotters and the backend crates depdens on a
52   same version of `plotters-backend` crate. This crate (`plotters-backend`) will enforce that any revision (means the last number in a version number)
53   won't contains breaking change - both on the Plotters side and backend side.
54 
55   Plotters main crate is always importing the backend crate with version specification `plotters-backend = "^<major>.<minor>*"`.
56   It's highly recommended that all the external crates follows the same rule to import `plotters-backend` depdendency, to avoid protential breaking
57   caused by `plotters-backend` crates gets a revision update.
58 
59   We also impose a versioning rule with `plotters` and some backends:
60   The compatible main crate (`plotters`) and this crate (`plotters-backend`) are always use the same major and minor version number.
61   All the plotters main crate and second-party backends with version "x.y.*" should be compatible, and they should depens on the latest version of `plotters-backend x.y.*`
62 
63 */
64 use std::error::Error;
65 
66 pub mod rasterizer;
67 mod style;
68 mod text;
69 
70 pub use style::{BackendColor, BackendStyle};
71 pub use text::{text_anchor, BackendTextStyle, FontFamily, FontStyle, FontTransform};
72 
73 use text_anchor::{HPos, VPos};
74 
75 /// A coordinate in the pixel-based backend. The coordinate follows the framebuffer's convention,
76 /// which defines the top-left point as (0, 0).
77 pub type BackendCoord = (i32, i32);
78 
79 /// The error produced by a drawing backend.
80 #[derive(Debug)]
81 pub enum DrawingErrorKind<E: Error + Send + Sync> {
82     /// A drawing backend error
83     DrawingError(E),
84     /// A font rendering error
85     FontError(Box<dyn Error + Send + Sync + 'static>),
86 }
87 
88 impl<E: Error + Send + Sync> std::fmt::Display for DrawingErrorKind<E> {
fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>89     fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
90         match self {
91             DrawingErrorKind::DrawingError(e) => write!(fmt, "Drawing backend error: {}", e),
92             DrawingErrorKind::FontError(e) => write!(fmt, "Font loading error: {}", e),
93         }
94     }
95 }
96 
97 impl<E: Error + Send + Sync> Error for DrawingErrorKind<E> {}
98 
99 ///  The drawing backend trait, which implements the low-level drawing APIs.
100 ///  This trait has a set of default implementation. And the minimal requirement of
101 ///  implementing a drawing backend is implementing the `draw_pixel` function.
102 ///
103 ///  If the drawing backend supports vector graphics, the other drawing APIs should be
104 ///  override by the backend specific implementation. Otherwise, the default implementation
105 ///  will use the pixel-based approach to draw other types of low-level shapes.
106 pub trait DrawingBackend: Sized {
107     /// The error type reported by the backend
108     type ErrorType: Error + Send + Sync;
109 
110     /// Get the dimension of the drawing backend in pixels
get_size(&self) -> (u32, u32)111     fn get_size(&self) -> (u32, u32);
112 
113     /// Ensure the backend is ready to draw
ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>114     fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>;
115 
116     /// Finalize the drawing step and present all the changes.
117     /// This is used as the real-time rendering support.
118     /// The backend may implement in the following way, when `ensure_prepared` is called
119     /// it checks if it needs a fresh buffer and `present` is called rendering all the
120     /// pending changes on the screen.
present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>121     fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>;
122 
123     /// Draw a pixel on the drawing backend
124     /// - `point`: The backend pixel-based coordinate to draw
125     /// - `color`: The color of the pixel
draw_pixel( &mut self, point: BackendCoord, color: BackendColor, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>126     fn draw_pixel(
127         &mut self,
128         point: BackendCoord,
129         color: BackendColor,
130     ) -> Result<(), DrawingErrorKind<Self::ErrorType>>;
131 
132     /// Draw a line on the drawing backend
133     /// - `from`: The start point of the line
134     /// - `to`: The end point of the line
135     /// - `style`: The style of the line
draw_line<S: BackendStyle>( &mut self, from: BackendCoord, to: BackendCoord, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>136     fn draw_line<S: BackendStyle>(
137         &mut self,
138         from: BackendCoord,
139         to: BackendCoord,
140         style: &S,
141     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
142         rasterizer::draw_line(self, from, to, style)
143     }
144 
145     /// Draw a rectangle on the drawing backend
146     /// - `upper_left`: The coordinate of the upper-left corner of the rect
147     /// - `bottom_right`: The coordinate of the bottom-right corner of the rect
148     /// - `style`: The style
149     /// - `fill`: If the rectangle should be filled
draw_rect<S: BackendStyle>( &mut self, upper_left: BackendCoord, bottom_right: BackendCoord, style: &S, fill: bool, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>150     fn draw_rect<S: BackendStyle>(
151         &mut self,
152         upper_left: BackendCoord,
153         bottom_right: BackendCoord,
154         style: &S,
155         fill: bool,
156     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
157         rasterizer::draw_rect(self, upper_left, bottom_right, style, fill)
158     }
159 
160     /// Draw a path on the drawing backend
161     /// - `path`: The iterator of key points of the path
162     /// - `style`: The style of the path
draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, path: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>163     fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
164         &mut self,
165         path: I,
166         style: &S,
167     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
168         if style.color().alpha == 0.0 {
169             return Ok(());
170         }
171 
172         if style.stroke_width() == 1 {
173             let mut begin: Option<BackendCoord> = None;
174             for end in path.into_iter() {
175                 if let Some(begin) = begin {
176                     let result = self.draw_line(begin, end, style);
177                     #[allow(clippy::question_mark)]
178                     if result.is_err() {
179                         return result;
180                     }
181                 }
182                 begin = Some(end);
183             }
184         } else {
185             let p: Vec<_> = path.into_iter().collect();
186             let v = rasterizer::polygonize(&p[..], style.stroke_width());
187             return self.fill_polygon(v, &style.color());
188         }
189         Ok(())
190     }
191 
192     /// Draw a circle on the drawing backend
193     /// - `center`: The center coordinate of the circle
194     /// - `radius`: The radius of the circle
195     /// - `style`: The style of the shape
196     /// - `fill`: If the circle should be filled
draw_circle<S: BackendStyle>( &mut self, center: BackendCoord, radius: u32, style: &S, fill: bool, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>197     fn draw_circle<S: BackendStyle>(
198         &mut self,
199         center: BackendCoord,
200         radius: u32,
201         style: &S,
202         fill: bool,
203     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
204         rasterizer::draw_circle(self, center, radius, style, fill)
205     }
206 
fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, vert: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>207     fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
208         &mut self,
209         vert: I,
210         style: &S,
211     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
212         let vert_buf: Vec<_> = vert.into_iter().collect();
213 
214         rasterizer::fill_polygon(self, &vert_buf[..], style)
215     }
216 
217     /// Draw a text on the drawing backend
218     /// - `text`: The text to draw
219     /// - `style`: The text style
220     /// - `pos` : The text anchor point
draw_text<TStyle: BackendTextStyle>( &mut self, text: &str, style: &TStyle, pos: BackendCoord, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>221     fn draw_text<TStyle: BackendTextStyle>(
222         &mut self,
223         text: &str,
224         style: &TStyle,
225         pos: BackendCoord,
226     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
227         let color = style.color();
228         if color.alpha == 0.0 {
229             return Ok(());
230         }
231 
232         let layout = style
233             .layout_box(text)
234             .map_err(|e| DrawingErrorKind::FontError(Box::new(e)))?;
235         let ((min_x, min_y), (max_x, max_y)) = layout;
236         let width = (max_x - min_x) as i32;
237         let height = (max_y - min_y) as i32;
238         let dx = match style.anchor().h_pos {
239             HPos::Left => 0,
240             HPos::Right => -width,
241             HPos::Center => -width / 2,
242         };
243         let dy = match style.anchor().v_pos {
244             VPos::Top => 0,
245             VPos::Center => -height / 2,
246             VPos::Bottom => -height,
247         };
248         let trans = style.transform();
249         let (w, h) = self.get_size();
250         match style.draw(text, (0, 0), |x, y, color| {
251             let (x, y) = trans.transform(x + dx - min_x, y + dy - min_y);
252             let (x, y) = (pos.0 + x, pos.1 + y);
253             if x >= 0 && x < w as i32 && y >= 0 && y < h as i32 {
254                 self.draw_pixel((x, y), color)
255             } else {
256                 Ok(())
257             }
258         }) {
259             Ok(drawing_result) => drawing_result,
260             Err(font_error) => Err(DrawingErrorKind::FontError(Box::new(font_error))),
261         }
262     }
263 
264     /// Estimate the size of the horizontal text if rendered on this backend.
265     /// This is important because some of the backend may not have font ability.
266     /// Thus this allows those backend reports proper value rather than ask the
267     /// font rasterizer for that.
268     ///
269     /// - `text`: The text to estimate
270     /// - `font`: The font to estimate
271     /// - *Returns* The estimated text size
estimate_text_size<TStyle: BackendTextStyle>( &self, text: &str, style: &TStyle, ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>>272     fn estimate_text_size<TStyle: BackendTextStyle>(
273         &self,
274         text: &str,
275         style: &TStyle,
276     ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
277         let layout = style
278             .layout_box(text)
279             .map_err(|e| DrawingErrorKind::FontError(Box::new(e)))?;
280         Ok((
281             ((layout.1).0 - (layout.0).0) as u32,
282             ((layout.1).1 - (layout.0).1) as u32,
283         ))
284     }
285 
286     /// Blit a bitmap on to the backend.
287     ///
288     /// - `text`: pos the left upper conner of the bitmap to blit
289     /// - `src`: The source of the image
290     ///
291     /// TODO: The default implementation of bitmap blitting assumes that the bitmap is RGB, but
292     /// this may not be the case. But for bitmap backend it's actually ok if we use the bitmap
293     /// element that matches the pixel format, but we need to fix this.
blit_bitmap( &mut self, pos: BackendCoord, (iw, ih): (u32, u32), src: &[u8], ) -> Result<(), DrawingErrorKind<Self::ErrorType>>294     fn blit_bitmap(
295         &mut self,
296         pos: BackendCoord,
297         (iw, ih): (u32, u32),
298         src: &[u8],
299     ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
300         let (w, h) = self.get_size();
301 
302         for dx in 0..iw {
303             if pos.0 + dx as i32 >= w as i32 {
304                 break;
305             }
306             for dy in 0..ih {
307                 if pos.1 + dy as i32 >= h as i32 {
308                     break;
309                 }
310                 // FIXME: This assume we have RGB image buffer
311                 let r = src[(dx + dy * w) as usize * 3];
312                 let g = src[(dx + dy * w) as usize * 3 + 1];
313                 let b = src[(dx + dy * w) as usize * 3 + 2];
314                 let color = BackendColor {
315                     alpha: 1.0,
316                     rgb: (r, g, b),
317                 };
318                 let result = self.draw_pixel((pos.0 + dx as i32, pos.1 + dy as i32), color);
319                 #[allow(clippy::question_mark)]
320                 if result.is_err() {
321                     return result;
322                 }
323             }
324         }
325 
326         Ok(())
327     }
328 }
329