1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gtkcsstransformvalueprivate.h"
21
22#include <math.h>
23#include <string.h>
24
25#include "gtkcssnumbervalueprivate.h"
26#include "gsktransform.h"
27
28typedef union _GtkCssTransform GtkCssTransform;
29
30typedef enum {
31 GTK_CSS_TRANSFORM_NONE,
32 GTK_CSS_TRANSFORM_MATRIX,
33 GTK_CSS_TRANSFORM_TRANSLATE,
34 GTK_CSS_TRANSFORM_ROTATE,
35 GTK_CSS_TRANSFORM_SCALE,
36 GTK_CSS_TRANSFORM_SKEW,
37 GTK_CSS_TRANSFORM_SKEW_X,
38 GTK_CSS_TRANSFORM_SKEW_Y,
39 GTK_CSS_TRANSFORM_PERSPECTIVE
40} GtkCssTransformType;
41
42union _GtkCssTransform {
43 GtkCssTransformType type;
44 struct {
45 GtkCssTransformType type;
46 graphene_matrix_t matrix;
47 } matrix;
48 struct {
49 GtkCssTransformType type;
50 GtkCssValue *x;
51 GtkCssValue *y;
52 GtkCssValue *z;
53 } translate, scale;
54 struct {
55 GtkCssTransformType type;
56 GtkCssValue *x;
57 GtkCssValue *y;
58 } skew;
59 struct {
60 GtkCssTransformType type;
61 GtkCssValue *x;
62 GtkCssValue *y;
63 GtkCssValue *z;
64 GtkCssValue *angle;
65 } rotate;
66 struct {
67 GtkCssTransformType type;
68 GtkCssValue *skew;
69 } skew_x, skew_y;
70 struct {
71 GtkCssTransformType type;
72 GtkCssValue *depth;
73 } perspective;
74};
75
76struct _GtkCssValue {
77 GTK_CSS_VALUE_BASE
78 guint n_transforms;
79 GtkCssTransform transforms[1];
80};
81
82static GtkCssValue * gtk_css_transform_value_alloc (guint n_values);
83static gboolean gtk_css_transform_value_is_none (const GtkCssValue *value);
84
85static void
86gtk_css_transform_clear (GtkCssTransform *transform)
87{
88 switch (transform->type)
89 {
90 case GTK_CSS_TRANSFORM_MATRIX:
91 break;
92 case GTK_CSS_TRANSFORM_TRANSLATE:
93 _gtk_css_value_unref (value: transform->translate.x);
94 _gtk_css_value_unref (value: transform->translate.y);
95 _gtk_css_value_unref (value: transform->translate.z);
96 break;
97 case GTK_CSS_TRANSFORM_ROTATE:
98 _gtk_css_value_unref (value: transform->rotate.x);
99 _gtk_css_value_unref (value: transform->rotate.y);
100 _gtk_css_value_unref (value: transform->rotate.z);
101 _gtk_css_value_unref (value: transform->rotate.angle);
102 break;
103 case GTK_CSS_TRANSFORM_SCALE:
104 _gtk_css_value_unref (value: transform->scale.x);
105 _gtk_css_value_unref (value: transform->scale.y);
106 _gtk_css_value_unref (value: transform->scale.z);
107 break;
108 case GTK_CSS_TRANSFORM_SKEW:
109 _gtk_css_value_unref (value: transform->skew.x);
110 _gtk_css_value_unref (value: transform->skew.y);
111 break;
112 case GTK_CSS_TRANSFORM_SKEW_X:
113 _gtk_css_value_unref (value: transform->skew_x.skew);
114 break;
115 case GTK_CSS_TRANSFORM_SKEW_Y:
116 _gtk_css_value_unref (value: transform->skew_y.skew);
117 break;
118 case GTK_CSS_TRANSFORM_PERSPECTIVE:
119 _gtk_css_value_unref (value: transform->perspective.depth);
120 break;
121 case GTK_CSS_TRANSFORM_NONE:
122 default:
123 g_assert_not_reached ();
124 break;
125 }
126}
127
128static gboolean
129gtk_css_transform_init_identity (GtkCssTransform *transform,
130 GtkCssTransformType type)
131{
132 switch (type)
133 {
134 case GTK_CSS_TRANSFORM_MATRIX:
135 graphene_matrix_init_identity (m: &transform->matrix.matrix);
136 break;
137 case GTK_CSS_TRANSFORM_TRANSLATE:
138 transform->translate.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
139 transform->translate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
140 transform->translate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
141 break;
142 case GTK_CSS_TRANSFORM_ROTATE:
143 transform->rotate.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
144 transform->rotate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
145 transform->rotate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
146 transform->rotate.angle = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_DEG);
147 break;
148 case GTK_CSS_TRANSFORM_SCALE:
149 transform->scale.x = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
150 transform->scale.y = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
151 transform->scale.z = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
152 break;
153 case GTK_CSS_TRANSFORM_SKEW:
154 transform->skew.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_DEG);
155 transform->skew.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_DEG);
156 break;
157 case GTK_CSS_TRANSFORM_SKEW_X:
158 transform->skew_x.skew = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_DEG);
159 break;
160 case GTK_CSS_TRANSFORM_SKEW_Y:
161 transform->skew_y.skew = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_DEG);
162 break;
163 case GTK_CSS_TRANSFORM_PERSPECTIVE:
164 return FALSE;
165
166 case GTK_CSS_TRANSFORM_NONE:
167 default:
168 g_assert_not_reached ();
169 return FALSE;
170 }
171
172 transform->type = type;
173
174 return TRUE;
175}
176
177static GskTransform *
178gtk_css_transform_apply (const GtkCssTransform *transform,
179 GskTransform *next)
180{
181 graphene_matrix_t skew;
182
183 switch (transform->type)
184 {
185 case GTK_CSS_TRANSFORM_MATRIX:
186 return gsk_transform_matrix (next, matrix: &transform->matrix.matrix);
187
188 case GTK_CSS_TRANSFORM_TRANSLATE:
189 return gsk_transform_translate_3d (next,
190 point: &GRAPHENE_POINT3D_INIT (
191 _gtk_css_number_value_get (transform->translate.x, 100),
192 _gtk_css_number_value_get (transform->translate.y, 100),
193 _gtk_css_number_value_get (transform->translate.z, 100)
194 ));
195
196 case GTK_CSS_TRANSFORM_ROTATE:
197 {
198 graphene_vec3_t axis;
199
200 graphene_vec3_init (v: &axis,
201 x: _gtk_css_number_value_get (number: transform->rotate.x, one_hundred_percent: 1),
202 y: _gtk_css_number_value_get (number: transform->rotate.y, one_hundred_percent: 1),
203 z: _gtk_css_number_value_get (number: transform->rotate.z, one_hundred_percent: 1));
204 return gsk_transform_rotate_3d (next,
205 angle: _gtk_css_number_value_get (number: transform->rotate.angle, one_hundred_percent: 100),
206 axis: &axis);
207 }
208
209 case GTK_CSS_TRANSFORM_SCALE:
210 return gsk_transform_scale_3d (next,
211 factor_x: _gtk_css_number_value_get (number: transform->scale.x, one_hundred_percent: 1),
212 factor_y: _gtk_css_number_value_get (number: transform->scale.y, one_hundred_percent: 1),
213 factor_z: _gtk_css_number_value_get (number: transform->scale.z, one_hundred_percent: 1));
214
215 case GTK_CSS_TRANSFORM_SKEW:
216 graphene_matrix_init_skew (m: &skew,
217 x_skew: _gtk_css_number_value_get (number: transform->skew.x, one_hundred_percent: 100) / 180.0f * G_PI,
218 y_skew: _gtk_css_number_value_get (number: transform->skew.y, one_hundred_percent: 100) / 180.0f * G_PI);
219 return gsk_transform_matrix (next, matrix: &skew);
220
221 case GTK_CSS_TRANSFORM_SKEW_X:
222 graphene_matrix_init_skew (m: &skew,
223 x_skew: _gtk_css_number_value_get (number: transform->skew_x.skew, one_hundred_percent: 100) / 180.0f * G_PI,
224 y_skew: 0);
225 return gsk_transform_matrix (next, matrix: &skew);
226
227 case GTK_CSS_TRANSFORM_SKEW_Y:
228 graphene_matrix_init_skew (m: &skew,
229 x_skew: 0,
230 y_skew: _gtk_css_number_value_get (number: transform->skew_y.skew, one_hundred_percent: 100) / 180.0f * G_PI);
231 return gsk_transform_matrix (next, matrix: &skew);
232
233 case GTK_CSS_TRANSFORM_PERSPECTIVE:
234 return gsk_transform_perspective (next,
235 depth: _gtk_css_number_value_get (number: transform->perspective.depth, one_hundred_percent: 100));
236
237 case GTK_CSS_TRANSFORM_NONE:
238 default:
239 g_assert_not_reached ();
240 break;
241 }
242
243 return NULL;
244}
245
246/* NB: The returned matrix may be invalid */
247static GskTransform *
248gtk_css_transform_value_compute_transform (const GtkCssValue *value)
249{
250 GskTransform *transform;
251 guint i;
252
253 transform = NULL;
254
255 for (i = 0; i < value->n_transforms; i++)
256 {
257 transform = gtk_css_transform_apply (transform: &value->transforms[i], next: transform);
258 }
259
260 return transform;
261}
262
263static void
264gtk_css_value_transform_free (GtkCssValue *value)
265{
266 guint i;
267
268 for (i = 0; i < value->n_transforms; i++)
269 {
270 gtk_css_transform_clear (transform: &value->transforms[i]);
271 }
272
273 g_slice_free1 (block_size: sizeof (GtkCssValue) + sizeof (GtkCssTransform) * (value->n_transforms - 1), mem_block: value);
274}
275
276/* returns TRUE if dest == src */
277static gboolean
278gtk_css_transform_compute (GtkCssTransform *dest,
279 GtkCssTransform *src,
280 guint property_id,
281 GtkStyleProvider *provider,
282 GtkCssStyle *style,
283 GtkCssStyle *parent_style)
284{
285 dest->type = src->type;
286
287 switch (src->type)
288 {
289 case GTK_CSS_TRANSFORM_MATRIX:
290 return TRUE;
291 case GTK_CSS_TRANSFORM_TRANSLATE:
292 dest->translate.x = _gtk_css_value_compute (value: src->translate.x, property_id, provider, style, parent_style);
293 dest->translate.y = _gtk_css_value_compute (value: src->translate.y, property_id, provider, style, parent_style);
294 dest->translate.z = _gtk_css_value_compute (value: src->translate.z, property_id, provider, style, parent_style);
295 return dest->translate.x == src->translate.x
296 && dest->translate.y == src->translate.y
297 && dest->translate.z == src->translate.z;
298 case GTK_CSS_TRANSFORM_ROTATE:
299 dest->rotate.x = _gtk_css_value_compute (value: src->rotate.x, property_id, provider, style, parent_style);
300 dest->rotate.y = _gtk_css_value_compute (value: src->rotate.y, property_id, provider, style, parent_style);
301 dest->rotate.z = _gtk_css_value_compute (value: src->rotate.z, property_id, provider, style, parent_style);
302 dest->rotate.angle = _gtk_css_value_compute (value: src->rotate.angle, property_id, provider, style, parent_style);
303 return dest->rotate.x == src->rotate.x
304 && dest->rotate.y == src->rotate.y
305 && dest->rotate.z == src->rotate.z
306 && dest->rotate.angle == src->rotate.angle;
307 case GTK_CSS_TRANSFORM_SCALE:
308 dest->scale.x = _gtk_css_value_compute (value: src->scale.x, property_id, provider, style, parent_style);
309 dest->scale.y = _gtk_css_value_compute (value: src->scale.y, property_id, provider, style, parent_style);
310 dest->scale.z = _gtk_css_value_compute (value: src->scale.z, property_id, provider, style, parent_style);
311 return dest->scale.x == src->scale.x
312 && dest->scale.y == src->scale.y
313 && dest->scale.z == src->scale.z;
314 case GTK_CSS_TRANSFORM_SKEW:
315 dest->skew.x = _gtk_css_value_compute (value: src->skew.x, property_id, provider, style, parent_style);
316 dest->skew.y = _gtk_css_value_compute (value: src->skew.y, property_id, provider, style, parent_style);
317 return dest->skew.x == src->skew.x
318 && dest->skew.y == src->skew.y;
319 case GTK_CSS_TRANSFORM_SKEW_X:
320 dest->skew_x.skew = _gtk_css_value_compute (value: src->skew_x.skew, property_id, provider, style, parent_style);
321 return dest->skew_x.skew == src->skew_x.skew;
322 case GTK_CSS_TRANSFORM_SKEW_Y:
323 dest->skew_y.skew = _gtk_css_value_compute (value: src->skew_y.skew, property_id, provider, style, parent_style);
324 return dest->skew_y.skew == src->skew_y.skew;
325 case GTK_CSS_TRANSFORM_PERSPECTIVE:
326 dest->perspective.depth = _gtk_css_value_compute (value: src->perspective.depth, property_id, provider, style, parent_style);
327 return dest->perspective.depth == src->perspective.depth;
328 case GTK_CSS_TRANSFORM_NONE:
329 default:
330 g_assert_not_reached ();
331 return FALSE;
332 }
333}
334
335static GtkCssValue *
336gtk_css_value_transform_compute (GtkCssValue *value,
337 guint property_id,
338 GtkStyleProvider *provider,
339 GtkCssStyle *style,
340 GtkCssStyle *parent_style)
341{
342 GtkCssValue *result;
343 gboolean changes;
344 guint i;
345
346 /* Special case the 99% case of "none" */
347 if (gtk_css_transform_value_is_none (value))
348 return _gtk_css_value_ref (value);
349
350 changes = FALSE;
351 result = gtk_css_transform_value_alloc (n_values: value->n_transforms);
352
353 for (i = 0; i < value->n_transforms; i++)
354 {
355 changes |= !gtk_css_transform_compute (dest: &result->transforms[i],
356 src: &value->transforms[i],
357 property_id,
358 provider,
359 style,
360 parent_style);
361 }
362
363 if (!changes)
364 {
365 _gtk_css_value_unref (value: result);
366 result = _gtk_css_value_ref (value);
367 }
368
369 return result;
370}
371
372static gboolean
373gtk_css_transform_equal (const GtkCssTransform *transform1,
374 const GtkCssTransform *transform2)
375{
376 if (transform1->type != transform2->type)
377 return FALSE;
378
379 switch (transform1->type)
380 {
381 case GTK_CSS_TRANSFORM_MATRIX:
382 {
383 guint i, j;
384
385 for (i = 0; i < 4; i++)
386 for (j = 0; j < 4; j++)
387 {
388 if (graphene_matrix_get_value (m: &transform1->matrix.matrix, row: i, col: j)
389 != graphene_matrix_get_value (m: &transform2->matrix.matrix, row: i, col: j))
390 return FALSE;
391 }
392 return TRUE;
393 }
394 case GTK_CSS_TRANSFORM_TRANSLATE:
395 return _gtk_css_value_equal (value1: transform1->translate.x, value2: transform2->translate.x)
396 && _gtk_css_value_equal (value1: transform1->translate.y, value2: transform2->translate.y)
397 && _gtk_css_value_equal (value1: transform1->translate.z, value2: transform2->translate.z);
398 case GTK_CSS_TRANSFORM_ROTATE:
399 return _gtk_css_value_equal (value1: transform1->rotate.x, value2: transform2->rotate.x)
400 && _gtk_css_value_equal (value1: transform1->rotate.y, value2: transform2->rotate.y)
401 && _gtk_css_value_equal (value1: transform1->rotate.z, value2: transform2->rotate.z)
402 && _gtk_css_value_equal (value1: transform1->rotate.angle, value2: transform2->rotate.angle);
403 case GTK_CSS_TRANSFORM_SCALE:
404 return _gtk_css_value_equal (value1: transform1->scale.x, value2: transform2->scale.x)
405 && _gtk_css_value_equal (value1: transform1->scale.y, value2: transform2->scale.y)
406 && _gtk_css_value_equal (value1: transform1->scale.z, value2: transform2->scale.z);
407 case GTK_CSS_TRANSFORM_SKEW:
408 return _gtk_css_value_equal (value1: transform1->skew.x, value2: transform2->skew.x)
409 && _gtk_css_value_equal (value1: transform1->skew.y, value2: transform2->skew.y);
410 case GTK_CSS_TRANSFORM_SKEW_X:
411 return _gtk_css_value_equal (value1: transform1->skew_x.skew, value2: transform2->skew_x.skew);
412 case GTK_CSS_TRANSFORM_SKEW_Y:
413 return _gtk_css_value_equal (value1: transform1->skew_y.skew, value2: transform2->skew_y.skew);
414 case GTK_CSS_TRANSFORM_PERSPECTIVE:
415 return _gtk_css_value_equal (value1: transform1->perspective.depth, value2: transform2->perspective.depth);
416 case GTK_CSS_TRANSFORM_NONE:
417 default:
418 g_assert_not_reached ();
419 return FALSE;
420 }
421}
422
423static gboolean
424gtk_css_value_transform_equal (const GtkCssValue *value1,
425 const GtkCssValue *value2)
426{
427 const GtkCssValue *larger;
428 guint i, n;
429
430 n = MIN (value1->n_transforms, value2->n_transforms);
431 for (i = 0; i < n; i++)
432 {
433 if (!gtk_css_transform_equal (transform1: &value1->transforms[i], transform2: &value2->transforms[i]))
434 return FALSE;
435 }
436
437 larger = value1->n_transforms > value2->n_transforms ? value1 : value2;
438
439 for (; i < larger->n_transforms; i++)
440 {
441 GtkCssTransform transform;
442
443 if (!gtk_css_transform_init_identity (transform: &transform, type: larger->transforms[i].type))
444 return FALSE;
445
446 if (!gtk_css_transform_equal (transform1: &larger->transforms[i], transform2: &transform))
447 {
448 gtk_css_transform_clear (transform: &transform);
449 return FALSE;
450 }
451
452 gtk_css_transform_clear (transform: &transform);
453 }
454
455 return TRUE;
456}
457
458static void
459gtk_css_transform_transition_default (GtkCssTransform *result,
460 const GtkCssTransform *start,
461 const GtkCssTransform *end,
462 guint property_id,
463 double progress)
464{
465 graphene_matrix_t start_mat, end_mat;
466 GskTransform *trans;
467
468 result->type = GTK_CSS_TRANSFORM_MATRIX;
469
470 if (start)
471 trans = gtk_css_transform_apply (transform: start, NULL);
472 else
473 trans = NULL;
474 gsk_transform_to_matrix (self: trans, out_matrix: &start_mat);
475 gsk_transform_unref (self: trans);
476
477 if (end)
478 trans = gtk_css_transform_apply (transform: end, NULL);
479 else
480 trans = NULL;
481 gsk_transform_to_matrix (self: trans, out_matrix: &end_mat);
482 gsk_transform_unref (self: trans);
483
484 graphene_matrix_interpolate (a: &start_mat,
485 b: &end_mat,
486 factor: progress,
487 res: &result->matrix.matrix);
488}
489
490static void
491gtk_css_transform_transition (GtkCssTransform *result,
492 const GtkCssTransform *start,
493 const GtkCssTransform *end,
494 guint property_id,
495 double progress)
496{
497 result->type = start->type;
498
499 switch (start->type)
500 {
501 case GTK_CSS_TRANSFORM_MATRIX:
502 graphene_matrix_interpolate (a: &start->matrix.matrix,
503 b: &end->matrix.matrix,
504 factor: progress,
505 res: &result->matrix.matrix);
506 break;
507 case GTK_CSS_TRANSFORM_TRANSLATE:
508 result->translate.x = _gtk_css_value_transition (start: start->translate.x, end: end->translate.x, property_id, progress);
509 result->translate.y = _gtk_css_value_transition (start: start->translate.y, end: end->translate.y, property_id, progress);
510 result->translate.z = _gtk_css_value_transition (start: start->translate.z, end: end->translate.z, property_id, progress);
511 break;
512 case GTK_CSS_TRANSFORM_ROTATE:
513 result->rotate.x = _gtk_css_value_transition (start: start->rotate.x, end: end->rotate.x, property_id, progress);
514 result->rotate.y = _gtk_css_value_transition (start: start->rotate.y, end: end->rotate.y, property_id, progress);
515 result->rotate.z = _gtk_css_value_transition (start: start->rotate.z, end: end->rotate.z, property_id, progress);
516 result->rotate.angle = _gtk_css_value_transition (start: start->rotate.angle, end: end->rotate.angle, property_id, progress);
517 break;
518 case GTK_CSS_TRANSFORM_SCALE:
519 result->scale.x = _gtk_css_value_transition (start: start->scale.x, end: end->scale.x, property_id, progress);
520 result->scale.y = _gtk_css_value_transition (start: start->scale.y, end: end->scale.y, property_id, progress);
521 result->scale.z = _gtk_css_value_transition (start: start->scale.z, end: end->scale.z, property_id, progress);
522 break;
523 case GTK_CSS_TRANSFORM_SKEW:
524 result->skew.x = _gtk_css_value_transition (start: start->skew.x, end: end->skew.x, property_id, progress);
525 result->skew.y = _gtk_css_value_transition (start: start->skew.y, end: end->skew.y, property_id, progress);
526 break;
527 case GTK_CSS_TRANSFORM_SKEW_X:
528 result->skew_x.skew = _gtk_css_value_transition (start: start->skew_x.skew, end: end->skew_x.skew, property_id, progress);
529 break;
530 case GTK_CSS_TRANSFORM_SKEW_Y:
531 result->skew_y.skew = _gtk_css_value_transition (start: start->skew_y.skew, end: end->skew_y.skew, property_id, progress);
532 break;
533 case GTK_CSS_TRANSFORM_PERSPECTIVE:
534 gtk_css_transform_transition_default (result, start, end, property_id, progress);
535 break;
536 case GTK_CSS_TRANSFORM_NONE:
537 default:
538 g_assert_not_reached ();
539 break;
540 }
541}
542
543static GtkCssValue *
544gtk_css_value_transform_transition (GtkCssValue *start,
545 GtkCssValue *end,
546 guint property_id,
547 double progress)
548{
549 GtkCssValue *result;
550 guint i, n;
551
552 if (gtk_css_transform_value_is_none (value: start))
553 {
554 if (gtk_css_transform_value_is_none (value: end))
555 return _gtk_css_value_ref (value: start);
556
557 n = 0;
558 }
559 else if (gtk_css_transform_value_is_none (value: end))
560 {
561 n = 0;
562 }
563 else
564 {
565 n = MIN (start->n_transforms, end->n_transforms);
566 }
567
568 /* Check transforms are compatible. If not, transition between
569 * their result matrices.
570 */
571 for (i = 0; i < n; i++)
572 {
573 if (start->transforms[i].type != end->transforms[i].type)
574 {
575 GskTransform *transform;
576 graphene_matrix_t start_matrix, end_matrix;
577
578 transform = gtk_css_transform_value_compute_transform (value: start);
579 gsk_transform_to_matrix (self: transform, out_matrix: &start_matrix);
580 gsk_transform_unref (self: transform);
581
582 transform = gtk_css_transform_value_compute_transform (value: end);
583 gsk_transform_to_matrix (self: transform, out_matrix: &end_matrix);
584 gsk_transform_unref (self: transform);
585
586 result = gtk_css_transform_value_alloc (n_values: 1);
587 result->transforms[0].type = GTK_CSS_TRANSFORM_MATRIX;
588 graphene_matrix_interpolate (a: &start_matrix, b: &end_matrix, factor: progress, res: &result->transforms[0].matrix.matrix);
589
590 return result;
591 }
592 }
593
594 result = gtk_css_transform_value_alloc (MAX (start->n_transforms, end->n_transforms));
595
596 for (i = 0; i < n; i++)
597 {
598 gtk_css_transform_transition (result: &result->transforms[i],
599 start: &start->transforms[i],
600 end: &end->transforms[i],
601 property_id,
602 progress);
603 }
604
605 for (; i < start->n_transforms; i++)
606 {
607 GtkCssTransform transform;
608
609 if (gtk_css_transform_init_identity (transform: &transform, type: start->transforms[i].type))
610 {
611 gtk_css_transform_transition (result: &result->transforms[i],
612 start: &start->transforms[i],
613 end: &transform,
614 property_id,
615 progress);
616 gtk_css_transform_clear (transform: &transform);
617 }
618 else
619 {
620 gtk_css_transform_transition_default (result: &result->transforms[i],
621 start: &start->transforms[i],
622 NULL,
623 property_id,
624 progress);
625 }
626 }
627 for (; i < end->n_transforms; i++)
628 {
629 GtkCssTransform transform;
630
631 if (gtk_css_transform_init_identity (transform: &transform, type: end->transforms[i].type))
632 {
633 gtk_css_transform_transition (result: &result->transforms[i],
634 start: &transform,
635 end: &end->transforms[i],
636 property_id,
637 progress);
638 gtk_css_transform_clear (transform: &transform);
639 }
640 else
641 {
642 gtk_css_transform_transition_default (result: &result->transforms[i],
643 NULL,
644 end: &end->transforms[i],
645 property_id,
646 progress);
647 }
648 }
649
650 g_assert (i == MAX (start->n_transforms, end->n_transforms));
651
652 return result;
653}
654
655static void
656gtk_css_transform_print (const GtkCssTransform *transform,
657 GString *string)
658{
659 char buf[G_ASCII_DTOSTR_BUF_SIZE];
660
661 switch (transform->type)
662 {
663 case GTK_CSS_TRANSFORM_MATRIX:
664 if (graphene_matrix_is_2d (m: &transform->matrix.matrix))
665 {
666 g_string_append (string, val: "matrix(");
667 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: 0, col: 0));
668 g_string_append (string, val: buf);
669 g_string_append (string, val: ", ");
670 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: 0, col: 1));
671 g_string_append (string, val: buf);
672 g_string_append (string, val: ", ");
673 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: 0, col: 2));
674 g_string_append (string, val: buf);
675 g_string_append (string, val: ", ");
676 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: 1, col: 0));
677 g_string_append (string, val: buf);
678 g_string_append (string, val: ", ");
679 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: 1, col: 1));
680 g_string_append (string, val: buf);
681 g_string_append (string, val: ", ");
682 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: 1, col: 2));
683 g_string_append (string, val: buf);
684 g_string_append (string, val: ")");
685 }
686 else
687 {
688 guint i;
689
690 g_string_append (string, val: "matrix3d(");
691 for (i = 0; i < 16; i++)
692 {
693 g_ascii_dtostr (buffer: buf, buf_len: sizeof (buf), d: graphene_matrix_get_value (m: &transform->matrix.matrix, row: i / 4, col: i % 4));
694 g_string_append (string, val: buf);
695 if (i < 15)
696 g_string_append (string, val: ", ");
697 }
698 g_string_append (string, val: ")");
699 }
700 break;
701 case GTK_CSS_TRANSFORM_TRANSLATE:
702 g_string_append (string, val: "translate3d(");
703 _gtk_css_value_print (value: transform->translate.x, string);
704 g_string_append (string, val: ", ");
705 _gtk_css_value_print (value: transform->translate.y, string);
706 g_string_append (string, val: ", ");
707 _gtk_css_value_print (value: transform->translate.z, string);
708 g_string_append (string, val: ")");
709 break;
710 case GTK_CSS_TRANSFORM_ROTATE:
711 g_string_append (string, val: "rotate3d(");
712 _gtk_css_value_print (value: transform->rotate.x, string);
713 g_string_append (string, val: ", ");
714 _gtk_css_value_print (value: transform->rotate.y, string);
715 g_string_append (string, val: ", ");
716 _gtk_css_value_print (value: transform->rotate.z, string);
717 g_string_append (string, val: ", ");
718 _gtk_css_value_print (value: transform->rotate.angle, string);
719 g_string_append (string, val: ")");
720 break;
721 case GTK_CSS_TRANSFORM_SCALE:
722 if (_gtk_css_number_value_get (number: transform->scale.z, one_hundred_percent: 100) == 1)
723 {
724 g_string_append (string, val: "scale(");
725 _gtk_css_value_print (value: transform->scale.x, string);
726 if (!_gtk_css_value_equal (value1: transform->scale.x, value2: transform->scale.y))
727 {
728 g_string_append (string, val: ", ");
729 _gtk_css_value_print (value: transform->scale.y, string);
730 }
731 g_string_append (string, val: ")");
732 }
733 else
734 {
735 g_string_append (string, val: "scale3d(");
736 _gtk_css_value_print (value: transform->scale.x, string);
737 g_string_append (string, val: ", ");
738 _gtk_css_value_print (value: transform->scale.y, string);
739 g_string_append (string, val: ", ");
740 _gtk_css_value_print (value: transform->scale.z, string);
741 g_string_append (string, val: ")");
742 }
743 break;
744 case GTK_CSS_TRANSFORM_SKEW:
745 g_string_append (string, val: "skew(");
746 _gtk_css_value_print (value: transform->skew.x, string);
747 g_string_append (string, val: ", ");
748 _gtk_css_value_print (value: transform->skew.y, string);
749 g_string_append (string, val: ")");
750 break;
751 case GTK_CSS_TRANSFORM_SKEW_X:
752 g_string_append (string, val: "skewX(");
753 _gtk_css_value_print (value: transform->skew_x.skew, string);
754 g_string_append (string, val: ")");
755 break;
756 case GTK_CSS_TRANSFORM_SKEW_Y:
757 g_string_append (string, val: "skewY(");
758 _gtk_css_value_print (value: transform->skew_y.skew, string);
759 g_string_append (string, val: ")");
760 break;
761 case GTK_CSS_TRANSFORM_PERSPECTIVE:
762 g_string_append (string, val: "perspective(");
763 _gtk_css_value_print (value: transform->perspective.depth, string);
764 g_string_append (string, val: ")");
765 break;
766 case GTK_CSS_TRANSFORM_NONE:
767 default:
768 g_assert_not_reached ();
769 break;
770 }
771}
772
773static void
774gtk_css_value_transform_print (const GtkCssValue *value,
775 GString *string)
776{
777 guint i;
778
779 if (gtk_css_transform_value_is_none (value))
780 {
781 g_string_append (string, val: "none");
782 return;
783 }
784
785 for (i = 0; i < value->n_transforms; i++)
786 {
787 if (i > 0)
788 g_string_append_c (string, ' ');
789
790 gtk_css_transform_print (transform: &value->transforms[i], string);
791 }
792}
793
794static const GtkCssValueClass GTK_CSS_VALUE_TRANSFORM = {
795 "GtkCssTransformValue",
796 gtk_css_value_transform_free,
797 gtk_css_value_transform_compute,
798 gtk_css_value_transform_equal,
799 gtk_css_value_transform_transition,
800 NULL,
801 NULL,
802 gtk_css_value_transform_print
803};
804
805static GtkCssValue transform_none_singleton = { &GTK_CSS_VALUE_TRANSFORM, 1, TRUE, 0, { { GTK_CSS_TRANSFORM_NONE } } };
806
807static GtkCssValue *
808gtk_css_transform_value_alloc (guint n_transforms)
809{
810 GtkCssValue *result;
811
812 g_return_val_if_fail (n_transforms > 0, NULL);
813
814 result = _gtk_css_value_alloc (klass: &GTK_CSS_VALUE_TRANSFORM, size: sizeof (GtkCssValue) + sizeof (GtkCssTransform) * (n_transforms - 1));
815 result->n_transforms = n_transforms;
816
817 return result;
818}
819
820GtkCssValue *
821_gtk_css_transform_value_new_none (void)
822{
823 return _gtk_css_value_ref (value: &transform_none_singleton);
824}
825
826static gboolean
827gtk_css_transform_value_is_none (const GtkCssValue *value)
828{
829 return value->n_transforms == 0;
830}
831
832static guint
833gtk_css_transform_parse_float (GtkCssParser *parser,
834 guint n,
835 gpointer data)
836{
837 float *f = data;
838 double d;
839
840 if (!gtk_css_parser_consume_number (self: parser, number: &d))
841 return 0;
842
843 f[n] = d;
844 return 1;
845}
846
847static guint
848gtk_css_transform_parse_length (GtkCssParser *parser,
849 guint n,
850 gpointer data)
851{
852 GtkCssValue **values = data;
853
854 values[n] = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_LENGTH);
855 if (values[n] == NULL)
856 return 0;
857
858 return 1;
859}
860
861static guint
862gtk_css_transform_parse_angle (GtkCssParser *parser,
863 guint n,
864 gpointer data)
865{
866 GtkCssValue **values = data;
867
868 values[n] = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_ANGLE);
869 if (values[n] == NULL)
870 return 0;
871
872 return 1;
873}
874
875static guint
876gtk_css_transform_parse_number (GtkCssParser *parser,
877 guint n,
878 gpointer data)
879{
880 GtkCssValue **values = data;
881
882 values[n] = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_NUMBER);
883 if (values[n] == NULL)
884 return 0;
885
886 return 1;
887}
888
889static guint
890gtk_css_transform_parse_rotate3d (GtkCssParser *parser,
891 guint n,
892 gpointer data)
893{
894 GtkCssTransform *transform = data;
895
896 switch (n)
897 {
898 case 0:
899 transform->rotate.x = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_NUMBER);
900 if (transform->rotate.x == NULL)
901 return 0;
902 break;
903
904 case 1:
905 transform->rotate.y = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_NUMBER);
906 if (transform->rotate.y == NULL)
907 return 0;
908 break;
909
910 case 2:
911 transform->rotate.z = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_NUMBER);
912 if (transform->rotate.z == NULL)
913 return 0;
914 break;
915
916 case 3:
917 transform->rotate.angle = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_ANGLE);
918 if (transform->rotate.angle == NULL)
919 return 0;
920 break;
921
922 default:
923 g_assert_not_reached();
924 return 0;
925 }
926
927 return 1;
928}
929
930GtkCssValue *
931_gtk_css_transform_value_parse (GtkCssParser *parser)
932{
933 GtkCssValue *value;
934 GArray *array;
935 guint i;
936 gboolean computed = TRUE;
937
938 if (gtk_css_parser_try_ident (self: parser, ident: "none"))
939 return _gtk_css_transform_value_new_none ();
940
941 array = g_array_new (FALSE, FALSE, element_size: sizeof (GtkCssTransform));
942
943 while (TRUE)
944 {
945 GtkCssTransform transform = { 0, };
946
947 if (gtk_css_parser_has_function (self: parser, name: "matrix"))
948 {
949 float f[6];
950
951 if (!gtk_css_parser_consume_function (self: parser, min_args: 6, max_args: 6, parse_func: gtk_css_transform_parse_float, data: f))
952 goto fail;
953
954 transform.type = GTK_CSS_TRANSFORM_MATRIX;
955 graphene_matrix_init_from_2d (m: &transform.matrix.matrix, xx: f[0], yx: f[1], xy: f[2], yy: f[3], x_0: f[4], y_0: f[5]);
956 }
957 else if (gtk_css_parser_has_function (self: parser, name: "matrix3d"))
958 {
959 float f[16];
960
961 if (!gtk_css_parser_consume_function (self: parser, min_args: 16, max_args: 16, parse_func: gtk_css_transform_parse_float, data: f))
962 goto fail;
963
964 transform.type = GTK_CSS_TRANSFORM_MATRIX;
965 graphene_matrix_init_from_float (m: &transform.matrix.matrix, v: f);
966 }
967 else if (gtk_css_parser_has_function (self: parser, name: "perspective"))
968 {
969 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_length, data: &transform.perspective.depth))
970 goto fail;
971
972 transform.type = GTK_CSS_TRANSFORM_PERSPECTIVE;
973 computed = computed && gtk_css_value_is_computed (value: transform.perspective.depth);
974 }
975 else if (gtk_css_parser_has_function (self: parser, name: "rotate") ||
976 gtk_css_parser_has_function (self: parser, name: "rotateZ"))
977 {
978 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_angle, data: &transform.rotate.angle))
979 goto fail;
980
981 transform.type = GTK_CSS_TRANSFORM_ROTATE;
982 transform.rotate.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
983 transform.rotate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
984 transform.rotate.z = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
985 computed = computed && gtk_css_value_is_computed (value: transform.rotate.angle);
986 }
987 else if (gtk_css_parser_has_function (self: parser, name: "rotate3d"))
988 {
989 if (!gtk_css_parser_consume_function (self: parser, min_args: 4, max_args: 4, parse_func: gtk_css_transform_parse_rotate3d, data: &transform))
990 {
991 g_clear_pointer (&transform.rotate.x, gtk_css_value_unref);
992 g_clear_pointer (&transform.rotate.y, gtk_css_value_unref);
993 g_clear_pointer (&transform.rotate.z, gtk_css_value_unref);
994 g_clear_pointer (&transform.rotate.angle, gtk_css_value_unref);
995 goto fail;
996 }
997
998 transform.type = GTK_CSS_TRANSFORM_ROTATE;
999 computed = computed && gtk_css_value_is_computed (value: transform.rotate.angle) &&
1000 gtk_css_value_is_computed (value: transform.rotate.x) &&
1001 gtk_css_value_is_computed (value: transform.rotate.y) &&
1002 gtk_css_value_is_computed (value: transform.rotate.z);
1003 }
1004 else if (gtk_css_parser_has_function (self: parser, name: "rotateX"))
1005 {
1006 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_angle, data: &transform.rotate.angle))
1007 goto fail;
1008
1009 transform.type = GTK_CSS_TRANSFORM_ROTATE;
1010 transform.rotate.x = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1011 transform.rotate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
1012 transform.rotate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
1013 computed = computed && gtk_css_value_is_computed (value: transform.rotate.angle);
1014 }
1015 else if (gtk_css_parser_has_function (self: parser, name: "rotateY"))
1016 {
1017 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_angle, data: &transform.rotate.angle))
1018 goto fail;
1019
1020 transform.type = GTK_CSS_TRANSFORM_ROTATE;
1021 transform.rotate.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
1022 transform.rotate.y = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1023 transform.rotate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_NUMBER);
1024 computed = computed && gtk_css_value_is_computed (value: transform.rotate.angle);
1025 }
1026 else if (gtk_css_parser_has_function (self: parser, name: "scale"))
1027 {
1028 GtkCssValue *values[2] = { NULL, NULL };
1029
1030 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 2, parse_func: gtk_css_transform_parse_number, data: values))
1031 {
1032 g_clear_pointer (&values[0], gtk_css_value_unref);
1033 g_clear_pointer (&values[1], gtk_css_value_unref);
1034 goto fail;
1035 }
1036
1037 transform.type = GTK_CSS_TRANSFORM_SCALE;
1038 transform.scale.x = values[0];
1039 if (values[1])
1040 transform.scale.y = values[1];
1041 else
1042 transform.scale.y = gtk_css_value_ref (value: values[0]);
1043 transform.scale.z = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1044 computed = computed && gtk_css_value_is_computed (value: transform.scale.x) &&
1045 gtk_css_value_is_computed (value: transform.scale.y);
1046 }
1047 else if (gtk_css_parser_has_function (self: parser, name: "scale3d"))
1048 {
1049 GtkCssValue *values[3] = { NULL, NULL };
1050
1051 if (!gtk_css_parser_consume_function (self: parser, min_args: 3, max_args: 3, parse_func: gtk_css_transform_parse_number, data: values))
1052 {
1053 g_clear_pointer (&values[0], gtk_css_value_unref);
1054 g_clear_pointer (&values[1], gtk_css_value_unref);
1055 g_clear_pointer (&values[2], gtk_css_value_unref);
1056 goto fail;
1057 }
1058
1059 transform.type = GTK_CSS_TRANSFORM_SCALE;
1060 transform.scale.x = values[0];
1061 transform.scale.y = values[1];
1062 transform.scale.z = values[2];
1063 computed = computed && gtk_css_value_is_computed (value: transform.scale.x) &&
1064 gtk_css_value_is_computed (value: transform.scale.y) &&
1065 gtk_css_value_is_computed (value: transform.scale.z);
1066 }
1067 else if (gtk_css_parser_has_function (self: parser, name: "scaleX"))
1068 {
1069 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_number, data: &transform.scale.x))
1070 goto fail;
1071
1072 transform.type = GTK_CSS_TRANSFORM_SCALE;
1073 transform.scale.y = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1074 transform.scale.z = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1075 computed = computed && gtk_css_value_is_computed (value: transform.scale.x);
1076 }
1077 else if (gtk_css_parser_has_function (self: parser, name: "scaleY"))
1078 {
1079 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_number, data: &transform.scale.y))
1080 goto fail;
1081
1082 transform.type = GTK_CSS_TRANSFORM_SCALE;
1083 transform.scale.x = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1084 transform.scale.z = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1085 computed = computed && gtk_css_value_is_computed (value: transform.scale.y);
1086 }
1087 else if (gtk_css_parser_has_function (self: parser, name: "scaleZ"))
1088 {
1089 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_number, data: &transform.scale.z))
1090 goto fail;
1091
1092 transform.type = GTK_CSS_TRANSFORM_SCALE;
1093 transform.scale.x = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1094 transform.scale.y = _gtk_css_number_value_new (value: 1, unit: GTK_CSS_NUMBER);
1095 computed = computed && gtk_css_value_is_computed (value: transform.scale.z);
1096 }
1097 else if (gtk_css_parser_has_function (self: parser, name: "skew"))
1098 {
1099 GtkCssValue *values[2] = { NULL, NULL };
1100
1101 if (!gtk_css_parser_consume_function (self: parser, min_args: 2, max_args: 2, parse_func: gtk_css_transform_parse_angle, data: values))
1102 {
1103 g_clear_pointer (&values[0], gtk_css_value_unref);
1104 g_clear_pointer (&values[1], gtk_css_value_unref);
1105 goto fail;
1106 }
1107
1108 transform.type = GTK_CSS_TRANSFORM_SKEW;
1109 transform.skew.x = values[0];
1110 transform.skew.y = values[1];
1111 computed = computed && gtk_css_value_is_computed (value: transform.skew.x) &&
1112 gtk_css_value_is_computed (value: transform.skew.y);
1113 }
1114 else if (gtk_css_parser_has_function (self: parser, name: "skewX"))
1115 {
1116 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_angle, data: &transform.skew_x.skew))
1117 goto fail;
1118
1119 transform.type = GTK_CSS_TRANSFORM_SKEW_X;
1120 computed = computed && gtk_css_value_is_computed (value: transform.skew_x.skew);
1121 }
1122 else if (gtk_css_parser_has_function (self: parser, name: "skewY"))
1123 {
1124 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_angle, data: &transform.skew_y.skew))
1125 goto fail;
1126
1127 transform.type = GTK_CSS_TRANSFORM_SKEW_Y;
1128 computed = computed && gtk_css_value_is_computed (value: transform.skew_y.skew);
1129 }
1130 else if (gtk_css_parser_has_function (self: parser, name: "translate"))
1131 {
1132 GtkCssValue *values[2] = { NULL, NULL };
1133
1134 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 2, parse_func: gtk_css_transform_parse_length, data: values))
1135 {
1136 g_clear_pointer (&values[0], gtk_css_value_unref);
1137 g_clear_pointer (&values[1], gtk_css_value_unref);
1138 goto fail;
1139 }
1140
1141 transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
1142 transform.translate.x = values[0];
1143 if (values[1])
1144 transform.translate.y = values[1];
1145 else
1146 transform.translate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1147 transform.translate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1148 computed = computed && gtk_css_value_is_computed (value: transform.translate.x) &&
1149 gtk_css_value_is_computed (value: transform.translate.y);
1150 }
1151 else if (gtk_css_parser_has_function (self: parser, name: "translate3d"))
1152 {
1153 GtkCssValue *values[3] = { NULL, NULL };
1154
1155 if (!gtk_css_parser_consume_function (self: parser, min_args: 3, max_args: 3, parse_func: gtk_css_transform_parse_length, data: values))
1156 {
1157 g_clear_pointer (&values[0], gtk_css_value_unref);
1158 g_clear_pointer (&values[1], gtk_css_value_unref);
1159 g_clear_pointer (&values[2], gtk_css_value_unref);
1160 goto fail;
1161 }
1162
1163 transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
1164 transform.translate.x = values[0];
1165 transform.translate.y = values[1];
1166 transform.translate.z = values[2];
1167 computed = computed && gtk_css_value_is_computed (value: transform.translate.x) &&
1168 gtk_css_value_is_computed (value: transform.translate.y) &&
1169 gtk_css_value_is_computed (value: transform.translate.z);
1170 }
1171 else if (gtk_css_parser_has_function (self: parser, name: "translateX"))
1172 {
1173 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_length, data: &transform.translate.x))
1174 goto fail;
1175
1176 transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
1177 transform.translate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1178 transform.translate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1179 computed = computed && gtk_css_value_is_computed (value: transform.translate.x);
1180 }
1181 else if (gtk_css_parser_has_function (self: parser, name: "translateY"))
1182 {
1183 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_length, data: &transform.translate.y))
1184 goto fail;
1185
1186 transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
1187 transform.translate.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1188 transform.translate.z = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1189 computed = computed && gtk_css_value_is_computed (value: transform.translate.y);
1190 }
1191 else if (gtk_css_parser_has_function (self: parser, name: "translateZ"))
1192 {
1193 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_transform_parse_length, data: &transform.translate.z))
1194 goto fail;
1195
1196 transform.type = GTK_CSS_TRANSFORM_TRANSLATE;
1197 transform.translate.x = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1198 transform.translate.y = _gtk_css_number_value_new (value: 0, unit: GTK_CSS_PX);
1199 computed = computed && gtk_css_value_is_computed (value: transform.translate.z);
1200 }
1201 else
1202 {
1203 break;
1204 }
1205
1206 g_array_append_val (array, transform);
1207 }
1208
1209 if (array->len == 0)
1210 {
1211 gtk_css_parser_error_syntax (self: parser, format: "Expected a transform");
1212 goto fail;
1213 }
1214
1215 value = gtk_css_transform_value_alloc (n_transforms: array->len);
1216 value->is_computed = computed;
1217 memcpy (dest: value->transforms, src: array->data, n: sizeof (GtkCssTransform) * array->len);
1218
1219 g_array_free (array, TRUE);
1220
1221 return value;
1222
1223fail:
1224 for (i = 0; i < array->len; i++)
1225 {
1226 gtk_css_transform_clear (transform: &g_array_index (array, GtkCssTransform, i));
1227 }
1228 g_array_free (array, TRUE);
1229 return NULL;
1230}
1231
1232GskTransform *
1233gtk_css_transform_value_get_transform (const GtkCssValue *transform)
1234{
1235 g_return_val_if_fail (transform->class == &GTK_CSS_VALUE_TRANSFORM, FALSE);
1236
1237 if (transform->n_transforms == 0)
1238 return NULL;
1239
1240 return gtk_css_transform_value_compute_transform (value: transform);
1241}
1242
1243

source code of gtk/gtk/gtkcsstransformvalue.c