[gtk: 16/16] transform: Add perspective()



commit 3a3b325f8ebba43fafac574046630a02eae21146
Author: Benjamin Otte <otte redhat com>
Date:   Tue Mar 5 09:27:30 2019 +0100

    transform: Add perspective()
    
    This commit adds gsk_transform_perspective(), gtk_snapshot_perspective()
    and support for perspective() in the CSS syntax.

 docs/reference/gsk/gsk4-sections.txt |   1 +
 docs/reference/gtk/gtk4-sections.txt |   1 +
 gsk/gsktransform.c                   | 114 +++++++++++++++++++++++++++++++
 gsk/gsktransform.h                   |   3 +
 gtk/gtkcsstransformvalue.c           | 129 +++++++++++++++++++++++++++++------
 gtk/gtksnapshot.c                    |  25 ++++++-
 gtk/gtksnapshot.h                    |   3 +
 testsuite/gtk/transform.c            |   4 ++
 8 files changed, 259 insertions(+), 21 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 980be45b99..c66c32cfcb 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -171,6 +171,7 @@ gsk_transform_rotate
 gsk_transform_rotate_3d
 gsk_transform_scale
 gsk_transform_scale_3d
+gsk_transform_perspective
 <SUBSECTION>
 gsk_transform_equal
 <SUBSECTION>
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 2b15df5708..911135eb39 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4388,6 +4388,7 @@ gtk_snapshot_rotate
 gtk_snapshot_rotate_3d
 gtk_snapshot_scale
 gtk_snapshot_scale_3d
+gtk_snapshot_perspective
 gtk_snapshot_append_node
 gtk_snapshot_append_cairo
 gtk_snapshot_append_texture
diff --git a/gsk/gsktransform.c b/gsk/gsktransform.c
index b434d4cb31..a4da1cfad3 100644
--- a/gsk/gsktransform.c
+++ b/gsk/gsktransform.c
@@ -1095,6 +1095,120 @@ gsk_transform_scale_3d (GskTransform *next,
   return &result->parent;
 }
 
+/*** PERSPECTIVE ***/
+
+typedef struct _GskPerspectiveTransform GskPerspectiveTransform;
+
+struct _GskPerspectiveTransform
+{
+  GskTransform parent;
+
+  float depth;
+};
+
+static void
+gsk_perspective_transform_finalize (GskTransform *self)
+{
+}
+
+static void
+gsk_perspective_transform_to_matrix (GskTransform      *transform,
+                                     graphene_matrix_t *out_matrix)
+{
+  GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
+  float f[16] = { 1.f, 0.f, 0.f,  0.f,
+                  0.f, 1.f, 0.f,  0.f,
+                  0.f, 0.f, 1.f, -1.f / self->depth,
+                  0.f, 0.f, 0.f,  1.f };
+
+  graphene_matrix_init_from_float (out_matrix, f);
+}
+
+
+static GskTransform *
+gsk_perspective_transform_apply (GskTransform *transform,
+                                 GskTransform *apply_to)
+{
+  GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
+
+  return gsk_transform_perspective (apply_to, self->depth);
+}
+
+static GskTransform *
+gsk_perspective_transform_invert (GskTransform *transform,
+                                  GskTransform *next)
+{
+  GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
+
+  return gsk_transform_perspective (next, - self->depth);
+}
+
+static gboolean
+gsk_perspective_transform_equal (GskTransform *first_transform,
+                                 GskTransform *second_transform)
+{
+  GskPerspectiveTransform *first = (GskPerspectiveTransform *) first_transform;
+  GskPerspectiveTransform *second = (GskPerspectiveTransform *) second_transform;
+
+  return first->depth == second->depth;
+}
+
+static void
+gsk_perspective_transform_print (GskTransform *transform,
+                                 GString      *string)
+{
+  GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
+
+  g_string_append (string, "perspective(");
+  string_append_double (string, self->depth);
+  g_string_append (string, ")");
+}
+
+static const GskTransformClass GSK_PERSPECTIVE_TRANSFORM_CLASS =
+{
+  sizeof (GskPerspectiveTransform),
+  "GskPerspectiveTransform",
+  gsk_perspective_transform_finalize,
+  gsk_perspective_transform_to_matrix,
+  NULL,
+  NULL,
+  NULL,
+  gsk_perspective_transform_print,
+  gsk_perspective_transform_apply,
+  gsk_perspective_transform_invert,
+  gsk_perspective_transform_equal,
+};
+
+/**
+ * gsk_transform_perspective:
+ * @next: (allow-none): the next transform
+ * @depth: distance of the z=0 plane. Lower values give a more
+ *     flattened pyramid and therefore a more pronounced
+ *     perspective effect.
+ *
+ * Applies a perspective projection transform. This transform
+ * scales points in X and Y based on their Z value, scaling
+ * points with positive Z values away from the origin, and
+ * those with negative Z values towards the origin. Points
+ * on the z=0 plane are unchanged.
+ *
+ * Returns: The new matrix
+ **/
+GskTransform *
+gsk_transform_perspective (GskTransform *next,
+                           float         depth)
+{
+  GskPerspectiveTransform *result;
+  
+  result = gsk_transform_alloc (&GSK_PERSPECTIVE_TRANSFORM_CLASS,
+                                GSK_TRANSFORM_CATEGORY_ANY,
+                                next);
+
+  result->depth = depth;
+
+  return &result->parent;
+}
+
 /*** PUBLIC API ***/
 
 static void
diff --git a/gsk/gsktransform.h b/gsk/gsktransform.h
index 19552a0add..6cbe3659f2 100644
--- a/gsk/gsktransform.h
+++ b/gsk/gsktransform.h
@@ -105,6 +105,9 @@ GskTransform *          gsk_transform_scale_3d                  (GskTransform
                                                                  float                           factor_x,
                                                                  float                           factor_y,
                                                                  float                           factor_z);
+GDK_AVAILABLE_IN_ALL
+GskTransform *          gsk_transform_perspective               (GskTransform                   *next,
+                                                                 float                           depth);
 
 GDK_AVAILABLE_IN_ALL
 void                    gsk_transform_transform_bounds          (GskTransform                   *self,
diff --git a/gtk/gtkcsstransformvalue.c b/gtk/gtkcsstransformvalue.c
index 83aadfcf37..51e9a06fdc 100644
--- a/gtk/gtkcsstransformvalue.c
+++ b/gtk/gtkcsstransformvalue.c
@@ -35,7 +35,8 @@ typedef enum {
   GTK_CSS_TRANSFORM_SCALE,
   GTK_CSS_TRANSFORM_SKEW,
   GTK_CSS_TRANSFORM_SKEW_X,
-  GTK_CSS_TRANSFORM_SKEW_Y
+  GTK_CSS_TRANSFORM_SKEW_Y,
+  GTK_CSS_TRANSFORM_PERSPECTIVE
 } GtkCssTransformType;
 
 union _GtkCssTransform {
@@ -66,6 +67,10 @@ union _GtkCssTransform {
     GtkCssTransformType type;
     GtkCssValue        *skew;
   }                   skew_x, skew_y;
+  struct {
+    GtkCssTransformType type;
+    GtkCssValue        *depth;
+  }                   perspective;
 };
 
 struct _GtkCssValue {
@@ -110,6 +115,9 @@ gtk_css_transform_clear (GtkCssTransform *transform)
     case GTK_CSS_TRANSFORM_SKEW_Y:
       _gtk_css_value_unref (transform->skew_y.skew);
       break;
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      _gtk_css_value_unref (transform->perspective.depth);
+      break;
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
@@ -117,7 +125,7 @@ gtk_css_transform_clear (GtkCssTransform *transform)
     }
 }
 
-static void
+static gboolean
 gtk_css_transform_init_identity (GtkCssTransform     *transform,
                                  GtkCssTransformType  type)
 {
@@ -152,13 +160,18 @@ gtk_css_transform_init_identity (GtkCssTransform     *transform,
     case GTK_CSS_TRANSFORM_SKEW_Y:
       transform->skew_y.skew = _gtk_css_number_value_new (0, GTK_CSS_DEG);
       break;
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      return FALSE;
+
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
-      break;
+      return FALSE;
     }
 
   transform->type = type;
+
+  return TRUE;
 }
 
 static GskTransform *
@@ -198,7 +211,7 @@ gtk_css_transform_apply (const GtkCssTransform   *transform,
                                      _gtk_css_number_value_get (transform->scale.x, 1),
                                      _gtk_css_number_value_get (transform->scale.y, 1),
                                      _gtk_css_number_value_get (transform->scale.z, 1));
