diff options
Diffstat (limited to 'src/drawing/backend.rs')
-rw-r--r-- | src/drawing/backend.rs | 284 |
1 files changed, 0 insertions, 284 deletions
diff --git a/src/drawing/backend.rs b/src/drawing/backend.rs deleted file mode 100644 index a5ba54b..0000000 --- a/src/drawing/backend.rs +++ /dev/null @@ -1,284 +0,0 @@ -use crate::style::text_anchor::{HPos, VPos}; -use crate::style::{Color, FontDesc, FontError, RGBAColor, ShapeStyle, TextStyle}; -use std::error::Error; - -/// A coordinate in the image -pub type BackendCoord = (i32, i32); - -/// The error produced by a drawing backend -#[derive(Debug)] -pub enum DrawingErrorKind<E: Error + Send + Sync> -where - FontError: Send + Sync, -{ - /// A drawing backend error - DrawingError(E), - /// A font rendering error - FontError(FontError), -} - -impl<E: Error + Send + Sync> std::fmt::Display for DrawingErrorKind<E> { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - match self { - DrawingErrorKind::DrawingError(e) => write!(fmt, "Drawing backend error: {}", e), - DrawingErrorKind::FontError(e) => write!(fmt, "Font loading error: {}", e), - } - } -} - -impl<E: Error + Send + Sync> Error for DrawingErrorKind<E> {} - -/// The style data for the backend drawing API -pub trait BackendStyle { - /// The underlying type represents the color for this style - type ColorType: Color; - - /// Convert the style into the underlying color - fn as_color(&self) -> RGBAColor; - - // TODO: In the future we should support stroke width, line shape, etc.... - fn stroke_width(&self) -> u32 { - 1 - } -} - -impl<T: Color> BackendStyle for T { - type ColorType = T; - fn as_color(&self) -> RGBAColor { - self.to_rgba() - } -} - -impl BackendStyle for ShapeStyle { - type ColorType = RGBAColor; - fn as_color(&self) -> RGBAColor { - self.color.clone() - } - fn stroke_width(&self) -> u32 { - self.stroke_width - } -} - -/// The drawing backend trait, which implements the low-level drawing APIs. -/// This trait has a set of default implementation. And the minimal requirement of -/// implementing a drawing backend is implementing the `draw_pixel` function. -/// -/// If the drawing backend supports vector graphics, the other drawing APIs should be -/// override by the backend specific implementation. Otherwise, the default implementation -/// will use the pixel-based approach to draw other types of low-level shapes. -pub trait DrawingBackend: Sized { - /// The error type reported by the backend - type ErrorType: Error + Send + Sync; - - /// Get the dimension of the drawing backend in pixels - fn get_size(&self) -> (u32, u32); - - /// Ensure the backend is ready to draw - fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>; - - /// Finalize the drawing step and present all the changes. - /// This is used as the real-time rendering support. - /// The backend may implement in the following way, when `ensure_prepared` is called - /// it checks if it needs a fresh buffer and `present` is called rendering all the - /// pending changes on the screen. - fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>; - - /// Draw a pixel on the drawing backend - /// - `point`: The backend pixel-based coordinate to draw - /// - `color`: The color of the pixel - fn draw_pixel( - &mut self, - point: BackendCoord, - color: &RGBAColor, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>>; - - /// Draw a line on the drawing backend - /// - `from`: The start point of the line - /// - `to`: The end point of the line - /// - `style`: The style of the line - fn draw_line<S: BackendStyle>( - &mut self, - from: BackendCoord, - to: BackendCoord, - style: &S, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - super::rasterizer::draw_line(self, from, to, style) - } - - /// Draw a rectangle on the drawing backend - /// - `upper_left`: The coordinate of the upper-left corner of the rect - /// - `bottom_right`: The coordinate of the bottom-right corner of the rect - /// - `style`: The style - /// - `fill`: If the rectangle should be filled - fn draw_rect<S: BackendStyle>( - &mut self, - upper_left: BackendCoord, - bottom_right: BackendCoord, - style: &S, - fill: bool, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - super::rasterizer::draw_rect(self, upper_left, bottom_right, style, fill) - } - - /// Draw a path on the drawing backend - /// - `path`: The iterator of key points of the path - /// - `style`: The style of the path - fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( - &mut self, - path: I, - style: &S, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - if style.as_color().alpha() == 0.0 { - return Ok(()); - } - - if style.stroke_width() == 1 { - let mut begin: Option<BackendCoord> = None; - for end in path.into_iter() { - if let Some(begin) = begin { - let result = self.draw_line(begin, end, style); - if result.is_err() { - return result; - } - } - begin = Some(end); - } - } else { - let p: Vec<_> = path.into_iter().collect(); - let v = super::rasterizer::polygonize(&p[..], style.stroke_width()); - return self.fill_polygon(v, &style.as_color()); - } - Ok(()) - } - - /// Draw a circle on the drawing backend - /// - `center`: The center coordinate of the circle - /// - `radius`: The radius of the circle - /// - `style`: The style of the shape - /// - `fill`: If the circle should be filled - fn draw_circle<S: BackendStyle>( - &mut self, - center: BackendCoord, - radius: u32, - style: &S, - fill: bool, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - super::rasterizer::draw_circle(self, center, radius, style, fill) - } - - fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( - &mut self, - vert: I, - style: &S, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - let vert_buf: Vec<_> = vert.into_iter().collect(); - - super::rasterizer::fill_polygon(self, &vert_buf[..], style) - } - - /// Draw a text on the drawing backend - /// - `text`: The text to draw - /// - `style`: The text style - /// - `pos` : The text anchor point - fn draw_text( - &mut self, - text: &str, - style: &TextStyle, - pos: BackendCoord, - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - let font = &style.font; - let color = &style.color; - if color.alpha() == 0.0 { - return Ok(()); - } - - let layout = font.layout_box(text).map_err(DrawingErrorKind::FontError)?; - let ((min_x, min_y), (max_x, max_y)) = layout; - let width = (max_x - min_x) as i32; - let height = (max_y - min_y) as i32; - let dx = match style.pos.h_pos { - HPos::Left => 0, - HPos::Right => -width, - HPos::Center => -width / 2, - }; - let dy = match style.pos.v_pos { - VPos::Top => 0, - VPos::Center => -height / 2, - VPos::Bottom => -height, - }; - let trans = font.get_transform(); - let (w, h) = self.get_size(); - match font.draw(text, (0, 0), |x, y, v| { - let (x, y) = trans.transform(x + dx - min_x, y + dy - min_y); - let (x, y) = (pos.0 + x, pos.1 + y); - if x >= 0 && x < w as i32 && y >= 0 && y < h as i32 { - self.draw_pixel((x, y), &color.mix(f64::from(v))) - } else { - Ok(()) - } - }) { - Ok(drawing_result) => drawing_result, - Err(font_error) => Err(DrawingErrorKind::FontError(font_error)), - } - } - - /// Estimate the size of the horizontal text if rendered on this backend. - /// This is important because some of the backend may not have font ability. - /// Thus this allows those backend reports proper value rather than ask the - /// font rasterizer for that. - /// - /// - `text`: The text to estimate - /// - `font`: The font to estimate - /// - *Returns* The estimated text size - fn estimate_text_size<'a>( - &self, - text: &str, - font: &FontDesc<'a>, - ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> { - let layout = font.layout_box(text).map_err(DrawingErrorKind::FontError)?; - Ok(( - ((layout.1).0 - (layout.0).0) as u32, - ((layout.1).1 - (layout.0).1) as u32, - )) - } - - /// Blit a bitmap on to the backend. - /// - /// - `text`: pos the left upper conner of the bitmap to blit - /// - `src`: The source of the image - /// - /// TODO: The default implementation of bitmap blitting assumes that the bitmap is RGB, but - /// this may not be the case. But for bitmap backend it's actually ok if we use the bitmap - /// element that matches the pixel format, but we need to fix this. - fn blit_bitmap<'a>( - &mut self, - pos: BackendCoord, - (iw, ih): (u32, u32), - src: &'a [u8], - ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { - let (w, h) = self.get_size(); - - for dx in 0..iw { - if pos.0 + dx as i32 >= w as i32 { - break; - } - for dy in 0..ih { - if pos.1 + dy as i32 >= h as i32 { - break; - } - // FIXME: This assume we have RGB image buffer - let r = src[(dx + dy * iw) as usize * 3]; - let g = src[(dx + dy * iw) as usize * 3 + 1]; - let b = src[(dx + dy * iw) as usize * 3 + 2]; - let color = crate::style::RGBColor(r, g, b); - let result = - self.draw_pixel((pos.0 + dx as i32, pos.1 + dy as i32), &color.to_rgba()); - if result.is_err() { - return result; - } - } - } - - Ok(()) - } -} |