diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-09-18 02:06:00 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-09-18 02:06:51 -0400 |
commit | 41b810da7f36366ca7bbda7630453faed54fa016 (patch) | |
tree | 15a6a5715af1263cc0257b499be332eef676d284 | |
parent | 5742483422a75aa69b7bbeda5e737159baaf1b12 (diff) | |
download | gtk+-41b810da7f36366ca7bbda7630453faed54fa016.tar.gz |
Add gsk_transform_to_2d_components
This function decomposes a general 2D transform
into skew, scale, rotation and translation.
Tests included.
-rw-r--r-- | gsk/gsktransform.c | 118 | ||||
-rw-r--r-- | gsk/gsktransform.h | 9 | ||||
-rw-r--r-- | 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` @@ -1698,6 +1701,95 @@ gsk_transform_to_2d (GskTransform *self, } /** + * 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` * @out_scale_x: (out): return location for the scale @@ -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 (); } |