[gtk/transform-work: 4/4] Add gsk_transform_to_2d_components




commit 41b810da7f36366ca7bbda7630453faed54fa016
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Sep 18 02:06:00 2021 -0400

    Add gsk_transform_to_2d_components
    
    This function decomposes a general 2D transform
    into skew, scale, rotation and translation.
    
    Tests included.

 gsk/gsktransform.c        | 118 +++++++++++++++++++++++++++++++++++++++++-----
 gsk/gsktransform.h        |   9 ++++
 testsuite/gsk/transform.c |  80 +++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 13 deletions(-)
---
diff --git a/gsk/gsktransform.c b/gsk/gsktransform.c
index cc30f567d9..9eeab2d66a 100644
--- a/gsk/gsktransform.c
+++ b/gsk/gsktransform.c
@@ -121,6 +121,14 @@ gsk_transform_alloc (const GskTransformClass *transform_class,
   return self;
 }
 
+static void
+gsk_transform_finalize (GskTransform *self)
+{
+  self->transform_class->finalize (self);
+
+  gsk_transform_unref (self->next);
+}
+
 /* }}} */
 /* {{{ IDENTITY */
 
@@ -1022,6 +1030,9 @@ gsk_skew_transform_finalize (GskTransform *self)
 {
 }
 
+#define DEG_TO_RAD(x) ((x) / 180.f * G_PI)
+#define RAD_TO_DEG(x) ((x) * 180.f / G_PI)
+
 static void
 gsk_skew_transform_to_matrix (GskTransform      *transform,
                               graphene_matrix_t *out_matrix)
@@ -1029,8 +1040,8 @@ gsk_skew_transform_to_matrix (GskTransform      *transform,
   GskSkewTransform *self = (GskSkewTransform *) transform;
 
   graphene_matrix_init_skew (out_matrix,
-                             self->skew_x / 180.0 * G_PI,
-                             self->skew_y / 180.0 * G_PI);
+                             DEG_TO_RAD (self->skew_x),
+                             DEG_TO_RAD (self->skew_y));
 }
 
 static void
@@ -1104,8 +1115,8 @@ gsk_skew_transform_invert (GskTransform *transform,
   float tx, ty;
   graphene_matrix_t matrix;
 
-  tx = tanf (self->skew_x / 180.0 * G_PI);
-  ty = tanf (self->skew_y / 180.0 * G_PI);
+  tx = tanf (DEG_TO_RAD (self->skew_x));
+  ty = tanf (DEG_TO_RAD (self->skew_y));
 
   graphene_matrix_init_from_2d (&matrix,
                                 1 / (1 - tx * ty),
@@ -1506,14 +1517,6 @@ gsk_transform_perspective (GskTransform *next,
 /* }}} */
 /* {{{ PUBLIC API */
 
-static void
-gsk_transform_finalize (GskTransform *self)
-{
-  self->transform_class->finalize (self);
-
-  gsk_transform_unref (self->next);
-}
-
 /**
  * gsk_transform_ref:
  * @self: (nullable): a `GskTransform`
@@ -1697,6 +1700,95 @@ gsk_transform_to_2d (GskTransform *self,
                                    out_dx, out_dy);
 }
 
+/**
+ * gsk_transform_to_2d_components:
+ * @self: a `GskTransform`
+ * @out_skew_x: (out): return location for the skew factor
+ *   in the  x direction
+ * @out_skew_y: (out): return location for the skew factor
+ *   in the  y direction
+ * @out_scale_x: (out): return location for the scale
+ *   factor in the x direction
+ * @out_scale_y: (out): return location for the scale
+ *   factor in the y direction
+ * @out_angle: (out): return location for the rotation angle
+ * @out_dx: (out): return location for the translation
+ *   in the x direction
+ * @out_dy: (out): return location for the translation
+ *   in the y direction
+ *
+ * Converts a `GskTransform` to 2D transformation factors.
+ *
+ * To recreate an equivalent transform from the factors returned
+ * by this function, use
+ *
+ *     gsk_transform_skew (
+ *         gsk_transform_scale (
+ *             gsk_transform_rotate (
+ *                 gsk_transform_translate (NULL, &GRAPHENE_POINT_T (dx, dy)),
+ *                 angle),
+ *             scale_x, scale_y),
+ *         skew_x, skew_y)
+ *
+ * @self must be a 2D transformation. If you are not sure, use
+ *
+ *     gsk_transform_get_category() >= %GSK_TRANSFORM_CATEGORY_2D
+ *
+ * to check.
+ *
+ * Since: 4.6
+ */
+void
+gsk_transform_to_2d_components (GskTransform *self,
+                                float        *out_skew_x,
+                                float        *out_skew_y,
+                                float        *out_scale_x,
+                                float        *out_scale_y,
+                                float        *out_angle,
+                                float        *out_dx,
+                                float        *out_dy)
+{
+  float a, b, c, d, e, f;
+
+  gsk_transform_to_2d (self, &a, &b, &c, &d, &e, &f);
+
+  *out_dx = e;
+  *out_dy = f;
+
+#define sign(f) ((f) < 0 ? -1 : 1)
+
+  if (a != 0 || b != 0)
+    {
+      float det = a * d - b * c;
+      float r = sqrtf (a*a + b*b);
+
+      *out_angle = RAD_TO_DEG (sign (b) * acosf (a / r));
+      *out_scale_x = r;
+      *out_scale_y = det / r;
+      *out_skew_x = RAD_TO_DEG (atanf ((a*c + b*d) / (r*r)));
+      *out_skew_y = 0;
+    }
+  else if (c != 0 || d != 0)
+    {
+      float det = a * d - b * c;
+      float s = sqrtf (c*c + d*d);
+
+      *out_angle = RAD_TO_DEG (G_PI/2 - sign (d) * acosf (-c / s));
+      *out_scale_x = det / s;
+      *out_scale_y = s;
+      *out_skew_x = 0;
+      *out_skew_y = RAD_TO_DEG (atanf ((a*c + b*d) / (s*s)));
+    }
+  else
+    {
+      *out_angle = 0;
+      *out_scale_x = 0;
+      *out_scale_y = 0;
+      *out_skew_x = 0;
+      *out_skew_y = 0;
+    }
+}
+
 /**
  * gsk_transform_to_affine:
  * @self: a `GskTransform`
@@ -1718,7 +1810,7 @@ gsk_transform_to_2d (GskTransform *self,
  *                                                   &GRAPHENE_POINT_T (dx, dy)),
  *                          sx, sy)
  *
- * @self must be a 2D transformation. If you are not
+ * @self must be a 2D affine transformation. If you are not
  * sure, use
  *
  *     gsk_transform_get_category() >= %GSK_TRANSFORM_CATEGORY_2D_AFFINE
diff --git a/gsk/gsktransform.h b/gsk/gsktransform.h
index a580547331..d74d4e197f 100644
--- a/gsk/gsktransform.h
+++ b/gsk/gsktransform.h
@@ -59,6 +59,15 @@ void                    gsk_transform_to_2d                     (GskTransform
                                                                  float                          *out_yy,
                                                                  float                          *out_dx,
                                                                  float                          *out_dy);
+GDK_AVAILABLE_IN_4_6
+void                    gsk_transform_to_2d_components          (GskTransform                   *self,
+                                                                 float                          *out_skew_x,
+                                                                 float                          *out_skew_y,
+                                                                 float                          *out_scale_x,
+                                                                 float                          *out_scale_y,
+                                                                 float                          *out_angle,
+                                                                 float                          *out_dx,
+                                                                 float                          *out_dy);
 GDK_AVAILABLE_IN_ALL
 void                    gsk_transform_to_affine                 (GskTransform                   *self,
                                                                  float                          *out_scale_x,
diff --git a/testsuite/gsk/transform.c b/testsuite/gsk/transform.c
index eba1e05ec0..ec2b138761 100644
--- a/testsuite/gsk/transform.c
+++ b/testsuite/gsk/transform.c
@@ -657,6 +657,84 @@ test_to_2d (void)
   g_assert_cmpfloat (dy, ==, 0.0);
 }
 
+static void
+test_to_2d_components (void)
+{
+  GskTransform *transform, *transform2;
+  float skew_x, skew_y, scale_x, scale_y, angle, dx, dy;
+  graphene_matrix_t m, m2;
+
+  transform = gsk_transform_scale (
+                  gsk_transform_rotate (
+                      gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (10, 20)),
+                      22),
+                  3, 3);
+  gsk_transform_to_2d_components (transform,
+                                  &skew_x, &skew_y,
+                                  &scale_x, &scale_y,
+                                  &angle,
+                                  &dx, &dy);
+  g_assert_cmpfloat_with_epsilon (skew_x, 0, 0.0001);
+  g_assert_cmpfloat_with_epsilon (skew_y, 0, 0.0001);
+  g_assert_cmpfloat_with_epsilon (scale_x, 3, 0.0001);
+  g_assert_cmpfloat_with_epsilon (scale_y, 3, 0.0001);
+  g_assert_cmpfloat_with_epsilon (angle, 22, 0.0001);
+  g_assert_cmpfloat_with_epsilon (dx, 10, 0.0001);
+  g_assert_cmpfloat_with_epsilon (dy, 20, 0.0001);
+
+  gsk_transform_unref (transform);
+
+  transform = gsk_transform_skew (
+                  gsk_transform_scale (
+                      gsk_transform_rotate (
+                          gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (10, 20)),
+                          22),
+                      3, 6),
+                  33, 0);
+
+  g_assert_true (gsk_transform_get_category (transform) >= GSK_TRANSFORM_CATEGORY_2D);
+
+  gsk_transform_to_2d_components (transform,
+                                  &skew_x, &skew_y,
+                                  &scale_x, &scale_y,
+                                  &angle,
+                                  &dx, &dy);
+
+  transform2 = gsk_transform_skew (
+                  gsk_transform_scale (
+                      gsk_transform_rotate (
+                          gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (dx, dy)),
+                          angle),
+                      scale_x, scale_y),
+                  skew_x, skew_y);
+
+  gsk_transform_to_matrix (transform, &m);
+  gsk_transform_to_matrix (transform2, &m2);
+  g_assert_true (graphene_matrix_near (&m, &m2, 0.001));
+
+  gsk_transform_unref (transform);
+  gsk_transform_unref (transform2);
+}
+
+static void
+test_transform_point (void)
+{
+  GskTransform *t, *t2;
+  graphene_point_t p;
+
+  t = gsk_transform_scale (gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (1, 2)), 2, 2);
+  t2 = gsk_transform_translate (gsk_transform_scale (NULL, 2, 2), &GRAPHENE_POINT_INIT (1, 2));
+
+  gsk_transform_transform_point (t, &GRAPHENE_POINT_INIT (1,1), &p);
+  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (3, 4)));
+
+  gsk_transform_transform_point (t2, &GRAPHENE_POINT_INIT (1,1), &p);
+  g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (4, 6)));
+
+  gsk_transform_unref (t);
+  gsk_transform_unref (t2);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -672,7 +750,9 @@ main (int   argc,
   g_test_add_func ("/transform/check-axis-aligneness", test_axis_aligned);
   g_test_add_func ("/transform/to-affine", test_to_affine);
   g_test_add_func ("/transform/bounds", test_transform_bounds);
+  g_test_add_func ("/transform/point", test_transform_point);
   g_test_add_func ("/transform/to-2d", test_to_2d);
+  g_test_add_func ("/transform/to-2d-components", test_to_2d_components);
 
   return g_test_run ();
 }


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