use std::path::Path; use crate::{ core_shapes::*, styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}, text::{DefinedFontDescription, DefinedText, FontDescription}, types::DefinedCoreShapeContext, }; pub trait Renderer { // Must be called once before any drawing happens fn set_size(&mut self, width: f64, height: f64); fn move_to(&mut self, x: f64, y: f64); fn stroke(&mut self); fn fill(&mut self); fn fill_preserve(&mut self); fn set_pattern(&mut self, pattern: &Pattern); fn set_dash(&mut self, dash: &DefinedDashStyle); fn clear_dash(&mut self); fn set_line_width(&mut self, width: f64); fn line_to(&mut self, x: f64, y: f64); fn rectangle(&mut self, x: f64, y: f64, width: f64, height: f64); // For a font of size 1. fn text_extents(&self, text: &str, font: &FontDescription) -> (f64, f64); fn show_text(&mut self, text: &str, font: &DefinedFontDescription); fn show_image(&mut self, path: &Path, x: f64, y: f64, width: f64, height: f64); fn geometry_for_image(&mut self, path: &Path) -> (f64, f64); } pub trait Render { fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer); } impl Render for DefinedCoreShape { fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) { match self { Self::Rectangle(r) => r.render(context, renderer), Self::Text(t) => t.render(context, renderer), Self::StraightPath(p) => p.render(context, renderer), Self::Image(i) => i.render(context, renderer), } } } fn draw(fill: &FillStyle, stroke: &DefinedStrokeStyle, renderer: &mut dyn Renderer) { let stroking = !stroke.pattern.is_none(); if !fill.pattern.is_none() { renderer.set_pattern(&fill.pattern); if stroking { renderer.fill_preserve(); } else { renderer.fill(); } } if !stroke.pattern.is_none() { renderer.set_pattern(&stroke.pattern); renderer.set_line_width(stroke.line_width); if let Some(dash) = &stroke.dash { renderer.set_dash(dash); } renderer.stroke(); renderer.clear_dash(); } } impl Render for Rectangle { fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) { let bounds = &context.bounds; renderer.rectangle(bounds.left, bounds.top, bounds.width, bounds.height); draw(&context.fill, &context.stroke, renderer); } } impl Render for DefinedText { fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) { // TODO: select font, style, text shaping (renderer specific), etc. let bounds = &context.bounds; //renderer.move_to(bounds.left, bounds.top + self.font.size); renderer.move_to(bounds.left, bounds.top); // TODO: ??? //draw(&context.fill, &context.stroke, renderer); renderer.show_text(&self.content, &self.font); } } impl Render for DefinedStraightPath { fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) { let mut iter = self.points.iter(); let first_point = match iter.next() { Some(point) => point, None => return, }; renderer.move_to(first_point.x, first_point.y); for point in iter { renderer.line_to(point.x, point.y); } draw(&context.fill, &context.stroke, renderer); } } impl Render for Image { fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) { // TODO: what about pattern, and fill color? Do we do something with that? renderer.show_image( &self.path, context.bounds.left, context.bounds.top, context.bounds.width, context.bounds.height, ); } }