[librsvg/wip/euclid: 15/15] transform: add a Tranform type



commit 9435ce9577893640173e15c1d38724fe38c9c19f
Author: Paolo Borelli <pborelli gnome org>
Date:   Sun Nov 17 11:33:58 2019 +0100

    transform: add a Tranform type
    
    Use an alias for cairo::Matrix and add a trait to ease transition
    to the euclid crate.

 rsvg_internals/src/aspect_ratio.rs |   7 +-
 rsvg_internals/src/bbox.rs         |  19 +++--
 rsvg_internals/src/clip_path.rs    |   1 -
 rsvg_internals/src/drawing_ctx.rs  |  31 ++++----
 rsvg_internals/src/node.rs         |  14 ++--
 rsvg_internals/src/text.rs         |   3 +-
 rsvg_internals/src/transform.rs    | 141 ++++++++++++++++++++++---------------
 7 files changed, 122 insertions(+), 94 deletions(-)
---
diff --git a/rsvg_internals/src/aspect_ratio.rs b/rsvg_internals/src/aspect_ratio.rs
index d4475aef..37c5484c 100644
--- a/rsvg_internals/src/aspect_ratio.rs
+++ b/rsvg_internals/src/aspect_ratio.rs
@@ -28,6 +28,7 @@ use crate::error::ValueErrorKind;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::parsers::Parse;
 use crate::parsers::ParseError;
+use crate::transform::Transform;
 use crate::viewbox::ViewBox;
 use cssparser::{CowRcStr, Parser};
 
@@ -154,7 +155,7 @@ impl AspectRatio {
         &self,
         vbox: Option<ViewBox>,
         viewport: &cairo::Rectangle,
-    ) -> Option<cairo::Matrix> {
+    ) -> Option<Transform> {
         // width or height set to 0 disables rendering of the element
         // https://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
         // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
@@ -174,14 +175,14 @@ impl AspectRatio {
                 None
             } else {
                 let (x, y, w, h) = self.compute(&vbox, viewport);
-                let mut matrix = cairo::Matrix::identity();
+                let mut matrix = Transform::identity();
                 matrix.translate(x, y);
                 matrix.scale(w / vbox.width, h / vbox.height);
                 matrix.translate(-vbox.x, -vbox.y);
                 Some(matrix)
             }
         } else {
-            let mut matrix = cairo::Matrix::identity();
+            let mut matrix = Transform::identity();
             matrix.translate(viewport.x, viewport.y);
             Some(matrix)
         }
diff --git a/rsvg_internals/src/bbox.rs b/rsvg_internals/src/bbox.rs
index d8bde22a..f82802a7 100644
--- a/rsvg_internals/src/bbox.rs
+++ b/rsvg_internals/src/bbox.rs
@@ -1,16 +1,17 @@
 use cairo;
 
 use crate::rect::RectangleExt;
