/* GTK - The GIMP Toolkit
* Copyright ( C ) 2011 Red Hat , Inc .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see < http : //www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkcsstransformvalueprivate.h"
#include <math.h>
#include <string.h>
#include "gtkcssnumbervalueprivate.h"
#include "gsktransform.h"
typedef union _GtkCssTransform GtkCssTransform;
typedef enum {
GTK_CSS_TRANSFORM_NONE,
GTK_CSS_TRANSFORM_MATRIX,
GTK_CSS_TRANSFORM_TRANSLATE,
GTK_CSS_TRANSFORM_ROTATE,
GTK_CSS_TRANSFORM_SCALE,
GTK_CSS_TRANSFORM_SKEW,
GTK_CSS_TRANSFORM_SKEW_X,
GTK_CSS_TRANSFORM_SKEW_Y,
GTK_CSS_TRANSFORM_PERSPECTIVE
} GtkCssTransformType;
union _GtkCssTransform {
GtkCssTransformType type;
struct {
GtkCssTransformType type;
graphene_matrix_t matrix;
} matrix;
struct {
GtkCssTransformType type;
GtkCssValue *x;
GtkCssValue *y;
GtkCssValue *z;
} translate, scale;
struct {
GtkCssTransformType type;
GtkCssValue *x;
GtkCssValue *y;
} skew;
struct {
GtkCssTransformType type;
GtkCssValue *x;
GtkCssValue *y;
GtkCssValue *z;
GtkCssValue *angle;
} rotate;
struct {
GtkCssTransformType type;
GtkCssValue *skew;
} skew_x, skew_y;
struct {
GtkCssTransformType type;
GtkCssValue *depth;
} perspective;
};
struct _GtkCssValue {
GTK_CSS_VALUE_BASE
guint n_transforms;
GtkCssTransform transforms[1 ];
};
static GtkCssValue * gtk_css_transform_value_alloc (guint n_values);
static gboolean gtk_css_transform_value_is_none (const GtkCssValue *value);
static void
gtk_css_transform_clear (GtkCssTransform *transform)
{
switch (transform->type)
{
case GTK_CSS_TRANSFORM_MATRIX:
break ;
case GTK_CSS_TRANSFORM_TRANSLATE:
gtk_css_value_unref (transform->translate.x);
gtk_css_value_unref (transform->translate.y);
gtk_css_value_unref (transform->translate.z);
break ;
case GTK_CSS_TRANSFORM_ROTATE:
gtk_css_value_unref (transform->rotate.x);
gtk_css_value_unref (transform->rotate.y);
gtk_css_value_unref (transform->rotate.z);
gtk_css_value_unref (transform->rotate.angle);
break ;
case GTK_CSS_TRANSFORM_SCALE:
gtk_css_value_unref (transform->scale.x);
gtk_css_value_unref (transform->scale.y);
gtk_css_value_unref (transform->scale.z);
break ;
case GTK_CSS_TRANSFORM_SKEW:
gtk_css_value_unref (transform->skew.x);
gtk_css_value_unref (transform->skew.y);
break ;
case GTK_CSS_TRANSFORM_SKEW_X:
gtk_css_value_unref (transform->skew_x.skew);
break ;
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 ();
break ;
}
}
static gboolean
gtk_css_transform_init_identity (GtkCssTransform *transform,
GtkCssTransformType type)
{
switch (type)
{
case GTK_CSS_TRANSFORM_MATRIX:
graphene_matrix_init_identity (&transform->matrix.matrix);
break ;
case GTK_CSS_TRANSFORM_TRANSLATE:
transform->translate.x = gtk_css_number_value_new (0 , GTK_CSS_PX);
transform->translate.y = gtk_css_number_value_new (0 , GTK_CSS_PX);
transform->translate.z = gtk_css_number_value_new (0 , GTK_CSS_PX);
break ;
case GTK_CSS_TRANSFORM_ROTATE:
transform->rotate.x = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
transform->rotate.y = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
transform->rotate.z = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform->rotate.angle = gtk_css_number_value_new (0 , GTK_CSS_DEG);
break ;
case GTK_CSS_TRANSFORM_SCALE:
transform->scale.x = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform->scale.y = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform->scale.z = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
break ;
case GTK_CSS_TRANSFORM_SKEW:
transform->skew.x = gtk_css_number_value_new (0 , GTK_CSS_DEG);
transform->skew.y = gtk_css_number_value_new (0 , GTK_CSS_DEG);
break ;
case GTK_CSS_TRANSFORM_SKEW_X:
transform->skew_x.skew = gtk_css_number_value_new (0 , GTK_CSS_DEG);
break ;
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 ();
return FALSE ;
}
transform->type = type;
return TRUE ;
}
static GskTransform *
gtk_css_transform_apply (const GtkCssTransform *transform,
GskTransform *next)
{
graphene_matrix_t skew;
switch (transform->type)
{
case GTK_CSS_TRANSFORM_MATRIX:
return gsk_transform_matrix (next, &transform->matrix.matrix);
case GTK_CSS_TRANSFORM_TRANSLATE:
return gsk_transform_translate_3d (next,
&GRAPHENE_POINT3D_INIT (
gtk_css_number_value_get (transform->translate.x, 100 ),
gtk_css_number_value_get (transform->translate.y, 100 ),
gtk_css_number_value_get (transform->translate.z, 100 )
));
case GTK_CSS_TRANSFORM_ROTATE:
{
graphene_vec3_t axis;
graphene_vec3_init (&axis,
gtk_css_number_value_get (transform->rotate.x, 1 ),
gtk_css_number_value_get (transform->rotate.y, 1 ),
gtk_css_number_value_get (transform->rotate.z, 1 ));
return gsk_transform_rotate_3d (next,
gtk_css_number_value_get (transform->rotate.angle, 100 ),
&axis);
}
case GTK_CSS_TRANSFORM_SCALE:
return gsk_transform_scale_3d (next,
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 ));
case GTK_CSS_TRANSFORM_SKEW:
graphene_matrix_init_skew (&skew,
gtk_css_number_value_get (transform->skew.x, 100 ) / 180 .0 f * G_PI,
gtk_css_number_value_get (transform->skew.y, 100 ) / 180 .0 f * G_PI);
return gsk_transform_matrix (next, &skew);
case GTK_CSS_TRANSFORM_SKEW_X:
graphene_matrix_init_skew (&skew,
gtk_css_number_value_get (transform->skew_x.skew, 100 ) / 180 .0 f * G_PI,
0 );
return gsk_transform_matrix (next, &skew);
case GTK_CSS_TRANSFORM_SKEW_Y:
graphene_matrix_init_skew (&skew,
0 ,
gtk_css_number_value_get (transform->skew_y.skew, 100 ) / 180 .0 f * 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 ();
break ;
}
return NULL;
}
/* NB: The returned matrix may be invalid */
static GskTransform *
gtk_css_transform_value_compute_transform (const GtkCssValue *value)
{
GskTransform *transform;
guint i;
transform = NULL;
for (i = 0 ; i < value->n_transforms; i++)
{
transform = gtk_css_transform_apply (&value->transforms[i], transform);
}
return transform;
}
static void
gtk_css_value_transform_free (GtkCssValue *value)
{
guint i;
for (i = 0 ; i < value->n_transforms; i++)
gtk_css_transform_clear (&value->transforms[i]);
g_free (value);
}
/* returns TRUE if dest == src */
static gboolean
gtk_css_transform_compute (GtkCssTransform *dest,
GtkCssTransform *src,
guint property_id,
GtkCssComputeContext *context)
{
dest->type = src->type;
switch (src->type)
{
case GTK_CSS_TRANSFORM_MATRIX:
memcpy (dest, src, sizeof (GtkCssTransform));
return TRUE ;
case GTK_CSS_TRANSFORM_TRANSLATE:
dest->translate.x = gtk_css_value_compute (src->translate.x, property_id, context);
dest->translate.y = gtk_css_value_compute (src->translate.y, property_id, context);
dest->translate.z = gtk_css_value_compute (src->translate.z, property_id, context);
return dest->translate.x == src->translate.x
&& dest->translate.y == src->translate.y
&& dest->translate.z == src->translate.z;
case GTK_CSS_TRANSFORM_ROTATE:
dest->rotate.x = gtk_css_value_compute (src->rotate.x, property_id, context);
dest->rotate.y = gtk_css_value_compute (src->rotate.y, property_id, context);
dest->rotate.z = gtk_css_value_compute (src->rotate.z, property_id, context);
dest->rotate.angle = gtk_css_value_compute (src->rotate.angle, property_id, context);
return dest->rotate.x == src->rotate.x
&& dest->rotate.y == src->rotate.y
&& dest->rotate.z == src->rotate.z
&& dest->rotate.angle == src->rotate.angle;
case GTK_CSS_TRANSFORM_SCALE:
dest->scale.x = gtk_css_value_compute (src->scale.x, property_id, context);
dest->scale.y = gtk_css_value_compute (src->scale.y, property_id, context);
dest->scale.z = gtk_css_value_compute (src->scale.z, property_id, context);
return dest->scale.x == src->scale.x
&& dest->scale.y == src->scale.y
&& dest->scale.z == src->scale.z;
case GTK_CSS_TRANSFORM_SKEW:
dest->skew.x = gtk_css_value_compute (src->skew.x, property_id, context);
dest->skew.y = gtk_css_value_compute (src->skew.y, property_id, context);
return dest->skew.x == src->skew.x
&& dest->skew.y == src->skew.y;
case GTK_CSS_TRANSFORM_SKEW_X:
dest->skew_x.skew = gtk_css_value_compute (src->skew_x.skew, property_id, context);
return dest->skew_x.skew == src->skew_x.skew;
case GTK_CSS_TRANSFORM_SKEW_Y:
dest->skew_y.skew = gtk_css_value_compute (src->skew_y.skew, property_id, context);
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, context);
return dest->perspective.depth == src->perspective.depth;
case GTK_CSS_TRANSFORM_NONE:
default :
g_assert_not_reached ();
return FALSE ;
}
}
static GtkCssValue *
gtk_css_value_transform_compute (GtkCssValue *value,
guint property_id,
GtkCssComputeContext *context)
{
GtkCssValue *result;
gboolean changes;
guint i;
/* Special case the 99% case of "none" */
if (gtk_css_transform_value_is_none (value))
return gtk_css_value_ref (value);
changes = FALSE ;
result = gtk_css_transform_value_alloc (value->n_transforms);
for (i = 0 ; i < value->n_transforms; i++)
{
changes |= !gtk_css_transform_compute (&result->transforms[i],
&value->transforms[i],
property_id,
context);
}
if (!changes)
{
gtk_css_value_unref (result);
result = gtk_css_value_ref (value);
}
return result;
}
static gboolean
gtk_css_transform_equal (const GtkCssTransform *transform1,
const GtkCssTransform *transform2)
{
if (transform1->type != transform2->type)
return FALSE ;
switch (transform1->type)
{
case GTK_CSS_TRANSFORM_MATRIX:
{
guint i, j;
for (i = 0 ; i < 4 ; i++)
for (j = 0 ; j < 4 ; j++)
{
if (graphene_matrix_get_value (&transform1->matrix.matrix, i, j)
!= graphene_matrix_get_value (&transform2->matrix.matrix, i, j))
return FALSE ;
}
return TRUE ;
}
case GTK_CSS_TRANSFORM_TRANSLATE:
return gtk_css_value_equal (transform1->translate.x, transform2->translate.x)
&& gtk_css_value_equal (transform1->translate.y, transform2->translate.y)
&& gtk_css_value_equal (transform1->translate.z, transform2->translate.z);
case GTK_CSS_TRANSFORM_ROTATE:
return gtk_css_value_equal (transform1->rotate.x, transform2->rotate.x)
&& gtk_css_value_equal (transform1->rotate.y, transform2->rotate.y)
&& gtk_css_value_equal (transform1->rotate.z, transform2->rotate.z)
&& gtk_css_value_equal (transform1->rotate.angle, transform2->rotate.angle);
case GTK_CSS_TRANSFORM_SCALE:
return gtk_css_value_equal (transform1->scale.x, transform2->scale.x)
&& gtk_css_value_equal (transform1->scale.y, transform2->scale.y)
&& gtk_css_value_equal (transform1->scale.z, transform2->scale.z);
case GTK_CSS_TRANSFORM_SKEW:
return gtk_css_value_equal (transform1->skew.x, transform2->skew.x)
&& gtk_css_value_equal (transform1->skew.y, transform2->skew.y);
case GTK_CSS_TRANSFORM_SKEW_X:
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 ();
return FALSE ;
}
}
static gboolean
gtk_css_value_transform_equal (const GtkCssValue *value1,
const GtkCssValue *value2)
{
const GtkCssValue *larger;
guint i, n;
n = MIN (value1->n_transforms, value2->n_transforms);
for (i = 0 ; i < n; i++)
{
if (!gtk_css_transform_equal (&value1->transforms[i], &value2->transforms[i]))
return FALSE ;
}
larger = value1->n_transforms > value2->n_transforms ? value1 : value2;
for (; i < larger->n_transforms; i++)
{
GtkCssTransform transform;
if (!gtk_css_transform_init_identity (&transform, larger->transforms[i].type))
return FALSE ;
if (!gtk_css_transform_equal (&larger->transforms[i], &transform))
{
gtk_css_transform_clear (&transform);
return FALSE ;
}
gtk_css_transform_clear (&transform);
}
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,
const GtkCssTransform *end,
guint property_id,
double progress)
{
result->type = start->type;
switch (start->type)
{
case GTK_CSS_TRANSFORM_MATRIX:
graphene_matrix_interpolate (&start->matrix.matrix,
&end->matrix.matrix,
progress,
&result->matrix.matrix);
break ;
case GTK_CSS_TRANSFORM_TRANSLATE:
result->translate.x = gtk_css_value_transition (start->translate.x, end->translate.x, property_id, progress);
result->translate.y = gtk_css_value_transition (start->translate.y, end->translate.y, property_id, progress);
result->translate.z = gtk_css_value_transition (start->translate.z, end->translate.z, property_id, progress);
break ;
case GTK_CSS_TRANSFORM_ROTATE:
result->rotate.x = gtk_css_value_transition (start->rotate.x, end->rotate.x, property_id, progress);
result->rotate.y = gtk_css_value_transition (start->rotate.y, end->rotate.y, property_id, progress);
result->rotate.z = gtk_css_value_transition (start->rotate.z, end->rotate.z, property_id, progress);
result->rotate.angle = gtk_css_value_transition (start->rotate.angle, end->rotate.angle, property_id, progress);
break ;
case GTK_CSS_TRANSFORM_SCALE:
result->scale.x = gtk_css_value_transition (start->scale.x, end->scale.x, property_id, progress);
result->scale.y = gtk_css_value_transition (start->scale.y, end->scale.y, property_id, progress);
result->scale.z = gtk_css_value_transition (start->scale.z, end->scale.z, property_id, progress);
break ;
case GTK_CSS_TRANSFORM_SKEW:
result->skew.x = gtk_css_value_transition (start->skew.x, end->skew.x, property_id, progress);
result->skew.y = gtk_css_value_transition (start->skew.y, end->skew.y, property_id, progress);
break ;
case GTK_CSS_TRANSFORM_SKEW_X:
result->skew_x.skew = gtk_css_value_transition (start->skew_x.skew, end->skew_x.skew, property_id, progress);
break ;
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 ();
break ;
}
}
static GtkCssValue *
gtk_css_value_transform_transition (GtkCssValue *start,
GtkCssValue *end,
guint property_id,
double progress)
{
GtkCssValue *result;
guint i, n;
if (gtk_css_transform_value_is_none (start))
{
if (gtk_css_transform_value_is_none (end))
return gtk_css_value_ref (start);
n = 0 ;
}
else if (gtk_css_transform_value_is_none (end))
{
n = 0 ;
}
else
{
n = MIN (start->n_transforms, end->n_transforms);
}
/* Check transforms are compatible. If not, transition between
* their result matrices .
*/
for (i = 0 ; i < n; i++)
{
if (start->transforms[i].type != end->transforms[i].type)
{
GskTransform *transform;
graphene_matrix_t start_matrix, end_matrix;
transform = gtk_css_transform_value_compute_transform (start);
gsk_transform_to_matrix (transform, &start_matrix);
gsk_transform_unref (transform);
transform = gtk_css_transform_value_compute_transform (end);
gsk_transform_to_matrix (transform, &end_matrix);
gsk_transform_unref (transform);
result = gtk_css_transform_value_alloc (1 );
result->transforms[0 ].type = GTK_CSS_TRANSFORM_MATRIX;
graphene_matrix_interpolate (&start_matrix, &end_matrix, progress, &result->transforms[0 ].matrix.matrix);
return result;
}
}
result = gtk_css_transform_value_alloc (MAX (start->n_transforms, end->n_transforms));
for (i = 0 ; i < n; i++)
{
gtk_css_transform_transition (&result->transforms[i],
&start->transforms[i],
&end->transforms[i],
property_id,
progress);
}
for (; i < start->n_transforms; i++)
{
GtkCssTransform 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;
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));
return result;
}
static void
gtk_css_transform_print (const GtkCssTransform *transform,
GString *string)
{
char buf[G_ASCII_DTOSTR_BUF_SIZE];
switch (transform->type)
{
case GTK_CSS_TRANSFORM_MATRIX:
if (graphene_matrix_is_2d (&transform->matrix.matrix))
{
g_string_append (string, "matrix(" );
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, 0 , 0 ));
g_string_append (string, buf);
g_string_append (string, ", " );
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, 0 , 1 ));
g_string_append (string, buf);
g_string_append (string, ", " );
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, 0 , 2 ));
g_string_append (string, buf);
g_string_append (string, ", " );
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, 1 , 0 ));
g_string_append (string, buf);
g_string_append (string, ", " );
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, 1 , 1 ));
g_string_append (string, buf);
g_string_append (string, ", " );
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, 1 , 2 ));
g_string_append (string, buf);
g_string_append (string, ")" );
}
else
{
guint i;
g_string_append (string, "matrix3d(" );
for (i = 0 ; i < 16 ; i++)
{
g_ascii_dtostr (buf, sizeof (buf), graphene_matrix_get_value (&transform->matrix.matrix, i / 4 , i % 4 ));
g_string_append (string, buf);
if (i < 15 )
g_string_append (string, ", " );
}
g_string_append (string, ")" );
}
break ;
case GTK_CSS_TRANSFORM_TRANSLATE:
g_string_append (string, "translate3d(" );
gtk_css_value_print (transform->translate.x, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->translate.y, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->translate.z, string);
g_string_append (string, ")" );
break ;
case GTK_CSS_TRANSFORM_ROTATE:
g_string_append (string, "rotate3d(" );
gtk_css_value_print (transform->rotate.x, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->rotate.y, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->rotate.z, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->rotate.angle, string);
g_string_append (string, ")" );
break ;
case GTK_CSS_TRANSFORM_SCALE:
if (gtk_css_number_value_get (transform->scale.z, 100 ) == 1 )
{
g_string_append (string, "scale(" );
gtk_css_value_print (transform->scale.x, string);
if (!gtk_css_value_equal (transform->scale.x, transform->scale.y))
{
g_string_append (string, ", " );
gtk_css_value_print (transform->scale.y, string);
}
g_string_append (string, ")" );
}
else
{
g_string_append (string, "scale3d(" );
gtk_css_value_print (transform->scale.x, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->scale.y, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->scale.z, string);
g_string_append (string, ")" );
}
break ;
case GTK_CSS_TRANSFORM_SKEW:
g_string_append (string, "skew(" );
gtk_css_value_print (transform->skew.x, string);
g_string_append (string, ", " );
gtk_css_value_print (transform->skew.y, string);
g_string_append (string, ")" );
break ;
case GTK_CSS_TRANSFORM_SKEW_X:
g_string_append (string, "skewX(" );
gtk_css_value_print (transform->skew_x.skew, string);
g_string_append (string, ")" );
break ;
case GTK_CSS_TRANSFORM_SKEW_Y:
g_string_append (string, "skewY(" );
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 ();
break ;
}
}
static void
gtk_css_value_transform_print (const GtkCssValue *value,
GString *string)
{
guint i;
if (gtk_css_transform_value_is_none (value))
{
g_string_append (string, "none" );
return ;
}
for (i = 0 ; i < value->n_transforms; i++)
{
if (i > 0 )
g_string_append_c (string, ' ' );
gtk_css_transform_print (&value->transforms[i], string);
}
}
static const GtkCssValueClass GTK_CSS_VALUE_TRANSFORM = {
"GtkCssTransformValue" ,
gtk_css_value_transform_free,
gtk_css_value_transform_compute,
NULL,
gtk_css_value_transform_equal,
gtk_css_value_transform_transition,
NULL,
NULL,
gtk_css_value_transform_print
};
static GtkCssValue transform_none_singleton = { >K_CSS_VALUE_TRANSFORM, 1 , 1 , 0 , 0 , 0 , { { GTK_CSS_TRANSFORM_NONE } } };
static GtkCssValue *
gtk_css_transform_value_alloc (guint n_transforms)
{
GtkCssValue *result;
g_return_val_if_fail (n_transforms > 0 , NULL);
result = gtk_css_value_alloc (>K_CSS_VALUE_TRANSFORM, sizeof (GtkCssValue) + sizeof (GtkCssTransform) * (n_transforms - 1 ));
result->n_transforms = n_transforms;
return result;
}
GtkCssValue *
_gtk_css_transform_value_new_none (void )
{
return gtk_css_value_ref (&transform_none_singleton);
}
static gboolean
gtk_css_transform_value_is_none (const GtkCssValue *value)
{
return value->n_transforms == 0 ;
}
static guint
gtk_css_transform_parse_float (GtkCssParser *parser,
guint n,
gpointer data)
{
float *f = data;
double d;
if (!gtk_css_parser_consume_number (parser, &d))
return 0 ;
f[n] = d;
return 1 ;
}
static guint
gtk_css_transform_parse_length (GtkCssParser *parser,
guint n,
gpointer data)
{
GtkCssValue **values = data;
values[n] = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH);
if (values[n] == NULL)
return 0 ;
return 1 ;
}
static guint
gtk_css_transform_parse_angle (GtkCssParser *parser,
guint n,
gpointer data)
{
GtkCssValue **values = data;
values[n] = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE);
if (values[n] == NULL)
return 0 ;
return 1 ;
}
static guint
gtk_css_transform_parse_number (GtkCssParser *parser,
guint n,
gpointer data)
{
GtkCssValue **values = data;
values[n] = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER);
if (values[n] == NULL)
return 0 ;
return 1 ;
}
static guint
gtk_css_transform_parse_rotate3d (GtkCssParser *parser,
guint n,
gpointer data)
{
GtkCssTransform *transform = data;
switch (n)
{
case 0 :
transform->rotate.x = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER);
if (transform->rotate.x == NULL)
return 0 ;
break ;
case 1 :
transform->rotate.y = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER);
if (transform->rotate.y == NULL)
return 0 ;
break ;
case 2 :
transform->rotate.z = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER);
if (transform->rotate.z == NULL)
return 0 ;
break ;
case 3 :
transform->rotate.angle = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE);
if (transform->rotate.angle == NULL)
return 0 ;
break ;
default :
g_assert_not_reached();
return 0 ;
}
return 1 ;
}
GtkCssValue *
_gtk_css_transform_value_parse (GtkCssParser *parser)
{
GtkCssValue *value;
GArray *array;
guint i;
gboolean computed = TRUE ;
if (gtk_css_parser_try_ident (parser, "none" ))
return _gtk_css_transform_value_new_none ();
array = g_array_new (FALSE , FALSE , sizeof (GtkCssTransform));
while (TRUE )
{
GtkCssTransform transform;
memset (&transform, 0 , sizeof (GtkCssTransform));
if (gtk_css_parser_has_function (parser, "matrix" ))
{
float f[6 ];
if (!gtk_css_parser_consume_function (parser, 6 , 6 , gtk_css_transform_parse_float, f))
goto fail;
transform.type = GTK_CSS_TRANSFORM_MATRIX;
graphene_matrix_init_from_2d (&transform.matrix.matrix, f[0 ], f[1 ], f[2 ], f[3 ], f[4 ], f[5 ]);
}
else if (gtk_css_parser_has_function (parser, "matrix3d" ))
{
float f[16 ];
if (!gtk_css_parser_consume_function (parser, 16 , 16 , gtk_css_transform_parse_float, f))
goto fail;
transform.type = GTK_CSS_TRANSFORM_MATRIX;
graphene_matrix_init_from_float (&transform.matrix.matrix, f);
}
else if (gtk_css_parser_has_function (parser, "perspective" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_length, &transform.perspective.depth))
goto fail;
transform.type = GTK_CSS_TRANSFORM_PERSPECTIVE;
computed = computed && gtk_css_value_is_computed (transform.perspective.depth);
}
else if (gtk_css_parser_has_function (parser, "rotate" ) ||
gtk_css_parser_has_function (parser, "rotateZ" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_angle, &transform.rotate.angle))
goto fail;
transform.type = GTK_CSS_TRANSFORM_ROTATE;
transform.rotate.x = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
transform.rotate.y = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
transform.rotate.z = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.rotate.angle);
}
else if (gtk_css_parser_has_function (parser, "rotate3d" ))
{
if (!gtk_css_parser_consume_function (parser, 4 , 4 , gtk_css_transform_parse_rotate3d, &transform))
{
g_clear_pointer (&transform.rotate.x, gtk_css_value_unref);
g_clear_pointer (&transform.rotate.y, gtk_css_value_unref);
g_clear_pointer (&transform.rotate.z, gtk_css_value_unref);
g_clear_pointer (&transform.rotate.angle, gtk_css_value_unref);
goto fail;
}
transform.type = GTK_CSS_TRANSFORM_ROTATE;
computed = computed && gtk_css_value_is_computed (transform.rotate.angle) &&
gtk_css_value_is_computed (transform.rotate.x) &&
gtk_css_value_is_computed (transform.rotate.y) &&
gtk_css_value_is_computed (transform.rotate.z);
}
else if (gtk_css_parser_has_function (parser, "rotateX" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_angle, &transform.rotate.angle))
goto fail;
transform.type = GTK_CSS_TRANSFORM_ROTATE;
transform.rotate.x = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform.rotate.y = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
transform.rotate.z = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.rotate.angle);
}
else if (gtk_css_parser_has_function (parser, "rotateY" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_angle, &transform.rotate.angle))
goto fail;
transform.type = GTK_CSS_TRANSFORM_ROTATE;
transform.rotate.x = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
transform.rotate.y = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform.rotate.z = gtk_css_number_value_new (0 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.rotate.angle);
}
else if (gtk_css_parser_has_function (parser, "scale" ))
{
GtkCssValue *values[2 ] = { NULL, NULL };
if (!gtk_css_parser_consume_function (parser, 1 , 2 , gtk_css_transform_parse_number, values))
{
g_clear_pointer (&values[0 ], gtk_css_value_unref);
g_clear_pointer (&values[1 ], gtk_css_value_unref);
goto fail;
}
transform.type = GTK_CSS_TRANSFORM_SCALE;
transform.scale.x = values[0 ];
if (values[1 ])
transform.scale.y = values[1 ];
else
transform.scale.y = gtk_css_value_ref (values[0 ]);
transform.scale.z = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.scale.x) &&
gtk_css_value_is_computed (transform.scale.y);
}
else if (gtk_css_parser_has_function (parser, "scale3d" ))
{
GtkCssValue *values[3 ] = { NULL, NULL };
if (!gtk_css_parser_consume_function (parser, 3 , 3 , gtk_css_transform_parse_number, values))
{
g_clear_pointer (&values[0 ], gtk_css_value_unref);
g_clear_pointer (&values[1 ], gtk_css_value_unref);
g_clear_pointer (&values[2 ], gtk_css_value_unref);
goto fail;
}
transform.type = GTK_CSS_TRANSFORM_SCALE;
transform.scale.x = values[0 ];
transform.scale.y = values[1 ];
transform.scale.z = values[2 ];
computed = computed && gtk_css_value_is_computed (transform.scale.x) &&
gtk_css_value_is_computed (transform.scale.y) &&
gtk_css_value_is_computed (transform.scale.z);
}
else if (gtk_css_parser_has_function (parser, "scaleX" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_number, &transform.scale.x))
goto fail;
transform.type = GTK_CSS_TRANSFORM_SCALE;
transform.scale.y = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform.scale.z = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.scale.x);
}
else if (gtk_css_parser_has_function (parser, "scaleY" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_number, &transform.scale.y))
goto fail;
transform.type = GTK_CSS_TRANSFORM_SCALE;
transform.scale.x = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform.scale.z = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.scale.y);
}
else if (gtk_css_parser_has_function (parser, "scaleZ" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_number, &transform.scale.z))
goto fail;
transform.type = GTK_CSS_TRANSFORM_SCALE;
transform.scale.x = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
transform.scale.y = gtk_css_number_value_new (1 , GTK_CSS_NUMBER);
computed = computed && gtk_css_value_is_computed (transform.scale.z);
}
else if (gtk_css_parser_has_function (parser, "skew" ))
{
GtkCssValue *values[2 ] = { NULL, NULL };
if (!gtk_css_parser_consume_function (parser, 2 , 2 , gtk_css_transform_parse_angle, values))
{
g_clear_pointer (&values[0 ], gtk_css_value_unref);
g_clear_pointer (&values[1 ], gtk_css_value_unref);
goto fail;
}
transform.type = GTK_CSS_TRANSFORM_SKEW;
transform.skew.x = values[0 ];
transform.skew.y = values[1 ];
computed = computed && gtk_css_value_is_computed (transform.skew.x) &&
gtk_css_value_is_computed (transform.skew.y);
}
else if (gtk_css_parser_has_function (parser, "skewX" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_angle, &transform.skew_x.skew))
goto fail;
transform.type = GTK_CSS_TRANSFORM_SKEW_X;
computed = computed && gtk_css_value_is_computed (transform.skew_x.skew);
}
else if (gtk_css_parser_has_function (parser, "skewY" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_angle, &transform.skew_y.skew))
goto fail;
transform.type = GTK_CSS_TRANSFORM_SKEW_Y;
computed = computed && gtk_css_value_is_computed (transform.skew_y.skew);
}
else if (gtk_css_parser_has_function (parser, "translate" ))
{
GtkCssValue *values[2 ] = { NULL, NULL };
if (!gtk_css_parser_consume_function (parser, 1 , 2 , gtk_css_transform_parse_length, values))
{
g_clear_pointer (&values[0 ], gtk_css_value_unref);
g_clear_pointer (&values[1 ], gtk_css_value_unref);
goto fail;
}
transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
transform.translate.x = values[0 ];
if (values[1 ])
transform.translate.y = values[1 ];
else
transform.translate.y = gtk_css_number_value_new (0 , GTK_CSS_PX);
transform.translate.z = gtk_css_number_value_new (0 , GTK_CSS_PX);
computed = computed && gtk_css_value_is_computed (transform.translate.x) &&
gtk_css_value_is_computed (transform.translate.y);
}
else if (gtk_css_parser_has_function (parser, "translate3d" ))
{
GtkCssValue *values[3 ] = { NULL, NULL };
if (!gtk_css_parser_consume_function (parser, 3 , 3 , gtk_css_transform_parse_length, values))
{
g_clear_pointer (&values[0 ], gtk_css_value_unref);
g_clear_pointer (&values[1 ], gtk_css_value_unref);
g_clear_pointer (&values[2 ], gtk_css_value_unref);
goto fail;
}
transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
transform.translate.x = values[0 ];
transform.translate.y = values[1 ];
transform.translate.z = values[2 ];
computed = computed && gtk_css_value_is_computed (transform.translate.x) &&
gtk_css_value_is_computed (transform.translate.y) &&
gtk_css_value_is_computed (transform.translate.z);
}
else if (gtk_css_parser_has_function (parser, "translateX" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_length, &transform.translate.x))
goto fail;
transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
transform.translate.y = gtk_css_number_value_new (0 , GTK_CSS_PX);
transform.translate.z = gtk_css_number_value_new (0 , GTK_CSS_PX);
computed = computed && gtk_css_value_is_computed (transform.translate.x);
}
else if (gtk_css_parser_has_function (parser, "translateY" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_length, &transform.translate.y))
goto fail;
transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
transform.translate.x = gtk_css_number_value_new (0 , GTK_CSS_PX);
transform.translate.z = gtk_css_number_value_new (0 , GTK_CSS_PX);
computed = computed && gtk_css_value_is_computed (transform.translate.y);
}
else if (gtk_css_parser_has_function (parser, "translateZ" ))
{
if (!gtk_css_parser_consume_function (parser, 1 , 1 , gtk_css_transform_parse_length, &transform.translate.z))
goto fail;
transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
transform.translate.x = gtk_css_number_value_new (0 , GTK_CSS_PX);
transform.translate.y = gtk_css_number_value_new (0 , GTK_CSS_PX);
computed = computed && gtk_css_value_is_computed (transform.translate.z);
}
else
{
break ;
}
g_array_append_val (array, transform);
}
if (array->len == 0 )
{
gtk_css_parser_error_syntax (parser, "Expected a transform" );
goto fail;
}
value = gtk_css_transform_value_alloc (array->len);
value->is_computed = computed;
memcpy (value->transforms, array->data, sizeof (GtkCssTransform) * array->len);
g_array_free (array, TRUE );
return value;
fail:
for (i = 0 ; i < array->len; i++)
{
gtk_css_transform_clear (&g_array_index (array, GtkCssTransform, i));
}
g_array_free (array, TRUE );
return NULL;
}
GskTransform *
gtk_css_transform_value_get_transform (const GtkCssValue *transform)
{
g_return_val_if_fail (transform->class == >K_CSS_VALUE_TRANSFORM, FALSE );
if (transform->n_transforms == 0 )
return NULL;
return gtk_css_transform_value_compute_transform (transform);
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland