From c7382a8b2c146609c94e0bc9732925b36be8ebeb Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 13 May 2019 18:40:25 -0700 Subject: [PATCH] Add affine transform support to the canvas implementation --- canvas/src/lib.rs | 36 ++++++++++++++++++++++++++++++++---- geometry/src/outline.rs | 8 ++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 63d68222..300e6049 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -94,10 +94,12 @@ impl CanvasRenderingContext2D { pub fn fill_text(&mut self, string: &str, position: Point2DF32) { // TODO(pcwalton): Report errors. let paint_id = self.scene.push_paint(&self.current_state.fill_paint); + let transform = Transform2DF32::from_translation(position).post_mul(&self.current_state + .transform); drop(self.scene.push_text(string, &TextStyle { size: self.current_state.font_size }, &self.current_state.font_collection, - &Transform2DF32::from_translation(position), + &transform, TextRenderMode::Fill, HintingOptions::None, paint_id)); @@ -106,10 +108,12 @@ impl CanvasRenderingContext2D { pub fn stroke_text(&mut self, string: &str, position: Point2DF32) { // TODO(pcwalton): Report errors. let paint_id = self.scene.push_paint(&self.current_state.stroke_paint); + let transform = Transform2DF32::from_translation(position).post_mul(&self.current_state + .transform); drop(self.scene.push_text(string, &TextStyle { size: self.current_state.font_size }, &self.current_state.font_collection, - &Transform2DF32::from_translation(position), + &transform, TextRenderMode::Stroke(self.current_state.line_width), HintingOptions::None, paint_id)); @@ -139,12 +143,14 @@ impl CanvasRenderingContext2D { self.current_state.font_size = new_font_size; } - // Paths + // Drawing paths #[inline] pub fn fill_path(&mut self, path: Path2D) { + let mut outline = path.into_outline(); + outline.transform(&self.current_state.transform); let paint_id = self.scene.push_paint(&self.current_state.fill_paint); - self.scene.push_path(PathObject::new(path.into_outline(), paint_id, String::new())) + self.scene.push_path(PathObject::new(outline, paint_id, String::new())) } #[inline] @@ -153,9 +159,29 @@ impl CanvasRenderingContext2D { let stroke_width = f32::max(self.current_state.line_width, HAIRLINE_STROKE_WIDTH); let mut stroke_to_fill = OutlineStrokeToFill::new(path.into_outline(), stroke_width); stroke_to_fill.offset(); + stroke_to_fill.outline.transform(&self.current_state.transform); self.scene.push_path(PathObject::new(stroke_to_fill.outline, paint_id, String::new())) } + // Transformations + + #[inline] + pub fn current_transform(&self) -> Transform2DF32 { + self.current_state.transform + } + + #[inline] + pub fn set_current_transform(&mut self, new_transform: &Transform2DF32) { + self.current_state.transform = *new_transform; + } + + #[inline] + pub fn reset_transform(&mut self) { + self.current_state.transform = Transform2DF32::default(); + } + + // The canvas state + #[inline] pub fn save(&mut self) { self.saved_states.push(self.current_state.clone()); @@ -171,6 +197,7 @@ impl CanvasRenderingContext2D { #[derive(Clone)] pub struct State { + transform: Transform2DF32, font_collection: Arc, font_size: f32, fill_paint: Paint, @@ -181,6 +208,7 @@ pub struct State { impl State { fn default(default_font_collection: Arc) -> State { State { + transform: Transform2DF32::default(), font_collection: default_font_collection, font_size: DEFAULT_FONT_SIZE, fill_paint: Paint { color: ColorU::black() }, diff --git a/geometry/src/outline.rs b/geometry/src/outline.rs index 71fa6856..241fd3d5 100644 --- a/geometry/src/outline.rs +++ b/geometry/src/outline.rs @@ -127,6 +127,10 @@ impl Outline { } pub fn transform(&mut self, transform: &Transform2DF32) { + if transform.is_identity() { + return; + } + let mut new_bounds = None; for contour in &mut self.contours { contour.transform(transform); @@ -487,6 +491,10 @@ impl Contour { } pub fn transform(&mut self, transform: &Transform2DF32) { + if transform.is_identity() { + return; + } + for (point_index, point) in self.points.iter_mut().enumerate() { *point = transform.transform_point(*point); union_rect(&mut self.bounds, *point, point_index == 0);