summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2019-03-05 09:27:30 +0100
committerBenjamin Otte <otte@redhat.com>2019-03-05 20:46:00 +0100
commit3a3b325f8ebba43fafac574046630a02eae21146 (patch)
treec7d2ab3998821ada2775b6cbab10037940cd1bf5
parentdbe58452d75d97f10650728b69c8981e33dc74d8 (diff)
downloadgtk+-3a3b325f8ebba43fafac574046630a02eae21146.tar.gz
transform: Add perspective()
This commit adds gsk_transform_perspective(), gtk_snapshot_perspective() and support for perspective() in the CSS syntax.
-rw-r--r--docs/reference/gsk/gsk4-sections.txt1
-rw-r--r--docs/reference/gtk/gtk4-sections.txt1
-rw-r--r--gsk/gsktransform.c114
-rw-r--r--gsk/gsktransform.h3
-rw-r--r--gtk/gtkcsstransformvalue.c129
-rw-r--r--gtk/gtksnapshot.c25
-rw-r--r--gtk/gtksnapshot.h3
-rw-r--r--testsuite/gtk/transform.c4
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))
{
@@ -431,6 +454,38 @@ gtk_css_value_transform_equal (const GtkCssValue *value1,
}
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,
const GtkCssTransform *end,
@@ -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;