1 use crate::style::text_anchor::{HPos, VPos}; 2 use crate::style::{Color, FontDesc, FontError, RGBAColor, ShapeStyle, TextStyle}; 3 use std::error::Error; 4 5 /// A coordinate in the image 6 pub type BackendCoord = (i32, i32); 7 8 /// The error produced by a drawing backend 9 #[derive(Debug)] 10 pub enum DrawingErrorKind<E: Error + Send + Sync> 11 where 12 FontError: Send + Sync, 13 { 14 /// A drawing backend error 15 DrawingError(E), 16 /// A font rendering error 17 FontError(FontError), 18 } 19 20 impl<E: Error + Send + Sync> std::fmt::Display for DrawingErrorKind<E> { fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>21 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 22 match self { 23 DrawingErrorKind::DrawingError(e) => write!(fmt, "Drawing backend error: {}", e), 24 DrawingErrorKind::FontError(e) => write!(fmt, "Font loading error: {}", e), 25 } 26 } 27 } 28 29 impl<E: Error + Send + Sync> Error for DrawingErrorKind<E> {} 30 31 /// The style data for the backend drawing API 32 pub trait BackendStyle { 33 /// The underlying type represents the color for this style 34 type ColorType: Color; 35 36 /// Convert the style into the underlying color as_color(&self) -> RGBAColor37 fn as_color(&self) -> RGBAColor; 38 39 // TODO: In the future we should support stroke width, line shape, etc.... stroke_width(&self) -> u3240 fn stroke_width(&self) -> u32 { 41 1 42 } 43 } 44 45 impl<T: Color> BackendStyle for T { 46 type ColorType = T; as_color(&self) -> RGBAColor47 fn as_color(&self) -> RGBAColor { 48 self.to_rgba() 49 } 50 } 51 52 impl BackendStyle for ShapeStyle { 53 type ColorType = RGBAColor; as_color(&self) -> RGBAColor54 fn as_color(&self) -> RGBAColor { 55 self.color.clone() 56 } stroke_width(&self) -> u3257 fn stroke_width(&self) -> u32 { 58 self.stroke_width 59 } 60 } 61 62 /// The drawing backend trait, which implements the low-level drawing APIs. 63 /// This trait has a set of default implementation. And the minimal requirement of 64 /// implementing a drawing backend is implementing the `draw_pixel` function. 65 /// 66 /// If the drawing backend supports vector graphics, the other drawing APIs should be 67 /// override by the backend specific implementation. Otherwise, the default implementation 68 /// will use the pixel-based approach to draw other types of low-level shapes. 69 pub trait DrawingBackend: Sized { 70 /// The error type reported by the backend 71 type ErrorType: Error + Send + Sync; 72 73 /// Get the dimension of the drawing backend in pixels get_size(&self) -> (u32, u32)74 fn get_size(&self) -> (u32, u32); 75 76 /// Ensure the backend is ready to draw ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>77 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>; 78 79 /// Finalize the drawing step and present all the changes. 80 /// This is used as the real-time rendering support. 81 /// The backend may implement in the following way, when `ensure_prepared` is called 82 /// it checks if it needs a fresh buffer and `present` is called rendering all the 83 /// pending changes on the screen. present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>84 fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>>; 85 86 /// Draw a pixel on the drawing backend 87 /// - `point`: The backend pixel-based coordinate to draw 88 /// - `color`: The color of the pixel draw_pixel( &mut self, point: BackendCoord, color: &RGBAColor, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>89 fn draw_pixel( 90 &mut self, 91 point: BackendCoord, 92 color: &RGBAColor, 93 ) -> Result<(), DrawingErrorKind<Self::ErrorType>>; 94 95 /// Draw a line on the drawing backend 96 /// - `from`: The start point of the line 97 /// - `to`: The end point of the line 98 /// - `style`: The style of the line draw_line<S: BackendStyle>( &mut self, from: BackendCoord, to: BackendCoord, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>99 fn draw_line<S: BackendStyle>( 100 &mut self, 101 from: BackendCoord, 102 to: BackendCoord, 103 style: &S, 104 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 105 super::rasterizer::draw_line(self, from, to, style) 106 } 107 108 /// Draw a rectangle on the drawing backend 109 /// - `upper_left`: The coordinate of the upper-left corner of the rect 110 /// - `bottom_right`: The coordinate of the bottom-right corner of the rect 111 /// - `style`: The style 112 /// - `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>>113 fn draw_rect<S: BackendStyle>( 114 &mut self, 115 upper_left: BackendCoord, 116 bottom_right: BackendCoord, 117 style: &S, 118 fill: bool, 119 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 120 super::rasterizer::draw_rect(self, upper_left, bottom_right, style, fill) 121 } 122 123 /// Draw a path on the drawing backend 124 /// - `path`: The iterator of key points of the path 125 /// - `style`: The style of the path draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, path: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>126 fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( 127 &mut self, 128 path: I, 129 style: &S, 130 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 131 if style.as_color().alpha() == 0.0 { 132 return Ok(()); 133 } 134 135 if style.stroke_width() == 1 { 136 let mut begin: Option<BackendCoord> = None; 137 for end in path.into_iter() { 138 if let Some(begin) = begin { 139 let result = self.draw_line(begin, end, style); 140 if result.is_err() { 141 return result; 142 } 143 } 144 begin = Some(end); 145 } 146 } else { 147 let p: Vec<_> = path.into_iter().collect(); 148 let v = super::rasterizer::polygonize(&p[..], style.stroke_width()); 149 return self.fill_polygon(v, &style.as_color()); 150 } 151 Ok(()) 152 } 153 154 /// Draw a circle on the drawing backend 155 /// - `center`: The center coordinate of the circle 156 /// - `radius`: The radius of the circle 157 /// - `style`: The style of the shape 158 /// - `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>>159 fn draw_circle<S: BackendStyle>( 160 &mut self, 161 center: BackendCoord, 162 radius: u32, 163 style: &S, 164 fill: bool, 165 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 166 super::rasterizer::draw_circle(self, center, radius, style, fill) 167 } 168 fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( &mut self, vert: I, style: &S, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>169 fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>( 170 &mut self, 171 vert: I, 172 style: &S, 173 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 174 let vert_buf: Vec<_> = vert.into_iter().collect(); 175 176 super::rasterizer::fill_polygon(self, &vert_buf[..], style) 177 } 178 179 /// Draw a text on the drawing backend 180 /// - `text`: The text to draw 181 /// - `style`: The text style 182 /// - `pos` : The text anchor point draw_text( &mut self, text: &str, style: &TextStyle, pos: BackendCoord, ) -> Result<(), DrawingErrorKind<Self::ErrorType>>183 fn draw_text( 184 &mut self, 185 text: &str, 186 style: &TextStyle, 187 pos: BackendCoord, 188 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 189 let font = &style.font; 190 let color = &style.color; 191 if color.alpha() == 0.0 { 192 return Ok(()); 193 } 194 195 let layout = font.layout_box(text).map_err(DrawingErrorKind::FontError)?; 196 let ((min_x, min_y), (max_x, max_y)) = layout; 197 let width = (max_x - min_x) as i32; 198 let height = (max_y - min_y) as i32; 199 let dx = match style.pos.h_pos { 200 HPos::Left => 0, 201 HPos::Right => -width, 202 HPos::Center => -width / 2, 203 }; 204 let dy = match style.pos.v_pos { 205 VPos::Top => 0, 206 VPos::Center => -height / 2, 207 VPos::Bottom => -height, 208 }; 209 let trans = font.get_transform(); 210 let (w, h) = self.get_size(); 211 match font.draw(text, (0, 0), |x, y, v| { 212 let (x, y) = trans.transform(x + dx - min_x, y + dy - min_y); 213 let (x, y) = (pos.0 + x, pos.1 + y); 214 if x >= 0 && x < w as i32 && y >= 0 && y < h as i32 { 215 self.draw_pixel((x, y), &color.mix(f64::from(v))) 216 } else { 217 Ok(()) 218 } 219 }) { 220 Ok(drawing_result) => drawing_result, 221 Err(font_error) => Err(DrawingErrorKind::FontError(font_error)), 222 } 223 } 224 225 /// Estimate the size of the horizontal text if rendered on this backend. 226 /// This is important because some of the backend may not have font ability. 227 /// Thus this allows those backend reports proper value rather than ask the 228 /// font rasterizer for that. 229 /// 230 /// - `text`: The text to estimate 231 /// - `font`: The font to estimate 232 /// - *Returns* The estimated text size estimate_text_size<'a>( &self, text: &str, font: &FontDesc<'a>, ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>>233 fn estimate_text_size<'a>( 234 &self, 235 text: &str, 236 font: &FontDesc<'a>, 237 ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> { 238 let layout = font.layout_box(text).map_err(DrawingErrorKind::FontError)?; 239 Ok(( 240 ((layout.1).0 - (layout.0).0) as u32, 241 ((layout.1).1 - (layout.0).1) as u32, 242 )) 243 } 244 245 /// Blit a bitmap on to the backend. 246 /// 247 /// - `text`: pos the left upper conner of the bitmap to blit 248 /// - `src`: The source of the image 249 /// 250 /// TODO: The default implementation of bitmap blitting assumes that the bitmap is RGB, but 251 /// this may not be the case. But for bitmap backend it's actually ok if we use the bitmap 252 /// element that matches the pixel format, but we need to fix this. blit_bitmap<'a>( &mut self, pos: BackendCoord, (iw, ih): (u32, u32), src: &'a [u8], ) -> Result<(), DrawingErrorKind<Self::ErrorType>>253 fn blit_bitmap<'a>( 254 &mut self, 255 pos: BackendCoord, 256 (iw, ih): (u32, u32), 257 src: &'a [u8], 258 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> { 259 let (w, h) = self.get_size(); 260 261 for dx in 0..iw { 262 if pos.0 + dx as i32 >= w as i32 { 263 break; 264 } 265 for dy in 0..ih { 266 if pos.1 + dy as i32 >= h as i32 { 267 break; 268 } 269 // FIXME: This assume we have RGB image buffer 270 let r = src[(dx + dy * w) as usize * 3]; 271 let g = src[(dx + dy * w) as usize * 3 + 1]; 272 let b = src[(dx + dy * w) as usize * 3 + 2]; 273 let color = crate::style::RGBColor(r, g, b); 274 let result = 275 self.draw_pixel((pos.0 + dx as i32, pos.1 + dy as i32), &color.to_rgba()); 276 if result.is_err() { 277 return result; 278 } 279 } 280 } 281 282 Ok(()) 283 } 284 } 285