-      break;
+
     case GTK_CSS_TRANSFORM_SKEW:
       graphene_matrix_init_skew (&skew,
                                  _gtk_css_number_value_get (transform->skew.x, 100) / 180.0f * G_PI,
@@ -217,6 +230,10 @@ gtk_css_transform_apply (const GtkCssTransform   *transform,
                                  _gtk_css_number_value_get (transform->skew_y.skew, 100) / 180.0f * G_PI);
       return gsk_transform_matrix (next, &skew);
 
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      return gsk_transform_perspective (next,
+                                        _gtk_css_number_value_get (transform->perspective.depth, 100));
+
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
@@ -303,6 +320,9 @@ gtk_css_transform_compute (GtkCssTransform  *dest,
     case GTK_CSS_TRANSFORM_SKEW_Y:
       dest->skew_y.skew = _gtk_css_value_compute (src->skew_y.skew, property_id, provider, style, 
parent_style);
       return dest->skew_y.skew == src->skew_y.skew;
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      dest->perspective.depth = _gtk_css_value_compute (src->perspective.depth, property_id, provider, 
style, parent_style);
+      return dest->perspective.depth == src->perspective.depth;
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
@@ -389,6 +409,8 @@ gtk_css_transform_equal (const GtkCssTransform *transform1,
       return _gtk_css_value_equal (transform1->skew_x.skew, transform2->skew_x.skew);
     case GTK_CSS_TRANSFORM_SKEW_Y:
       return _gtk_css_value_equal (transform1->skew_y.skew, transform2->skew_y.skew);
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      return _gtk_css_value_equal (transform1->perspective.depth, transform2->perspective.depth);
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
@@ -416,7 +438,8 @@ gtk_css_value_transform_equal (const GtkCssValue *value1,
     {
       GtkCssTransform transform;
 
-      gtk_css_transform_init_identity (&transform, larger->transforms[i].type);
+      if (!gtk_css_transform_init_identity (&transform, larger->transforms[i].type))
+        return FALSE;
 
       if (!gtk_css_transform_equal (&larger->transforms[i], &transform))
         {
@@ -430,6 +453,38 @@ gtk_css_value_transform_equal (const GtkCssValue *value1,
   return TRUE;
 }
 
+static void
+gtk_css_transform_transition_default (GtkCssTransform       *result,
+                                      const GtkCssTransform *start,
+                                      const GtkCssTransform *end,
+                                      guint                  property_id,
+                                      double                 progress)
+{
+  graphene_matrix_t start_mat, end_mat;
+  GskTransform *trans;
+
+  result->type = GTK_CSS_TRANSFORM_MATRIX;
+
+  if (start)
+    trans = gtk_css_transform_apply (start, NULL);
+  else
+    trans = NULL;
+  gsk_transform_to_matrix (trans, &start_mat);
+  gsk_transform_unref (trans);
+
+  if (end)
+    trans = gtk_css_transform_apply (end, NULL);
+  else
+    trans = NULL;
+  gsk_transform_to_matrix (trans, &end_mat);
+  gsk_transform_unref (trans);
+
+  graphene_matrix_interpolate (&start_mat,
+                               &end_mat,
+                               progress,
+                               &result->matrix.matrix);
+}
+
 static void
 gtk_css_transform_transition (GtkCssTransform       *result,
                               const GtkCssTransform *start,
@@ -473,6 +528,9 @@ gtk_css_transform_transition (GtkCssTransform       *result,
     case GTK_CSS_TRANSFORM_SKEW_Y:
       result->skew_y.skew = _gtk_css_value_transition (start->skew_y.skew, end->skew_y.skew, property_id, 
progress);
       break;
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      gtk_css_transform_transition_default (result, start, end, property_id, progress);
+      break;
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
@@ -546,25 +604,45 @@ gtk_css_value_transform_transition (GtkCssValue *start,
     {
       GtkCssTransform transform;
 
-      gtk_css_transform_init_identity (&transform, start->transforms[i].type);
-      gtk_css_transform_transition (&result->transforms[i],
-                                    &start->transforms[i],
-                                    &transform,
-                                    property_id,
-                                    progress);
-      gtk_css_transform_clear (&transform);
+      if (gtk_css_transform_init_identity (&transform, start->transforms[i].type))
+        {
+          gtk_css_transform_transition (&result->transforms[i],
+                                        &start->transforms[i],
+                                        &transform,
+                                        property_id,
+                                        progress);
+          gtk_css_transform_clear (&transform);
+        }
+      else
+        {
+          gtk_css_transform_transition_default (&result->transforms[i],
+                                                &start->transforms[i],
+                                                NULL,
+                                                property_id,
+                                                progress);
+        }
     }
   for (; i < end->n_transforms; i++)
     {
       GtkCssTransform transform;
 
-      gtk_css_transform_init_identity (&transform, end->transforms[i].type);
-      gtk_css_transform_transition (&result->transforms[i],
-                                    &transform,
-                                    &end->transforms[i],
-                                    property_id,
-                                    progress);
-      gtk_css_transform_clear (&transform);
+      if (gtk_css_transform_init_identity (&transform, end->transforms[i].type))
+        {
+          gtk_css_transform_transition (&result->transforms[i],
+                                        &transform,
+                                        &end->transforms[i],
+                                        property_id,
+                                        progress);
+          gtk_css_transform_clear (&transform);
+        }
+      else
+        {
+          gtk_css_transform_transition_default (&result->transforms[i],
+                                                NULL,
+                                                &end->transforms[i],
+                                                property_id,
+                                                progress);
+        }
     }
 
   g_assert (i == MAX (start->n_transforms, end->n_transforms));
@@ -678,6 +756,11 @@ gtk_css_transform_print (const GtkCssTransform *transform,
       _gtk_css_value_print (transform->skew_y.skew, string);
       g_string_append (string, ")");
       break;
+    case GTK_CSS_TRANSFORM_PERSPECTIVE:
+      g_string_append (string, "perspective(");
+      _gtk_css_value_print (transform->perspective.depth, string);
+      g_string_append (string, ")");
+      break;
     case GTK_CSS_TRANSFORM_NONE:
     default:
       g_assert_not_reached ();
@@ -1046,6 +1129,14 @@ gtk_css_transform_parse (GtkCssTransform *transform,
       if (transform->skew_y.skew == NULL)
         return FALSE;
     }
+  else if (_gtk_css_parser_try (parser, "perspective(", TRUE))
+    {
+      transform->type = GTK_CSS_TRANSFORM_PERSPECTIVE;
+
+      transform->perspective.depth = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH);
+      if (transform->perspective.depth == NULL)
+        return FALSE;
+    }
   else
     {
       _gtk_css_parser_error (parser, "unknown syntax for transform");
diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c
index cdf14f6b0c..dde973a867 100644
--- a/gtk/gtksnapshot.c
+++ b/gtk/gtksnapshot.c
@@ -1354,7 +1354,7 @@ gtk_snapshot_rotate_3d (GtkSnapshot           *snapshot,
  * @factor_x: scaling factor on the X axis
  * @factor_y: scaling factor on the Y axis
  *
- * Scales @@snapshot's coordinate system in 2-dimensional space by
+ * Scales @snapshot's coordinate system in 2-dimensional space by
  * the given factors.
  *
  * Use gtk_snapshot_scale_3d() to scale in all 3 dimensions.
@@ -1379,7 +1379,7 @@ gtk_snapshot_scale (GtkSnapshot *snapshot,
  * @factor_y: scaling factor on the Y axis
  * @factor_z: scaling factor on the Z axis
  *
- * Scales @@snapshot's coordinate system by the given factors.
+ * Scales @snapshot's coordinate system by the given factors.
  */
 void
 gtk_snapshot_scale_3d (GtkSnapshot *snapshot,
@@ -1395,6 +1395,27 @@ gtk_snapshot_scale_3d (GtkSnapshot *snapshot,
   state->transform = gsk_transform_scale_3d (state->transform, factor_x, factor_y, factor_z);
 }
 
+/**
+ * gtk_snapshot_perspective:
+ * @snapshot: a #GtkSnapshot
+ * @depth: distance of the z=0 plane
+ *
+ * Applies a perspective projection transform.
+ *
+ * See gsk_transform_perspective() for a discussion on the details.
+ */
+void
+gtk_snapshot_perspective (GtkSnapshot *snapshot,
+                          float        depth)
+{
+  GtkSnapshotState *state;
+
+  g_return_if_fail (GTK_IS_SNAPSHOT (snapshot));
+
+  state = gtk_snapshot_get_current_state (snapshot);
+  state->transform = gsk_transform_perspective (state->transform, depth);
+}
+
 void
 gtk_snapshot_append_node_internal (GtkSnapshot   *snapshot,
                                    GskRenderNode *node)
diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h
index d0e8dd9a4f..91c050d048 100644
--- a/gtk/gtksnapshot.h
+++ b/gtk/gtksnapshot.h
@@ -134,6 +134,9 @@ void            gtk_snapshot_scale_3d                   (GtkSnapshot
                                                          float                   factor_y,
                                                          float                   factor_z);
 GDK_AVAILABLE_IN_ALL
+void            gtk_snapshot_perspective                (GtkSnapshot            *snapshot,
+                                                         float                   depth);
+GDK_AVAILABLE_IN_ALL
 void            gtk_snapshot_append_node                (GtkSnapshot            *snapshot,
                                                          GskRenderNode          *node);
 GDK_AVAILABLE_IN_ALL
diff --git a/testsuite/gtk/transform.c b/testsuite/gtk/transform.c
index b3db0881cf..6026463b90 100644
--- a/testsuite/gtk/transform.c
+++ b/testsuite/gtk/transform.c
@@ -85,6 +85,7 @@ static struct {
   { GSK_TRANSFORM_CATEGORY_3D },
   { GSK_TRANSFORM_CATEGORY_2D_AFFINE },
   { GSK_TRANSFORM_CATEGORY_3D },
+  { GSK_TRANSFORM_CATEGORY_ANY },
 };
 
 static GskTransform *
@@ -117,6 +118,9 @@ apply_test_transform (GskTransform *transform,
     case 7:
       return gsk_transform_scale_3d (transform, 2, 3, 5);
 
+    case 8:
+      return gsk_transform_perspective (transform, 5);
+
     default:
       g_assert_not_reached ();
       return NULL;


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