diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt index f85ff189c4..980be45b99 100644 --- a/docs/reference/gsk/gsk4-sections.txt +++ b/docs/reference/gsk/gsk4-sections.txt @@ -163,6 +163,7 @@ gsk_transform_to_affine gsk_transform_to_translate gsk_transform_transform +gsk_transform_invert gsk_transform_matrix gsk_transform_translate gsk_transform_translate_3d diff --git a/gsk/gsktransform.c b/gsk/gsktransform.c index a1e6da676e..767a275af8 100644 --- a/gsk/gsktransform.c +++ b/gsk/gsktransform.c @@ -75,6 +75,8 @@ struct _GskTransformClass GString *string); GskTransform * (* apply) (GskTransform *transform, GskTransform *apply_to); + GskTransform * (* invert) (GskTransform *transform, + GskTransform *next); /* both matrices have the same type */ gboolean (* equal) (GskTransform *first_transform, GskTransform *second_transform); @@ -178,7 +180,26 @@ static GskTransform * gsk_identity_transform_apply (GskTransform *transform, GskTransform *apply_to) { - return apply_to; + /* We do the following to make sure inverting a non-NULL transform + * will return a non-NULL transform. + */ + if (apply_to) + return apply_to; + else + return gsk_transform_new (); +} + +static GskTransform * +gsk_identity_transform_invert (GskTransform *transform, + GskTransform *next) +{ + /* We do the following to make sure inverting a non-NULL transform + * will return a non-NULL transform. + */ + if (next) + return next; + else + return gsk_transform_new (); } static gboolean @@ -200,6 +221,7 @@ static const GskTransformClass GSK_IDENTITY_TRANSFORM_CLASS = gsk_identity_transform_apply_translate, gsk_identity_transform_print, gsk_identity_transform_apply, + gsk_identity_transform_invert, gsk_identity_transform_equal, }; @@ -372,6 +394,24 @@ gsk_matrix_transform_apply (GskTransform *transform, self->category); } +static GskTransform * +gsk_matrix_transform_invert (GskTransform *transform, + GskTransform *next) +{ + GskMatrixTransform *self = (GskMatrixTransform *) transform; + graphene_matrix_t inverse; + + if (!graphene_matrix_inverse (&self->matrix, &inverse)) + { + gsk_transform_unref (next); + return NULL; + } + + return gsk_transform_matrix_with_category (next, + &inverse, + self->category); +} + static gboolean gsk_matrix_transform_equal (GskTransform *first_transform, GskTransform *second_transform) @@ -395,6 +435,7 @@ static const GskTransformClass GSK_TRANSFORM_TRANSFORM_CLASS = gsk_matrix_transform_apply_translate, gsk_matrix_transform_print, gsk_matrix_transform_apply, + gsk_matrix_transform_invert, gsk_matrix_transform_equal, }; @@ -526,6 +567,15 @@ gsk_translate_transform_apply (GskTransform *transform, return gsk_transform_translate_3d (apply_to, &self->point); } +static GskTransform * +gsk_translate_transform_invert (GskTransform *transform, + GskTransform *next) +{ + GskTranslateTransform *self = (GskTranslateTransform *) transform; + + return gsk_transform_translate_3d (next, &GRAPHENE_POINT3D_INIT (-self->point.x, -self->point.y, -self->point.z)); +} + static gboolean gsk_translate_transform_equal (GskTransform *first_transform, GskTransform *second_transform) @@ -570,6 +620,7 @@ static const GskTransformClass GSK_TRANSLATE_TRANSFORM_CLASS = gsk_translate_transform_apply_translate, gsk_translate_transform_print, gsk_translate_transform_apply, + gsk_translate_transform_invert, gsk_translate_transform_equal, }; @@ -710,6 +761,15 @@ gsk_rotate_transform_apply (GskTransform *transform, return gsk_transform_rotate (apply_to, self->angle); } +static GskTransform * +gsk_rotate_transform_invert (GskTransform *transform, + GskTransform *next) +{ + GskRotateTransform *self = (GskRotateTransform *) transform; + + return gsk_transform_rotate (next, - self->angle); +} + static gboolean gsk_rotate_transform_equal (GskTransform *first_transform, GskTransform *second_transform) @@ -743,6 +803,7 @@ static const GskTransformClass GSK_ROTATE_TRANSFORM_CLASS = gsk_rotate_transform_apply_translate, gsk_rotate_transform_print, gsk_rotate_transform_apply, + gsk_rotate_transform_invert, gsk_rotate_transform_equal, }; @@ -837,6 +898,15 @@ gsk_rotate3d_transform_apply (GskTransform *transform, return gsk_transform_rotate_3d (apply_to, self->angle, &self->axis); } +static GskTransform * +gsk_rotate3d_transform_invert (GskTransform *transform, + GskTransform *next) +{ + GskRotate3dTransform *self = (GskRotate3dTransform *) transform; + + return gsk_transform_rotate_3d (next, - self->angle, &self->axis); +} + static gboolean gsk_rotate3d_transform_equal (GskTransform *first_transform, GskTransform *second_transform) @@ -879,6 +949,7 @@ static const GskTransformClass GSK_ROTATE3D_TRANSFORM_CLASS = gsk_rotate3d_transform_apply_translate, gsk_rotate3d_transform_print, gsk_rotate3d_transform_apply, + gsk_rotate3d_transform_invert, gsk_rotate3d_transform_equal, }; @@ -1007,6 +1078,18 @@ gsk_scale_transform_apply (GskTransform *transform, return gsk_transform_scale_3d (apply_to, self->factor_x, self->factor_y, self->factor_z); } +static GskTransform * +gsk_scale_transform_invert (GskTransform *transform, + GskTransform *next) +{ + GskScaleTransform *self = (GskScaleTransform *) transform; + + return gsk_transform_scale_3d (next, + 1.f / self->factor_x, + 1.f / self->factor_y, + 1.f / self->factor_z); +} + static gboolean gsk_scale_transform_equal (GskTransform *first_transform, GskTransform *second_transform) @@ -1060,6 +1143,7 @@ static const GskTransformClass GSK_SCALE_TRANSFORM_CLASS = gsk_scale_transform_apply_translate, gsk_scale_transform_print, gsk_scale_transform_apply, + gsk_scale_transform_invert, gsk_scale_transform_equal, }; @@ -1394,6 +1478,39 @@ gsk_transform_transform (GskTransform *next, return other->transform_class->apply (other, next); } +/** + * gsk_transform_invert: + * @self: (allow-none) (transfer full): Transform to invert + * + * Inverts the given transform. + * + * If @self is not invertible, %NULL is returned. + * Note that inverting %NULL also returns %NULL, which is + * the correct inverse of %NULL. If you need to differentiate + * between those cases, you should check @self is not %NULL + * before calling this function. + * + * Returns: The inverted transform or %NULL if the transform + * cannot be inverted. + **/ +GskTransform * +gsk_transform_invert (GskTransform *self) +{ + GskTransform *result = NULL; + GskTransform *cur; + + for (cur = self; cur; cur = cur->next) + { + result = cur->transform_class->invert (cur, result); + if (result == NULL) + break; + } + + gsk_transform_unref (self); + + return result; +} + /** * gsk_transform_equal: * @first: the first matrix diff --git a/gsk/gsktransform.h b/gsk/gsktransform.h index 3696f6fab2..4a90b1308b 100644 --- a/gsk/gsktransform.h +++ b/gsk/gsktransform.h @@ -79,6 +79,8 @@ GDK_AVAILABLE_IN_ALL GskTransform * gsk_transform_transform (GskTransform *next, GskTransform *other); GDK_AVAILABLE_IN_ALL +GskTransform * gsk_transform_invert (GskTransform *self); +GDK_AVAILABLE_IN_ALL GskTransform * gsk_transform_matrix (GskTransform *next, const graphene_matrix_t *matrix); GDK_AVAILABLE_IN_ALL diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 1aac43184a..bdee0bf4dc 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -820,13 +820,23 @@ gtk_widget_real_pick (GtkWidget *widget, child = _gtk_widget_get_prev_sibling (child)) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (child); + GskTransform *transform; graphene_matrix_t inv; GtkWidget *picked; graphene_point3d_t p0, p1, res; - gsk_transform_to_matrix (priv->transform, &inv); - if (!graphene_matrix_inverse (&inv, &inv)) - continue; + if (priv->transform) + { + transform = gsk_transform_invert (gsk_transform_ref (priv->transform)); + if (transform == NULL) + continue; + } + else + { + transform = NULL; + } + gsk_transform_to_matrix (transform, &inv); + gsk_transform_unref (transform); graphene_point3d_init (&p0, x, y, 0); graphene_point3d_init (&p1, x, y, 1); graphene_matrix_transform_point3d (&inv, &p0, &p0); diff --git a/testsuite/gtk/transform.c b/testsuite/gtk/transform.c index 0d7a8ad9f1..0a71af4fd0 100644 --- a/testsuite/gtk/transform.c +++ b/testsuite/gtk/transform.c @@ -21,7 +21,7 @@ #include -#define EPSILON (1.f / 1024 / 1024) +#define EPSILON (1.f / 1024 / 32) /* 2^-15 */ /* macros stolen from graphene testsuite, so they get to keep their names */ @@ -66,6 +66,14 @@ } \ } G_STMT_END +#define graphene_assert_fuzzy_transform_equal(t1,t2,epsilon) \ + G_STMT_START { \ + graphene_matrix_t __mat1, __mat2; \ + gsk_transform_to_matrix ((t1), &__mat1); \ + gsk_transform_to_matrix ((t2), &__mat2); \ + graphene_assert_fuzzy_matrix_equal (&__mat1, &__mat2, (epsilon)); \ + } G_STMT_END + static struct { GskTransformCategory category; } test_transforms[] = { @@ -235,6 +243,38 @@ test_conversions_transformed (void) } } +static void +test_invert (void) +{ + GskTransform *transform, *inverse, *identity; + guint i, j, k; + + for (i = 0; i < G_N_ELEMENTS (test_transforms); i++) + { + for (j = 0; j < G_N_ELEMENTS (test_transforms); j++) + { + for (k = 0; k < G_N_ELEMENTS (test_transforms); k++) + { + transform = apply_test_transform (NULL, i); + transform = apply_test_transform (transform, j); + transform = apply_test_transform (transform, k); + inverse = gsk_transform_invert (gsk_transform_ref (transform)); + g_assert (inverse != NULL || transform == NULL); + + identity = gsk_transform_transform (gsk_transform_ref (transform), inverse); + graphene_assert_fuzzy_transform_equal (identity, NULL, EPSILON); + gsk_transform_unref (identity); + + inverse = gsk_transform_invert (inverse); + graphene_assert_fuzzy_transform_equal (transform, inverse, EPSILON); + + gsk_transform_unref (transform); + gsk_transform_unref (inverse); + } + } + } +} + int main (int argc, char *argv[]) @@ -243,6 +283,7 @@ main (int argc, g_test_add_func ("/transform/conversions/simple", test_conversions_simple); g_test_add_func ("/transform/conversions/transformed", test_conversions_transformed); + g_test_add_func ("/transform/invert", test_invert); return g_test_run (); }