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 | |
28 | typedef union _GtkCssTransform GtkCssTransform; |
29 | |
30 | typedef 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 | |
42 | union _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 | |
76 | struct _GtkCssValue { |
77 | GTK_CSS_VALUE_BASE |
78 | guint n_transforms; |
79 | GtkCssTransform transforms[1]; |
80 | }; |
81 | |
82 | static GtkCssValue * gtk_css_transform_value_alloc (guint n_values); |
83 | static gboolean gtk_css_transform_value_is_none (const GtkCssValue *value); |
84 | |
85 | static void |
86 | gtk_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 | |
128 | static gboolean |
129 | gtk_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 | |
177 | static GskTransform * |
178 | gtk_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 */ |
247 | static GskTransform * |
248 | gtk_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 | |
263 | static void |
264 | gtk_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 */ |
277 | static gboolean |
278 | gtk_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 | |
335 | static GtkCssValue * |
336 | gtk_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 | |
372 | static gboolean |
373 | gtk_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 | |
423 | static gboolean |
424 | gtk_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 | |
458 | static void |
459 | gtk_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 | |
490 | static void |
491 | gtk_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 | |
543 | static GtkCssValue * |
544 | gtk_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 | |
655 | static void |
656 | gtk_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 | |
773 | static void |
774 | gtk_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 | |
794 | static 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 | |
805 | static GtkCssValue transform_none_singleton = { >K_CSS_VALUE_TRANSFORM, 1, TRUE, 0, { { GTK_CSS_TRANSFORM_NONE } } }; |
806 | |
807 | static GtkCssValue * |
808 | gtk_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: >K_CSS_VALUE_TRANSFORM, size: sizeof (GtkCssValue) + sizeof (GtkCssTransform) * (n_transforms - 1)); |
815 | result->n_transforms = n_transforms; |
816 | |
817 | return result; |
818 | } |
819 | |
820 | GtkCssValue * |
821 | _gtk_css_transform_value_new_none (void) |
822 | { |
823 | return _gtk_css_value_ref (value: &transform_none_singleton); |
824 | } |
825 | |
826 | static gboolean |
827 | gtk_css_transform_value_is_none (const GtkCssValue *value) |
828 | { |
829 | return value->n_transforms == 0; |
830 | } |
831 | |
832 | static guint |
833 | gtk_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 | |
847 | static guint |
848 | gtk_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 | |
861 | static guint |
862 | gtk_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 | |
875 | static guint |
876 | gtk_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 | |
889 | static guint |
890 | gtk_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 | |
930 | GtkCssValue * |
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 | |
1223 | fail: |
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 | |
1232 | GskTransform * |
1233 | gtk_css_transform_value_get_transform (const GtkCssValue *transform) |
1234 | { |
1235 | g_return_val_if_fail (transform->class == >K_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 | |