+use crate::transform::{Transform, TransformExt};
 
 #[derive(Debug, Copy, Clone)]
 pub struct BoundingBox {
-    pub affine: cairo::Matrix,
+    pub affine: Transform,
     pub rect: Option<cairo::Rectangle>,     // without stroke
     pub ink_rect: Option<cairo::Rectangle>, // with stroke
 }
 
 impl BoundingBox {
-    pub fn new(affine: &cairo::Matrix) -> BoundingBox {
+    pub fn new(affine: &Transform) -> BoundingBox {
         BoundingBox {
             affine: *affine,
             rect: None,
@@ -39,14 +40,12 @@ impl BoundingBox {
             return;
         }
 
-        let mut affine = self.affine;
+        if let Some(inverse) = self.affine.inverse() {
+            let affine = inverse.pre_transform(&src.affine);
 
-        // this will panic!() if it's not invertible... should we check on our own?
-        affine.invert();
-        affine = cairo::Matrix::multiply(&src.affine, &affine);
-
-        self.rect = combine_rects(self.rect, src.rect, &affine, clip);
-        self.ink_rect = combine_rects(self.ink_rect, src.ink_rect, &affine, clip);
+            self.rect = combine_rects(self.rect, src.rect, &affine, clip);
+            self.ink_rect = combine_rects(self.ink_rect, src.ink_rect, &affine, clip);
+        }
     }
 
     pub fn insert(&mut self, src: &BoundingBox) {
@@ -70,7 +69,7 @@ fn rect_from_extents((x1, y1, x2, y2): (f64, f64, f64, f64)) -> Option<cairo::Re
 fn combine_rects(
     r1: Option<cairo::Rectangle>,
     r2: Option<cairo::Rectangle>,
-    affine: &cairo::Matrix,
+    affine: &Transform,
     clip: bool,
 ) -> Option<cairo::Rectangle> {
     match (r1, r2, clip) {
diff --git a/rsvg_internals/src/clip_path.rs b/rsvg_internals/src/clip_path.rs
index 5dfd21a2..64e5681b 100644
--- a/rsvg_internals/src/clip_path.rs
+++ b/rsvg_internals/src/clip_path.rs
@@ -1,4 +1,3 @@
-use cairo;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::bbox::BoundingBox;
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 29e805f1..94f41b72 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -25,6 +25,7 @@ use crate::property_defs::{
 };
 use crate::rect::RectangleExt;
 use crate::surface_utils::shared_surface::SharedImageSurface;
+use crate::transform::{Transform, TransformExt};
 use crate::unit_interval::UnitInterval;
 use crate::viewbox::ViewBox;
 
@@ -77,7 +78,7 @@ pub enum ClipMode {
 pub struct DrawingCtx {
     document: Rc<Document>,
 
-    initial_affine: cairo::Matrix,
+    initial_affine: Transform,
 
     rect: cairo::Rectangle,
     dpi: Dpi,
@@ -533,7 +534,7 @@ impl DrawingCtx {
         }
     }
 
-    fn initial_affine_with_offset(&self) -> cairo::Matrix {
+    fn initial_affine_with_offset(&self) -> Transform {
         let mut initial_with_offset = self.initial_affine;
         initial_with_offset.translate(self.rect.x, self.rect.y);
         initial_with_offset
@@ -831,7 +832,7 @@ impl DrawingCtx {
         node: &RsvgNode,
         cascaded: &CascadedValues<'_>,
         surface: &cairo::ImageSurface,
-        affine: cairo::Matrix,
+        affine: Transform,
         width: f64,
         height: f64,
     ) -> Result<BoundingBox, RenderingError> {
@@ -893,17 +894,17 @@ impl DrawingCtx {
 
 #[derive(Debug)]
 pub struct CompositingAffines {
-    pub outside_temporary_surface: cairo::Matrix,
-    pub initial: cairo::Matrix,
-    pub for_temporary_surface: cairo::Matrix,
-    pub compositing: cairo::Matrix,
-    pub for_snapshot: cairo::Matrix,
+    pub outside_temporary_surface: Transform,
+    pub initial: Transform,
+    pub for_temporary_surface: Transform,
+    pub compositing: Transform,
+    pub for_snapshot: Transform,
 }
 
 impl CompositingAffines {
     fn new(
-        current: cairo::Matrix,
-        initial: cairo::Matrix,
+        current: Transform,
+        initial: Transform,
         cr_stack_depth: usize,
     ) -> CompositingAffines {
         let is_topmost_temporary_surface = cr_stack_depth == 0;
@@ -913,15 +914,15 @@ impl CompositingAffines {
         let outside_temporary_surface = if is_topmost_temporary_surface {
             current
         } else {
-            cairo::Matrix::multiply(&current, &initial_inverse)
+            initial_inverse.pre_transform(&current)
         };
 
         let (scale_x, scale_y) = initial.transform_distance(1.0, 1.0);
 
         let for_temporary_surface = if is_topmost_temporary_surface {
-            let untransformed = cairo::Matrix::multiply(&current, &initial_inverse);
-            let scale = cairo::Matrix::new(scale_x, 0.0, 0.0, scale_y, 0.0, 0.0);
-            cairo::Matrix::multiply(&untransformed, &scale)
+            let untransformed = initial_inverse.pre_transform(&current);
+            let scale = Transform::new(scale_x, 0.0, 0.0, scale_y, 0.0, 0.0);
+            scale.pre_transform(&untransformed)
         } else {
             current
         };
@@ -931,7 +932,7 @@ impl CompositingAffines {
             scaled.scale(1.0 / scale_x, 1.0 / scale_y);
             scaled
         } else {
-            cairo::Matrix::identity()
+            Transform::identity()
         };
 
         let for_snapshot = compositing.try_invert().unwrap();
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index fd94dd59..ea8aa8e1 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -1,6 +1,7 @@
-use cairo::Matrix;
 use downcast_rs::*;
+use locale_config::Locale;
 use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
+use rctree;
 use std::cell::Ref;
 use std::collections::HashSet;
 use std::fmt;
@@ -15,8 +16,7 @@ use crate::parsers::Parse;
 use crate::properties::{ComputedValues, SpecifiedValue, SpecifiedValues};
 use crate::property_bag::PropertyBag;
 use crate::property_defs::Overflow;
-use locale_config::Locale;
-use rctree;
+use crate::transform::Transform;
 
 /// Tree node with specific data
 pub type RsvgNode = rctree::Node<NodeData>;
@@ -31,7 +31,7 @@ pub struct NodeData {
     specified_values: SpecifiedValues,
     important_styles: HashSet<QualName>,
     result: NodeResult,
-    transform: Matrix,
+    transform: Transform,
     values: ComputedValues,
     cond: bool,
     style_attr: String,
@@ -53,7 +53,7 @@ impl NodeData {
             class: class.map(str::to_string),
             specified_values: Default::default(),
             important_styles: Default::default(),
-            transform: Matrix::identity(),
+            transform: Transform::identity(),
             result: Ok(()),
             values: ComputedValues::default(),
             cond: true,
@@ -94,7 +94,7 @@ impl NodeData {
         self.cond
     }
 
-    pub fn get_transform(&self) -> Matrix {
+    pub fn get_transform(&self) -> Transform {
         self.transform
     }
 
@@ -135,7 +135,7 @@ impl NodeData {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!(svg "transform") => {
-                    return Matrix::parse_str(value).attribute(attr).and_then(|affine| {
+                    return Transform::parse_str(value).attribute(attr).and_then(|affine| {
                         self.transform = affine;
                         Ok(())
                     });
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 29f13c84..d10bde41 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -29,6 +29,7 @@ use crate::property_defs::{
     XmlSpace,
 };
 use crate::space::{xml_space_normalize, NormalizeDefault, XmlSpaceNormalize};
+use crate::transform::Transform;
 
 /// An absolutely-positioned array of `Span`s
 ///
@@ -363,7 +364,7 @@ impl PositionedSpan {
 
     fn compute_text_bbox(
         &self,
-        affine: &cairo::Matrix,
+        affine: &Transform,
         gravity: pango::Gravity,
     ) -> Option<BoundingBox> {
         let (ink, _) = self.layout.get_extents();
diff --git a/rsvg_internals/src/transform.rs b/rsvg_internals/src/transform.rs
index 89298307..e830a6b3 100644
--- a/rsvg_internals/src/transform.rs
+++ b/rsvg_internals/src/transform.rs
@@ -7,16 +7,43 @@ use cssparser::{ParseError as CssParseError, Parser, Token};
 use crate::error::*;
 use crate::parsers::{finite_f32, CssParserExt, Parse, ParseError};
 
-impl Parse for cairo::Matrix {
+pub type Transform = cairo::Matrix;
+
+// Extension trait to prepare the switch from cairo::Matrix to euclid
+pub trait TransformExt
+where Self: std::marker::Sized
+{
+    fn row_major(m11: f64, m12: f64, m21: f64, m22: f64, m31: f64, m32: f64) -> Self;
+
+    fn inverse(&self) -> Option<Self>;
+
+    fn pre_transform(&self, mat: &Self) -> Self;
+}
+
+impl TransformExt for Transform {
+    fn row_major(m11: f64, m12: f64, m21: f64, m22: f64, m31: f64, m32: f64) -> Self {
+        cairo::Matrix::new(m11, m12, m21, m22, m31, m32)
+    }
+
+    fn inverse(&self) -> Option<Self> {
+        self.try_invert().ok()
+    }
+
+    fn pre_transform(&self, mat: &Self) -> Self {
+        cairo::Matrix::multiply(mat, self)
+    }
+}
+
+impl Parse for Transform {
     type Err = ValueErrorKind;
 
-    fn parse(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+    fn parse(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
         let matrix = parse_transform_list(parser)?;
 
-        matrix
-            .try_invert()
-            .map(|_| matrix)
-            .map_err(|_| ValueErrorKind::Value("invalid transformation matrix".to_string()))
+        match matrix.inverse() {
+            Some(_) => Ok(matrix),
+            _ => Err(ValueErrorKind::Value("invalid transformation matrix".to_string())),
+        }
     }
 }
 
@@ -24,8 +51,8 @@ impl Parse for cairo::Matrix {
 // Its operataion and grammar are described here:
 // https://www.w3.org/TR/SVG/coords.html#TransformAttribute
 
-fn parse_transform_list(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
-    let mut matrix = cairo::Matrix::identity();
+fn parse_transform_list(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
+    let mut matrix = Transform::identity();
 
     loop {
         if parser.is_exhausted() {
@@ -33,7 +60,7 @@ fn parse_transform_list(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, Va
         }
 
         let m = parse_transform_command(parser)?;
-        matrix = cairo::Matrix::multiply(&m, &matrix);
+        matrix = matrix.pre_transform(&m);
 
         parser.optional_comma();
     }
@@ -47,7 +74,7 @@ fn make_expected_function_error() -> ValueErrorKind {
     ))
 }
 
-fn parse_transform_command(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_transform_command(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     match parser.next()?.clone() {
         Token::Function(ref name) => parse_transform_function(name, parser),
 
@@ -63,7 +90,7 @@ fn parse_transform_command(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix,
 fn parse_transform_function(
     name: &str,
     parser: &mut Parser<'_, '_>,
-) -> Result<cairo::Matrix, ValueErrorKind> {
+) -> Result<Transform, ValueErrorKind> {
     match name {
         "matrix" => parse_matrix_args(parser),
         "translate" => parse_translate_args(parser),
@@ -75,7 +102,7 @@ fn parse_transform_function(
     }
 }
 
-fn parse_matrix_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_matrix_args(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     parser
         .parse_nested_block(|p| {
             let xx = p.expect_number()?;
@@ -107,11 +134,11 @@ fn parse_matrix_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, Value
             let x0 = f64::from(finite_f32(x0)?);
             let y0 = f64::from(finite_f32(y0)?);
 
-            Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
+            Ok(Transform::row_major(xx, yx, xy, yy, x0, y0))
         })
 }
 
-fn parse_translate_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_translate_args(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     parser
         .parse_nested_block(|p| {
             let tx = p.expect_number()?;
@@ -131,11 +158,11 @@ fn parse_translate_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, Va
             let tx = f64::from(finite_f32(tx)?);
             let ty = f64::from(finite_f32(ty)?);
 
-            Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty))
+            Ok(Transform::row_major(1.0, 0.0, 0.0, 1.0, tx, ty))
         })
 }
 
-fn parse_scale_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_scale_args(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     parser
         .parse_nested_block(|p| {
             let x = p.expect_number()?;
@@ -155,11 +182,11 @@ fn parse_scale_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueE
             let x = f64::from(finite_f32(x)?);
             let y = f64::from(finite_f32(y)?);
 
-            Ok(cairo::Matrix::new(x, 0.0, 0.0, y, 0.0, 0.0))
+            Ok(Transform::row_major(x, 0.0, 0.0, y, 0.0, 0.0))
         })
 }
 
-fn parse_rotate_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_rotate_args(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     parser
         .parse_nested_block(|p| {
             let angle = p.expect_number()?;
@@ -188,15 +215,16 @@ fn parse_rotate_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, Value
             let angle = angle * PI / 180.0;
             let (s, c) = angle.sin_cos();
 
-            let mut m = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty);
+            let mut m = Transform::row_major(1.0, 0.0, 0.0, 1.0, tx, ty);
 
-            m = cairo::Matrix::multiply(&cairo::Matrix::new(c, s, -s, c, 0.0, 0.0), &m);
-            m = cairo::Matrix::multiply(&cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
+            // FIXME: use euclid pre_rotate / pre_translate?
+            m = m.pre_transform(&Transform::row_major(c, s, -s, c, 0.0, 0.0));
+            m = m.pre_transform(&Transform::row_major(1.0, 0.0, 0.0, 1.0, -tx, -ty));
             Ok(m)
         })
 }
 
-fn parse_skewx_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_skewx_args(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     parser
         .parse_nested_block(|p| {
             let a = p.expect_number()?;
@@ -208,11 +236,11 @@ fn parse_skewx_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueE
             let a = f64::from(finite_f32(a)?);
 
             let a = a * PI / 180.0;
-            Ok(cairo::Matrix::new(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0))
+            Ok(Transform::row_major(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0))
         })
 }
 
-fn parse_skewy_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueErrorKind> {
+fn parse_skewy_args(parser: &mut Parser<'_, '_>) -> Result<Transform, ValueErrorKind> {
     parser
         .parse_nested_block(|p| {
             let a = p.expect_number()?;
@@ -224,22 +252,21 @@ fn parse_skewy_args(parser: &mut Parser<'_, '_>) -> Result<cairo::Matrix, ValueE
             let a = f64::from(finite_f32(a)?);
 
             let a = a * PI / 180.0;
-            Ok(cairo::Matrix::new(1.0, a.tan(), 0.0, 1.0, 0.0, 0.0))
+            Ok(Transform::row_major(1.0, a.tan(), 0.0, 1.0, 0.0, 0.0))
         })
 }
 
 #[cfg(test)]
-fn make_rotation_matrix(angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
+fn make_rotation_matrix(angle_degrees: f64, tx: f64, ty: f64) -> Transform {
     let angle = angle_degrees * PI / 180.0;
 
-    let mut m = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty);
+    let mut m = Transform::row_major(1.0, 0.0, 0.0, 1.0, tx, ty);
 
-    let mut r = cairo::Matrix::identity();
+    let mut r = Transform::identity();
     r.rotate(angle);
-    m = cairo::Matrix::multiply(&r, &m);
 
-    m = cairo::Matrix::multiply(&cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
-    m
+    m = m.pre_transform(&r);
+    m.pre_transform(&Transform::row_major(1.0, 0.0, 0.0, 1.0, -tx, -ty))
 }
 
 #[cfg(test)]
@@ -248,11 +275,11 @@ mod tests {
     use float_cmp::ApproxEq;
     use std::f64;
 
-    fn parse_transform(s: &str) -> Result<cairo::Matrix, ValueErrorKind> {
-        cairo::Matrix::parse_str(s)
+    fn parse_transform(s: &str) -> Result<Transform, ValueErrorKind> {
+        Transform::parse_str(s)
     }
 
-    fn assert_matrix_eq(a: &cairo::Matrix, b: &cairo::Matrix) {
+    fn assert_matrix_eq(a: &Transform, b: &Transform) {
         let epsilon = 8.0 * f64::EPSILON; // kind of arbitrary, but allow for some sloppiness
 
         assert!(a.xx.approx_eq(b.xx, (epsilon, 1)));
@@ -265,14 +292,14 @@ mod tests {
 
     #[test]
     fn parses_valid_transform() {
-        let t = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
-        let s = cairo::Matrix::new(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
+        let t = Transform::row_major(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
+        let s = Transform::row_major(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
         let r = make_rotation_matrix(30.0, 10.0, 10.0);
 
-        let a = cairo::Matrix::multiply(&s, &t);
+        let a = t.pre_transform(&s);
         assert_matrix_eq(
             &parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
-            &cairo::Matrix::multiply(&r, &a),
+            &a.pre_transform(&r),
         );
     }
 
@@ -325,17 +352,17 @@ mod tests {
     fn parses_matrix() {
         assert_matrix_eq(
             &parse_transform("matrix (1 2 3 4 5 6)").unwrap(),
-            &cairo::Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
+            &Transform::row_major(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
         );
 
         assert_matrix_eq(
             &parse_transform("matrix(1,2,3,4 5 6)").unwrap(),
-            &cairo::Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
+            &Transform::row_major(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
         );
 
         assert_matrix_eq(
             &parse_transform("matrix (1,2.25,-3.25e2,4 5 6)").unwrap(),
-            &cairo::Matrix::new(1.0, 2.25, -325.0, 4.0, 5.0, 6.0),
+            &Transform::row_major(1.0, 2.25, -325.0, 4.0, 5.0, 6.0),
         );
     }
 
@@ -343,17 +370,17 @@ mod tests {
     fn parses_translate() {
         assert_matrix_eq(
             &parse_transform("translate(-1 -2)").unwrap(),
-            &cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
+            &Transform::row_major(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
         );
 
         assert_matrix_eq(
             &parse_transform("translate(-1, -2)").unwrap(),
-            &cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
+            &Transform::row_major(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
         );
 
         assert_matrix_eq(
             &parse_transform("translate(-1)").unwrap(),
-            &cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, -1.0, 0.0),
+            &Transform::row_major(1.0, 0.0, 0.0, 1.0, -1.0, 0.0),
         );
     }
 
@@ -361,17 +388,17 @@ mod tests {
     fn parses_scale() {
         assert_matrix_eq(
             &parse_transform("scale (-1)").unwrap(),
-            &cairo::Matrix::new(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0),
+            &Transform::row_major(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0),
         );
 
         assert_matrix_eq(
             &parse_transform("scale(-1 -2)").unwrap(),
-            &cairo::Matrix::new(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
+            &Transform::row_major(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
         );
 
         assert_matrix_eq(
             &parse_transform("scale(-1, -2)").unwrap(),
-            &cairo::Matrix::new(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
+            &Transform::row_major(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
         );
     }
 
@@ -391,12 +418,12 @@ mod tests {
         );
     }
 
-    fn make_skew_x_matrix(angle_degrees: f64) -> cairo::Matrix {
+    fn make_skew_x_matrix(angle_degrees: f64) -> Transform {
         let a = angle_degrees * PI / 180.0;
-        cairo::Matrix::new(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0)
+        Transform::row_major(1.0, 0.0, a.tan(), 1.0, 0.0, 0.0)
     }
 
-    fn make_skew_y_matrix(angle_degrees: f64) -> cairo::Matrix {
+    fn make_skew_y_matrix(angle_degrees: f64) -> Transform {
         let mut m = make_skew_x_matrix(angle_degrees);
         m.yx = m.xy;
         m.xy = 0.0;
@@ -421,29 +448,29 @@ mod tests {
 
     #[test]
     fn parses_transform_list() {
-        let t = cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
-        let s = cairo::Matrix::new(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
+        let t = Transform::row_major(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
+        let s = Transform::row_major(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
         let r = make_rotation_matrix(30.0, 10.0, 10.0);
 
         assert_matrix_eq(
             &parse_transform("scale(10)rotate(30, 10, 10)").unwrap(),
-            &cairo::Matrix::multiply(&r, &s),
+            &s.pre_transform(&r),
         );
 
         assert_matrix_eq(
             &parse_transform("translate(20, 30), scale (10)").unwrap(),
-            &cairo::Matrix::multiply(&s, &t),
+            &t.pre_transform(&s),
         );
 
-        let a = cairo::Matrix::multiply(&s, &t);
+        let a = t.pre_transform(&s);
         assert_matrix_eq(
             &parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
-            &cairo::Matrix::multiply(&r, &a),
+            &a.pre_transform(&r),
         );
     }
 
     #[test]
     fn parses_empty() {
-        assert_matrix_eq(&parse_transform("").unwrap(), &cairo::Matrix::identity());
+        assert_matrix_eq(&parse_transform("").unwrap(), &Transform::identity());
     }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]