1 /*
2  * Copyright © 2019 Benjamin Otte
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.1 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  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19 
20 
21 /**
22  * GskTransform: (ref-func gsk_transform_ref) (unref-func gsk_transform_unref)
23  *
24  * `GskTransform` is an object to describe transform matrices.
25  *
26  * Unlike `graphene_matrix_t`, `GskTransform` retains the steps in how
27  * a transform was constructed, and allows inspecting them. It is modeled
28  * after the way CSS describes transforms.
29  *
30  * `GskTransform` objects are immutable and cannot be changed after creation.
31  * This means code can safely expose them as properties of objects without
32  * having to worry about others changing them.
33  */
34 
35 #include "config.h"
36 
37 #include "gsktransformprivate.h"
38 
39 struct _GskTransformClass
40 {
41   gsize struct_size;
42   const char *type_name;
43 
44   void                  (* finalize)            (GskTransform           *transform);
45   void                  (* to_matrix)           (GskTransform           *transform,
46                                                  graphene_matrix_t      *out_matrix);
47   void                  (* apply_2d)            (GskTransform           *transform,
48                                                  float                  *out_xx,
49                                                  float                  *out_yx,
50                                                  float                  *out_xy,
51                                                  float                  *out_yy,
52                                                  float                  *out_dx,
53                                                  float                  *out_dy);
54   void                  (* apply_affine)        (GskTransform           *transform,
55                                                  float                  *out_scale_x,
56                                                  float                  *out_scale_y,
57                                                  float                  *out_dx,
58                                                  float                  *out_dy);
59   void                  (* apply_translate)     (GskTransform           *transform,
60                                                  float                  *out_dx,
61                                                  float                  *out_dy);
62   void                  (* print)               (GskTransform           *transform,
63                                                  GString                *string);
64   GskTransform *        (* apply)               (GskTransform           *transform,
65                                                  GskTransform           *apply_to);
66   GskTransform *        (* invert)              (GskTransform           *transform,
67                                                  GskTransform           *next);
68   /* both matrices have the same type */
69   gboolean              (* equal)               (GskTransform           *first_transform,
70                                                  GskTransform           *second_transform);
71 };
72 
73 
74 G_DEFINE_BOXED_TYPE (GskTransform, gsk_transform,
75                      gsk_transform_ref,
76                      gsk_transform_unref)
77 
78 static gboolean
79 gsk_transform_is_identity (GskTransform *self);
80 static GskTransform *
81 gsk_transform_matrix_with_category (GskTransform           *next,
82                                     const graphene_matrix_t*matrix,
83                                     GskTransformCategory    category);
84 
85 static inline gboolean
gsk_transform_has_class(GskTransform * self,const GskTransformClass * transform_class)86 gsk_transform_has_class (GskTransform            *self,
87                          const GskTransformClass *transform_class)
88 {
89   return self != NULL && self->transform_class == transform_class;
90 }
91 
92 /*< private >
93  * gsk_transform_alloc:
94  * @transform_class: class structure for this self
95  * @category: The category of this transform. Will be used to initialize
96  *   the result's category together with &next's category
97  * @next: (transfer full) (nullable): Next transform to multiply with
98  *
99  * Returns: (transfer full): the newly created `GskTransform`
100  */
101 static gpointer
gsk_transform_alloc(const GskTransformClass * transform_class,GskTransformCategory category,GskTransform * next)102 gsk_transform_alloc (const GskTransformClass *transform_class,
103                      GskTransformCategory     category,
104                      GskTransform            *next)
105 {
106   GskTransform *self;
107 
108   g_return_val_if_fail (transform_class != NULL, NULL);
109 
110   self = g_atomic_rc_box_alloc0 (transform_class->struct_size);
111 
112   self->transform_class = transform_class;
113   self->category = next ? MIN (category, next->category) : category;
114   if (gsk_transform_is_identity (next))
115     gsk_transform_unref (next);
116   else
117     self->next = next;
118 
119   return self;
120 }
121 
122 /*** IDENTITY ***/
123 
124 static void
gsk_identity_transform_finalize(GskTransform * transform)125 gsk_identity_transform_finalize (GskTransform *transform)
126 {
127 }
128 
129 static void
gsk_identity_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)130 gsk_identity_transform_to_matrix (GskTransform      *transform,
131                                   graphene_matrix_t *out_matrix)
132 {
133   graphene_matrix_init_identity (out_matrix);
134 }
135 
136 static void
gsk_identity_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)137 gsk_identity_transform_apply_2d (GskTransform *transform,
138                                  float        *out_xx,
139                                  float        *out_yx,
140                                  float        *out_xy,
141                                  float        *out_yy,
142                                  float        *out_dx,
143                                  float        *out_dy)
144 {
145 }
146 
147 static void
gsk_identity_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)148 gsk_identity_transform_apply_affine (GskTransform *transform,
149                                      float        *out_scale_x,
150                                      float        *out_scale_y,
151                                      float        *out_dx,
152                                      float        *out_dy)
153 {
154 }
155 
156 static void
gsk_identity_transform_apply_translate(GskTransform * transform,float * out_dx,float * out_dy)157 gsk_identity_transform_apply_translate (GskTransform *transform,
158                                         float        *out_dx,
159                                         float        *out_dy)
160 {
161 }
162 
163 static void
gsk_identity_transform_print(GskTransform * transform,GString * string)164 gsk_identity_transform_print (GskTransform *transform,
165                               GString      *string)
166 {
167   g_string_append (string, "none");
168 }
169 
170 static GskTransform *
gsk_identity_transform_apply(GskTransform * transform,GskTransform * apply_to)171 gsk_identity_transform_apply (GskTransform *transform,
172                               GskTransform *apply_to)
173 {
174   /* We do the following to make sure inverting a non-NULL transform
175    * will return a non-NULL transform.
176    */
177   if (apply_to)
178     return apply_to;
179   else
180     return gsk_transform_new ();
181 }
182 
183 static GskTransform *
gsk_identity_transform_invert(GskTransform * transform,GskTransform * next)184 gsk_identity_transform_invert (GskTransform *transform,
185                                GskTransform *next)
186 {
187   /* We do the following to make sure inverting a non-NULL transform
188    * will return a non-NULL transform.
189    */
190   if (next)
191     return next;
192   else
193     return gsk_transform_new ();
194 }
195 
196 static gboolean
gsk_identity_transform_equal(GskTransform * first_transform,GskTransform * second_transform)197 gsk_identity_transform_equal (GskTransform *first_transform,
198                               GskTransform *second_transform)
199 {
200   return TRUE;
201 }
202 
203 static const GskTransformClass GSK_IDENTITY_TRANSFORM_CLASS =
204 {
205   sizeof (GskTransform),
206   "GskIdentityTransform",
207   gsk_identity_transform_finalize,
208   gsk_identity_transform_to_matrix,
209   gsk_identity_transform_apply_2d,
210   gsk_identity_transform_apply_affine,
211   gsk_identity_transform_apply_translate,
212   gsk_identity_transform_print,
213   gsk_identity_transform_apply,
214   gsk_identity_transform_invert,
215   gsk_identity_transform_equal,
216 };
217 
218 /*<private>
219  * gsk_transform_is_identity:
220  * @transform: (nullable): A transform
221  *
222  * Checks if the transform is a representation of the identity
223  * transform.
224  *
225  * This is different from a transform like `scale(2) scale(0.5)`
226  * which just results in an identity transform when simplified.
227  *
228  * Returns: %TRUE  if this transform is a representation of
229  *   the identity transform
230  **/
231 static gboolean
gsk_transform_is_identity(GskTransform * self)232 gsk_transform_is_identity (GskTransform *self)
233 {
234   return self == NULL ||
235          (self->transform_class == &GSK_IDENTITY_TRANSFORM_CLASS && gsk_transform_is_identity (self->next));
236 }
237 
238 /*** MATRIX ***/
239 
240 typedef struct _GskMatrixTransform GskMatrixTransform;
241 
242 struct _GskMatrixTransform
243 {
244   GskTransform parent;
245 
246   graphene_matrix_t matrix;
247 };
248 
249 static void
gsk_matrix_transform_finalize(GskTransform * self)250 gsk_matrix_transform_finalize (GskTransform *self)
251 {
252 }
253 
254 static void
gsk_matrix_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)255 gsk_matrix_transform_to_matrix (GskTransform      *transform,
256                                 graphene_matrix_t *out_matrix)
257 {
258   GskMatrixTransform *self = (GskMatrixTransform *) transform;
259 
260   graphene_matrix_init_from_matrix (out_matrix, &self->matrix);
261 }
262 
263 static void
gsk_matrix_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)264 gsk_matrix_transform_apply_2d (GskTransform *transform,
265                                float        *out_xx,
266                                float        *out_yx,
267                                float        *out_xy,
268                                float        *out_yy,
269                                float        *out_dx,
270                                float        *out_dy)
271 {
272   GskMatrixTransform *self = (GskMatrixTransform *) transform;
273   graphene_matrix_t mat;
274 
275   graphene_matrix_init_from_2d (&mat,
276                                 *out_xx, *out_yx,
277                                 *out_xy, *out_yy,
278                                 *out_dx, *out_dy);
279   graphene_matrix_multiply (&self->matrix, &mat, &mat);
280 
281   /* not using graphene_matrix_to_2d() because it may
282    * fail the is_2d() check due to improper rounding */
283   *out_xx = graphene_matrix_get_value (&mat, 0, 0);
284   *out_yx = graphene_matrix_get_value (&mat, 0, 1);
285   *out_xy = graphene_matrix_get_value (&mat, 1, 0);
286   *out_yy = graphene_matrix_get_value (&mat, 1, 1);
287   *out_dx = graphene_matrix_get_value (&mat, 3, 0);
288   *out_dy = graphene_matrix_get_value (&mat, 3, 1);
289 }
290 
291 static void
gsk_matrix_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)292 gsk_matrix_transform_apply_affine (GskTransform *transform,
293                                    float        *out_scale_x,
294                                    float        *out_scale_y,
295                                    float        *out_dx,
296                                    float        *out_dy)
297 {
298   GskMatrixTransform *self = (GskMatrixTransform *) transform;
299 
300   switch (transform->category)
301   {
302     case GSK_TRANSFORM_CATEGORY_UNKNOWN:
303     case GSK_TRANSFORM_CATEGORY_ANY:
304     case GSK_TRANSFORM_CATEGORY_3D:
305     case GSK_TRANSFORM_CATEGORY_2D:
306     default:
307       g_assert_not_reached ();
308       break;
309 
310     case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
311       *out_dx += *out_scale_x * graphene_matrix_get_x_translation (&self->matrix);
312       *out_dy += *out_scale_y * graphene_matrix_get_y_translation (&self->matrix);
313       *out_scale_x *= graphene_matrix_get_x_scale (&self->matrix);
314       *out_scale_y *= graphene_matrix_get_y_scale (&self->matrix);
315       break;
316 
317     case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
318       *out_dx += *out_scale_x * graphene_matrix_get_x_translation (&self->matrix);
319       *out_dy += *out_scale_y * graphene_matrix_get_y_translation (&self->matrix);
320       break;
321 
322     case GSK_TRANSFORM_CATEGORY_IDENTITY:
323       break;
324   }
325 }
326 
327 static void
gsk_matrix_transform_apply_translate(GskTransform * transform,float * out_dx,float * out_dy)328 gsk_matrix_transform_apply_translate (GskTransform *transform,
329                                       float        *out_dx,
330                                       float        *out_dy)
331 {
332   GskMatrixTransform *self = (GskMatrixTransform *) transform;
333 
334   switch (transform->category)
335   {
336     case GSK_TRANSFORM_CATEGORY_UNKNOWN:
337     case GSK_TRANSFORM_CATEGORY_ANY:
338     case GSK_TRANSFORM_CATEGORY_3D:
339     case GSK_TRANSFORM_CATEGORY_2D:
340     case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
341     default:
342       g_assert_not_reached ();
343       break;
344 
345     case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
346       *out_dx += graphene_matrix_get_x_translation (&self->matrix);
347       *out_dy += graphene_matrix_get_y_translation (&self->matrix);
348       break;
349 
350     case GSK_TRANSFORM_CATEGORY_IDENTITY:
351       break;
352   }
353 }
354 
355 static void
string_append_double(GString * string,double d)356 string_append_double (GString *string,
357                       double   d)
358 {
359   char buf[G_ASCII_DTOSTR_BUF_SIZE];
360 
361   g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%g", d);
362   g_string_append (string, buf);
363 }
364 
365 static void
gsk_matrix_transform_print(GskTransform * transform,GString * string)366 gsk_matrix_transform_print (GskTransform *transform,
367                             GString      *string)
368 {
369   GskMatrixTransform *self = (GskMatrixTransform *) transform;
370   guint i;
371   float f[16];
372 
373   g_string_append (string, "matrix3d(");
374   graphene_matrix_to_float (&self->matrix, f);
375   for (i = 0; i < 16; i++)
376     {
377       if (i > 0)
378         g_string_append (string, ", ");
379       string_append_double (string, f[i]);
380     }
381   g_string_append (string, ")");
382 }
383 
384 static GskTransform *
gsk_matrix_transform_apply(GskTransform * transform,GskTransform * apply_to)385 gsk_matrix_transform_apply (GskTransform *transform,
386                             GskTransform *apply_to)
387 {
388   GskMatrixTransform *self = (GskMatrixTransform *) transform;
389 
390   return gsk_transform_matrix_with_category (apply_to,
391                                              &self->matrix,
392                                              transform->category);
393 }
394 
395 static GskTransform *
gsk_matrix_transform_invert(GskTransform * transform,GskTransform * next)396 gsk_matrix_transform_invert (GskTransform *transform,
397                              GskTransform *next)
398 {
399   GskMatrixTransform *self = (GskMatrixTransform *) transform;
400   graphene_matrix_t inverse;
401 
402   if (!graphene_matrix_inverse (&self->matrix, &inverse))
403     {
404       gsk_transform_unref (next);
405       return NULL;
406     }
407 
408   return gsk_transform_matrix_with_category (next,
409                                              &inverse,
410                                              transform->category);
411 }
412 
413 static gboolean
gsk_matrix_transform_equal(GskTransform * first_transform,GskTransform * second_transform)414 gsk_matrix_transform_equal (GskTransform *first_transform,
415                             GskTransform *second_transform)
416 {
417   GskMatrixTransform *first = (GskMatrixTransform *) first_transform;
418   GskMatrixTransform *second = (GskMatrixTransform *) second_transform;
419 
420   if (graphene_matrix_equal_fast (&first->matrix, &second->matrix))
421     return TRUE;
422 
423   return graphene_matrix_equal (&first->matrix, &second->matrix);
424 }
425 
426 static const GskTransformClass GSK_TRANSFORM_TRANSFORM_CLASS =
427 {
428   sizeof (GskMatrixTransform),
429   "GskMatrixTransform",
430   gsk_matrix_transform_finalize,
431   gsk_matrix_transform_to_matrix,
432   gsk_matrix_transform_apply_2d,
433   gsk_matrix_transform_apply_affine,
434   gsk_matrix_transform_apply_translate,
435   gsk_matrix_transform_print,
436   gsk_matrix_transform_apply,
437   gsk_matrix_transform_invert,
438   gsk_matrix_transform_equal,
439 };
440 
441 static GskTransform *
gsk_transform_matrix_with_category(GskTransform * next,const graphene_matrix_t * matrix,GskTransformCategory category)442 gsk_transform_matrix_with_category (GskTransform            *next,
443                                     const graphene_matrix_t *matrix,
444                                     GskTransformCategory     category)
445 {
446   GskMatrixTransform *result = gsk_transform_alloc (&GSK_TRANSFORM_TRANSFORM_CLASS, category, next);
447 
448   graphene_matrix_init_from_matrix (&result->matrix, matrix);
449 
450   return &result->parent;
451 }
452 
453 /**
454  * gsk_transform_matrix:
455  * @next: (nullable) (transfer full): the next transform
456  * @matrix: the matrix to multiply @next with
457  *
458  * Multiplies @next with the given @matrix.
459  *
460  * Returns: The new transform
461  **/
462 GskTransform *
gsk_transform_matrix(GskTransform * next,const graphene_matrix_t * matrix)463 gsk_transform_matrix (GskTransform            *next,
464                       const graphene_matrix_t *matrix)
465 {
466   return gsk_transform_matrix_with_category (next, matrix, GSK_TRANSFORM_CATEGORY_UNKNOWN);
467 }
468 
469 /*** TRANSLATE ***/
470 
471 typedef struct _GskTranslateTransform GskTranslateTransform;
472 
473 struct _GskTranslateTransform
474 {
475   GskTransform parent;
476 
477   graphene_point3d_t point;
478 };
479 
480 static void
gsk_translate_transform_finalize(GskTransform * self)481 gsk_translate_transform_finalize (GskTransform *self)
482 {
483 }
484 
485 static void
gsk_translate_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)486 gsk_translate_transform_to_matrix (GskTransform      *transform,
487                                    graphene_matrix_t *out_matrix)
488 {
489   GskTranslateTransform *self = (GskTranslateTransform *) transform;
490 
491   graphene_matrix_init_translate (out_matrix, &self->point);
492 }
493 
494 static void
gsk_translate_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)495 gsk_translate_transform_apply_2d (GskTransform *transform,
496                                   float        *out_xx,
497                                   float        *out_yx,
498                                   float        *out_xy,
499                                   float        *out_yy,
500                                   float        *out_dx,
501                                   float        *out_dy)
502 {
503   GskTranslateTransform *self = (GskTranslateTransform *) transform;
504 
505   g_assert (self->point.z == 0.0);
506 
507   *out_dx += *out_xx * self->point.x + *out_xy * self->point.y;
508   *out_dy += *out_yx * self->point.x + *out_yy * self->point.y;
509 }
510 
511 static void
gsk_translate_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)512 gsk_translate_transform_apply_affine (GskTransform *transform,
513                                       float        *out_scale_x,
514                                       float        *out_scale_y,
515                                       float        *out_dx,
516                                       float        *out_dy)
517 {
518   GskTranslateTransform *self = (GskTranslateTransform *) transform;
519 
520   g_assert (self->point.z == 0.0);
521 
522   *out_dx += *out_scale_x * self->point.x;
523   *out_dy += *out_scale_y * self->point.y;
524 }
525 
526 static void
gsk_translate_transform_apply_translate(GskTransform * transform,float * out_dx,float * out_dy)527 gsk_translate_transform_apply_translate (GskTransform *transform,
528                                          float        *out_dx,
529                                          float        *out_dy)
530 {
531   GskTranslateTransform *self = (GskTranslateTransform *) transform;
532 
533   g_assert (self->point.z == 0.0);
534 
535   *out_dx += self->point.x;
536   *out_dy += self->point.y;
537 }
538 
539 static GskTransform *
gsk_translate_transform_apply(GskTransform * transform,GskTransform * apply_to)540 gsk_translate_transform_apply (GskTransform *transform,
541                                GskTransform *apply_to)
542 {
543   GskTranslateTransform *self = (GskTranslateTransform *) transform;
544 
545   return gsk_transform_translate_3d (apply_to, &self->point);
546 }
547 
548 static GskTransform *
gsk_translate_transform_invert(GskTransform * transform,GskTransform * next)549 gsk_translate_transform_invert (GskTransform *transform,
550                                 GskTransform *next)
551 {
552   GskTranslateTransform *self = (GskTranslateTransform *) transform;
553 
554   return gsk_transform_translate_3d (next, &GRAPHENE_POINT3D_INIT (-self->point.x, -self->point.y, -self->point.z));
555 }
556 
557 static gboolean
gsk_translate_transform_equal(GskTransform * first_transform,GskTransform * second_transform)558 gsk_translate_transform_equal (GskTransform *first_transform,
559                                GskTransform *second_transform)
560 {
561   GskTranslateTransform *first = (GskTranslateTransform *) first_transform;
562   GskTranslateTransform *second = (GskTranslateTransform *) second_transform;
563 
564   return G_APPROX_VALUE (first->point.x, second->point.x, FLT_EPSILON) &&
565          G_APPROX_VALUE (first->point.y, second->point.y, FLT_EPSILON) &&
566          G_APPROX_VALUE (first->point.z, second->point.z, FLT_EPSILON);
567 }
568 
569 static void
gsk_translate_transform_print(GskTransform * transform,GString * string)570 gsk_translate_transform_print (GskTransform *transform,
571                                GString      *string)
572 {
573   GskTranslateTransform *self = (GskTranslateTransform *) transform;
574 
575   if (self->point.z == 0)
576     g_string_append (string, "translate(");
577   else
578     g_string_append (string, "translate3d(");
579 
580   string_append_double (string, self->point.x);
581   g_string_append (string, ", ");
582   string_append_double (string, self->point.y);
583   if (self->point.z != 0)
584     {
585       g_string_append (string, ", ");
586       string_append_double (string, self->point.z);
587     }
588   g_string_append (string, ")");
589 }
590 
591 static const GskTransformClass GSK_TRANSLATE_TRANSFORM_CLASS =
592 {
593   sizeof (GskTranslateTransform),
594   "GskTranslateTransform",
595   gsk_translate_transform_finalize,
596   gsk_translate_transform_to_matrix,
597   gsk_translate_transform_apply_2d,
598   gsk_translate_transform_apply_affine,
599   gsk_translate_transform_apply_translate,
600   gsk_translate_transform_print,
601   gsk_translate_transform_apply,
602   gsk_translate_transform_invert,
603   gsk_translate_transform_equal,
604 };
605 
606 /**
607  * gsk_transform_translate:
608  * @next: (nullable) (transfer full): the next transform
609  * @point: the point to translate the transform by
610  *
611  * Translates @next in 2-dimensional space by @point.
612  *
613  * Returns: The new transform
614  **/
615 GskTransform *
gsk_transform_translate(GskTransform * next,const graphene_point_t * point)616 gsk_transform_translate (GskTransform           *next,
617                          const graphene_point_t *point)
618 {
619   graphene_point3d_t point3d;
620 
621   graphene_point3d_init (&point3d, point->x, point->y, 0);
622 
623   return gsk_transform_translate_3d (next, &point3d);
624 }
625 
626 /**
627  * gsk_transform_translate_3d:
628  * @next: (nullable) (transfer full): the next transform
629  * @point: the point to translate the transform by
630  *
631  * Translates @next by @point.
632  *
633  * Returns: The new transform
634  **/
635 GskTransform *
gsk_transform_translate_3d(GskTransform * next,const graphene_point3d_t * point)636 gsk_transform_translate_3d (GskTransform             *next,
637                             const graphene_point3d_t *point)
638 {
639   GskTranslateTransform *result;
640 
641   if (graphene_point3d_equal (point, graphene_point3d_zero ()))
642     return next;
643 
644   if (gsk_transform_has_class (next, &GSK_TRANSLATE_TRANSFORM_CLASS))
645     {
646       GskTranslateTransform *t = (GskTranslateTransform *) next;
647       GskTransform *r = gsk_transform_translate_3d (gsk_transform_ref (next->next),
648                                                     &GRAPHENE_POINT3D_INIT(t->point.x + point->x,
649                                                                            t->point.y + point->y,
650                                                                            t->point.z + point->z));
651       gsk_transform_unref (next);
652       return r;
653     }
654 
655   result = gsk_transform_alloc (&GSK_TRANSLATE_TRANSFORM_CLASS,
656                                 point->z == 0.0 ? GSK_TRANSFORM_CATEGORY_2D_TRANSLATE
657                                                 : GSK_TRANSFORM_CATEGORY_3D,
658                                 next);
659 
660   graphene_point3d_init_from_point (&result->point, point);
661 
662   return &result->parent;
663 }
664 
665 /*** ROTATE ***/
666 
667 typedef struct _GskRotateTransform GskRotateTransform;
668 
669 struct _GskRotateTransform
670 {
671   GskTransform parent;
672 
673   float angle;
674 };
675 
676 static void
gsk_rotate_transform_finalize(GskTransform * self)677 gsk_rotate_transform_finalize (GskTransform *self)
678 {
679 }
680 
681 static inline void
_sincos(float deg,float * out_s,float * out_c)682 _sincos (float  deg,
683          float *out_s,
684          float *out_c)
685 {
686   if (deg == 90.0)
687     {
688       *out_c = 0.0;
689       *out_s = 1.0;
690     }
691   else if (deg == 180.0)
692     {
693       *out_c = -1.0;
694       *out_s = 0.0;
695     }
696   else if (deg == 270.0)
697     {
698       *out_c = 0.0;
699       *out_s = -1.0;
700     }
701   else if (deg == 0.0)
702     {
703       *out_c = 1.0;
704       *out_s = 0.0;
705     }
706   else
707     {
708       float angle = deg * M_PI / 180.0;
709 
710 #ifdef HAVE_SINCOSF
711       sincosf (angle, out_s, out_c);
712 #else
713       *out_s = sinf (angle);
714       *out_c = cosf (angle);
715 #endif
716 
717     }
718 }
719 
720 static void
gsk_rotate_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)721 gsk_rotate_transform_to_matrix (GskTransform      *transform,
722                                 graphene_matrix_t *out_matrix)
723 {
724   GskRotateTransform *self = (GskRotateTransform *) transform;
725   float c, s;
726 
727   _sincos (self->angle, &s, &c);
728 
729   graphene_matrix_init_from_2d (out_matrix,
730                                 c, s,
731                                 -s, c,
732                                 0, 0);
733 }
734 
735 static void
gsk_rotate_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)736 gsk_rotate_transform_apply_2d (GskTransform *transform,
737                                float        *out_xx,
738                                float        *out_yx,
739                                float        *out_xy,
740                                float        *out_yy,
741                                float        *out_dx,
742                                float        *out_dy)
743 {
744   GskRotateTransform *self = (GskRotateTransform *) transform;
745   float s, c, xx, xy, yx, yy;
746 
747   _sincos (self->angle, &s, &c);
748 
749   xx =  c * *out_xx + s * *out_xy;
750   yx =  c * *out_yx + s * *out_yy;
751   xy = -s * *out_xx + c * *out_xy;
752   yy = -s * *out_yx + c * *out_yy;
753 
754   *out_xx = xx;
755   *out_yx = yx;
756   *out_xy = xy;
757   *out_yy = yy;
758 }
759 
760 static GskTransform *
gsk_rotate_transform_apply(GskTransform * transform,GskTransform * apply_to)761 gsk_rotate_transform_apply (GskTransform *transform,
762                             GskTransform *apply_to)
763 {
764   GskRotateTransform *self = (GskRotateTransform *) transform;
765 
766   return gsk_transform_rotate (apply_to, self->angle);
767 }
768 
769 static GskTransform *
gsk_rotate_transform_invert(GskTransform * transform,GskTransform * next)770 gsk_rotate_transform_invert (GskTransform *transform,
771                              GskTransform *next)
772 {
773   GskRotateTransform *self = (GskRotateTransform *) transform;
774 
775   return gsk_transform_rotate (next, - self->angle);
776 }
777 
778 static gboolean
gsk_rotate_transform_equal(GskTransform * first_transform,GskTransform * second_transform)779 gsk_rotate_transform_equal (GskTransform *first_transform,
780                             GskTransform *second_transform)
781 {
782   GskRotateTransform *first = (GskRotateTransform *) first_transform;
783   GskRotateTransform *second = (GskRotateTransform *) second_transform;
784 
785   return G_APPROX_VALUE (first->angle, second->angle, 0.01f);
786 }
787 
788 static void
gsk_rotate_transform_print(GskTransform * transform,GString * string)789 gsk_rotate_transform_print (GskTransform *transform,
790                             GString      *string)
791 {
792   GskRotateTransform *self = (GskRotateTransform *) transform;
793 
794   g_string_append (string, "rotate(");
795   string_append_double (string, self->angle);
796   g_string_append (string, ")");
797 }
798 
799 static const GskTransformClass GSK_ROTATE_TRANSFORM_CLASS =
800 {
801   sizeof (GskRotateTransform),
802   "GskRotateTransform",
803   gsk_rotate_transform_finalize,
804   gsk_rotate_transform_to_matrix,
805   gsk_rotate_transform_apply_2d,
806   NULL,
807   NULL,
808   gsk_rotate_transform_print,
809   gsk_rotate_transform_apply,
810   gsk_rotate_transform_invert,
811   gsk_rotate_transform_equal,
812 };
813 
814 static inline float
normalize_angle(float angle)815 normalize_angle (float angle)
816 {
817 
818   if (angle >= 0 && angle < 360)
819     return angle;
820 
821   while (angle >= 360)
822     angle -= 360;
823   while (angle < 0)
824     angle += 360;
825 
826   /* Due to precision issues we may end up with a result that is just
827    * past the allowed range when rounded. For example, something like
828    * -epsilon + 360 when rounded to a float may end up with 360.
829    * So, we handle these cases by returning the exact value 0.
830    */
831 
832   if (angle >= 360)
833     angle = 0;
834 
835   g_assert (angle < 360.0);
836   g_assert (angle >= 0.0);
837 
838   return angle;
839 }
840 
841 /**
842  * gsk_transform_rotate:
843  * @next: (nullable) (transfer full): the next transform
844  * @angle: the rotation angle, in degrees (clockwise)
845  *
846  * Rotates @next @angle degrees in 2D - or in 3D-speak, around the z axis.
847  *
848  * Returns: The new transform
849  */
850 GskTransform *
gsk_transform_rotate(GskTransform * next,float angle)851 gsk_transform_rotate (GskTransform *next,
852                       float         angle)
853 {
854   GskRotateTransform *result;
855 
856   if (angle == 0.0f)
857     return next;
858 
859   if (gsk_transform_has_class (next, &GSK_ROTATE_TRANSFORM_CLASS))
860     {
861       GskTransform *r  = gsk_transform_rotate (gsk_transform_ref (next->next),
862                                                ((GskRotateTransform *) next)->angle + angle);
863       gsk_transform_unref (next);
864       return r;
865     }
866 
867   result = gsk_transform_alloc (&GSK_ROTATE_TRANSFORM_CLASS,
868                                 GSK_TRANSFORM_CATEGORY_2D,
869                                 next);
870 
871   result->angle = normalize_angle (angle);
872 
873   return &result->parent;
874 }
875 
876 /*** ROTATE 3D ***/
877 
878 typedef struct _GskRotate3dTransform GskRotate3dTransform;
879 
880 struct _GskRotate3dTransform
881 {
882   GskTransform parent;
883 
884   float angle;
885   graphene_vec3_t axis;
886 };
887 
888 static void
gsk_rotate3d_transform_finalize(GskTransform * self)889 gsk_rotate3d_transform_finalize (GskTransform *self)
890 {
891 }
892 
893 static void
gsk_rotate3d_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)894 gsk_rotate3d_transform_to_matrix (GskTransform      *transform,
895                                   graphene_matrix_t *out_matrix)
896 {
897   GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
898 
899   graphene_matrix_init_rotate (out_matrix, self->angle, &self->axis);
900 }
901 
902 static GskTransform *
gsk_rotate3d_transform_apply(GskTransform * transform,GskTransform * apply_to)903 gsk_rotate3d_transform_apply (GskTransform *transform,
904                               GskTransform *apply_to)
905 {
906   GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
907 
908   return gsk_transform_rotate_3d (apply_to, self->angle, &self->axis);
909 }
910 
911 static GskTransform *
gsk_rotate3d_transform_invert(GskTransform * transform,GskTransform * next)912 gsk_rotate3d_transform_invert (GskTransform *transform,
913                                GskTransform *next)
914 {
915   GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
916 
917   return gsk_transform_rotate_3d (next, - self->angle, &self->axis);
918 }
919 
920 static gboolean
gsk_rotate3d_transform_equal(GskTransform * first_transform,GskTransform * second_transform)921 gsk_rotate3d_transform_equal (GskTransform *first_transform,
922                               GskTransform *second_transform)
923 {
924   GskRotate3dTransform *first = (GskRotate3dTransform *) first_transform;
925   GskRotate3dTransform *second = (GskRotate3dTransform *) second_transform;
926 
927   return G_APPROX_VALUE (first->angle, second->angle, 0.01f) &&
928          graphene_vec3_equal (&first->axis, &second->axis);
929 }
930 
931 static void
gsk_rotate3d_transform_print(GskTransform * transform,GString * string)932 gsk_rotate3d_transform_print (GskTransform *transform,
933                             GString      *string)
934 {
935   GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
936   float f[3];
937   guint i;
938 
939   g_string_append (string, "rotate3d(");
940   graphene_vec3_to_float (&self->axis, f);
941   for (i = 0; i < 3; i++)
942     {
943       string_append_double (string, f[i]);
944       g_string_append (string, ", ");
945     }
946   string_append_double (string, self->angle);
947   g_string_append (string, ")");
948 }
949 
950 static const GskTransformClass GSK_ROTATE3D_TRANSFORM_CLASS =
951 {
952   sizeof (GskRotate3dTransform),
953   "GskRotate3dTransform",
954   gsk_rotate3d_transform_finalize,
955   gsk_rotate3d_transform_to_matrix,
956   NULL,
957   NULL,
958   NULL,
959   gsk_rotate3d_transform_print,
960   gsk_rotate3d_transform_apply,
961   gsk_rotate3d_transform_invert,
962   gsk_rotate3d_transform_equal,
963 };
964 
965 /**
966  * gsk_transform_rotate_3d:
967  * @next: (nullable) (transfer full): the next transform
968  * @angle: the rotation angle, in degrees (clockwise)
969  * @axis: The rotation axis
970  *
971  * Rotates @next @angle degrees around @axis.
972  *
973  * For a rotation in 2D space, use [method@Gsk.Transform.rotate]
974  *
975  * Returns: The new transform
976  */
977 GskTransform *
gsk_transform_rotate_3d(GskTransform * next,float angle,const graphene_vec3_t * axis)978 gsk_transform_rotate_3d (GskTransform          *next,
979                          float                  angle,
980                          const graphene_vec3_t *axis)
981 {
982   GskRotate3dTransform *result;
983 
984   if (graphene_vec3_get_x (axis) == 0.0 && graphene_vec3_get_y (axis) == 0.0)
985     return gsk_transform_rotate (next, angle);
986 
987   if (angle == 0.0f)
988     return next;
989 
990   result = gsk_transform_alloc (&GSK_ROTATE3D_TRANSFORM_CLASS,
991                                 GSK_TRANSFORM_CATEGORY_3D,
992                                 next);
993 
994   result->angle = normalize_angle (angle);
995   graphene_vec3_init_from_vec3 (&result->axis, axis);
996 
997   return &result->parent;
998 }
999 
1000 /*** SCALE ***/
1001 
1002 typedef struct _GskScaleTransform GskScaleTransform;
1003 
1004 struct _GskScaleTransform
1005 {
1006   GskTransform parent;
1007 
1008   float factor_x;
1009   float factor_y;
1010   float factor_z;
1011 };
1012 
1013 static void
gsk_scale_transform_finalize(GskTransform * self)1014 gsk_scale_transform_finalize (GskTransform *self)
1015 {
1016 }
1017 
1018 static void
gsk_scale_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)1019 gsk_scale_transform_to_matrix (GskTransform      *transform,
1020                                graphene_matrix_t *out_matrix)
1021 {
1022   GskScaleTransform *self = (GskScaleTransform *) transform;
1023 
1024   graphene_matrix_init_scale (out_matrix, self->factor_x, self->factor_y, self->factor_z);
1025 }
1026 
1027 static void
gsk_scale_transform_apply_2d(GskTransform * transform,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)1028 gsk_scale_transform_apply_2d (GskTransform *transform,
1029                               float        *out_xx,
1030                               float        *out_yx,
1031                               float        *out_xy,
1032                               float        *out_yy,
1033                               float        *out_dx,
1034                               float        *out_dy)
1035 {
1036   GskScaleTransform *self = (GskScaleTransform *) transform;
1037 
1038   g_assert (self->factor_z == 1.0);
1039 
1040   *out_xx *= self->factor_x;
1041   *out_yx *= self->factor_x;
1042   *out_xy *= self->factor_y;
1043   *out_yy *= self->factor_y;
1044 }
1045 
1046 static void
gsk_scale_transform_apply_affine(GskTransform * transform,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)1047 gsk_scale_transform_apply_affine (GskTransform *transform,
1048                                   float        *out_scale_x,
1049                                   float        *out_scale_y,
1050                                   float        *out_dx,
1051                                   float        *out_dy)
1052 {
1053   GskScaleTransform *self = (GskScaleTransform *) transform;
1054 
1055   g_assert (self->factor_z == 1.0);
1056 
1057   *out_scale_x *= self->factor_x;
1058   *out_scale_y *= self->factor_y;
1059 }
1060 
1061 static GskTransform *
gsk_scale_transform_apply(GskTransform * transform,GskTransform * apply_to)1062 gsk_scale_transform_apply (GskTransform *transform,
1063                            GskTransform *apply_to)
1064 {
1065   GskScaleTransform *self = (GskScaleTransform *) transform;
1066 
1067   return gsk_transform_scale_3d (apply_to, self->factor_x, self->factor_y, self->factor_z);
1068 }
1069 
1070 static GskTransform *
gsk_scale_transform_invert(GskTransform * transform,GskTransform * next)1071 gsk_scale_transform_invert (GskTransform *transform,
1072                             GskTransform *next)
1073 {
1074   GskScaleTransform *self = (GskScaleTransform *) transform;
1075 
1076   return gsk_transform_scale_3d (next,
1077                                  1.f / self->factor_x,
1078                                  1.f / self->factor_y,
1079                                  1.f / self->factor_z);
1080 }
1081 
1082 static gboolean
gsk_scale_transform_equal(GskTransform * first_transform,GskTransform * second_transform)1083 gsk_scale_transform_equal (GskTransform *first_transform,
1084                            GskTransform *second_transform)
1085 {
1086   GskScaleTransform *first = (GskScaleTransform *) first_transform;
1087   GskScaleTransform *second = (GskScaleTransform *) second_transform;
1088 
1089   return G_APPROX_VALUE (first->factor_x, second->factor_x, FLT_EPSILON) &&
1090          G_APPROX_VALUE (first->factor_y, second->factor_y, FLT_EPSILON) &&
1091          G_APPROX_VALUE (first->factor_z, second->factor_z, FLT_EPSILON);
1092 }
1093 
1094 static void
gsk_scale_transform_print(GskTransform * transform,GString * string)1095 gsk_scale_transform_print (GskTransform *transform,
1096                            GString      *string)
1097 {
1098   GskScaleTransform *self = (GskScaleTransform *) transform;
1099 
1100   if (self->factor_z == 1.0)
1101     {
1102       g_string_append (string, "scale(");
1103       string_append_double (string, self->factor_x);
1104       if (self->factor_x != self->factor_y)
1105         {
1106           g_string_append (string, ", ");
1107           string_append_double (string, self->factor_y);
1108         }
1109       g_string_append (string, ")");
1110     }
1111   else
1112     {
1113       g_string_append (string, "scale3d(");
1114       string_append_double (string, self->factor_x);
1115       g_string_append (string, ", ");
1116       string_append_double (string, self->factor_y);
1117       g_string_append (string, ", ");
1118       string_append_double (string, self->factor_z);
1119       g_string_append (string, ")");
1120     }
1121 }
1122 
1123 static const GskTransformClass GSK_SCALE_TRANSFORM_CLASS =
1124 {
1125   sizeof (GskScaleTransform),
1126   "GskScaleTransform",
1127   gsk_scale_transform_finalize,
1128   gsk_scale_transform_to_matrix,
1129   gsk_scale_transform_apply_2d,
1130   gsk_scale_transform_apply_affine,
1131   NULL,
1132   gsk_scale_transform_print,
1133   gsk_scale_transform_apply,
1134   gsk_scale_transform_invert,
1135   gsk_scale_transform_equal,
1136 };
1137 
1138 /**
1139  * gsk_transform_scale:
1140  * @next: (nullable) (transfer full): the next transform
1141  * @factor_x: scaling factor on the X axis
1142  * @factor_y: scaling factor on the Y axis
1143  *
1144  * Scales @next in 2-dimensional space by the given factors.
1145  *
1146  * Use [method@Gsk.Transform.scale_3d] to scale in all 3 dimensions.
1147  *
1148  * Returns: The new transform
1149  **/
1150 GskTransform *
gsk_transform_scale(GskTransform * next,float factor_x,float factor_y)1151 gsk_transform_scale (GskTransform *next,
1152                      float         factor_x,
1153                      float         factor_y)
1154 {
1155   return gsk_transform_scale_3d (next, factor_x, factor_y, 1.0);
1156 }
1157 
1158 /**
1159  * gsk_transform_scale_3d:
1160  * @next: (nullable) (transfer full): the next transform
1161  * @factor_x: scaling factor on the X axis
1162  * @factor_y: scaling factor on the Y axis
1163  * @factor_z: scaling factor on the Z axis
1164  *
1165  * Scales @next by the given factors.
1166  *
1167  * Returns: The new transform
1168  **/
1169 GskTransform *
gsk_transform_scale_3d(GskTransform * next,float factor_x,float factor_y,float factor_z)1170 gsk_transform_scale_3d (GskTransform *next,
1171                         float         factor_x,
1172                         float         factor_y,
1173                         float         factor_z)
1174 {
1175   GskScaleTransform *result;
1176 
1177   if (factor_x == 1 && factor_y == 1 && factor_z == 1)
1178     return next;
1179 
1180   if (gsk_transform_has_class (next, &GSK_SCALE_TRANSFORM_CLASS))
1181     {
1182       GskScaleTransform *scale = (GskScaleTransform *) next;
1183       GskTransform *r = gsk_transform_scale_3d (gsk_transform_ref (next->next),
1184                                                 scale->factor_x * factor_x,
1185                                                 scale->factor_y * factor_y,
1186                                                 scale->factor_z * factor_z);
1187       gsk_transform_unref (next);
1188       return r;
1189     }
1190 
1191   result = gsk_transform_alloc (&GSK_SCALE_TRANSFORM_CLASS,
1192                                 factor_z != 1.0 ? GSK_TRANSFORM_CATEGORY_3D
1193                                                 : GSK_TRANSFORM_CATEGORY_2D_AFFINE,
1194                                 next);
1195 
1196   result->factor_x = factor_x;
1197   result->factor_y = factor_y;
1198   result->factor_z = factor_z;
1199 
1200   return &result->parent;
1201 }
1202 
1203 /*** PERSPECTIVE ***/
1204 
1205 typedef struct _GskPerspectiveTransform GskPerspectiveTransform;
1206 
1207 struct _GskPerspectiveTransform
1208 {
1209   GskTransform parent;
1210 
1211   float depth;
1212 };
1213 
1214 static void
gsk_perspective_transform_finalize(GskTransform * self)1215 gsk_perspective_transform_finalize (GskTransform *self)
1216 {
1217 }
1218 
1219 static void
gsk_perspective_transform_to_matrix(GskTransform * transform,graphene_matrix_t * out_matrix)1220 gsk_perspective_transform_to_matrix (GskTransform      *transform,
1221                                      graphene_matrix_t *out_matrix)
1222 {
1223   GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1224   float f[16] = { 1.f, 0.f, 0.f,  0.f,
1225                   0.f, 1.f, 0.f,  0.f,
1226                   0.f, 0.f, 1.f, self->depth ? -1.f / self->depth : 0.f,
1227                   0.f, 0.f, 0.f,  1.f };
1228 
1229   graphene_matrix_init_from_float (out_matrix, f);
1230 }
1231 
1232 
1233 static GskTransform *
gsk_perspective_transform_apply(GskTransform * transform,GskTransform * apply_to)1234 gsk_perspective_transform_apply (GskTransform *transform,
1235                                  GskTransform *apply_to)
1236 {
1237   GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1238 
1239   return gsk_transform_perspective (apply_to, self->depth);
1240 }
1241 
1242 static GskTransform *
gsk_perspective_transform_invert(GskTransform * transform,GskTransform * next)1243 gsk_perspective_transform_invert (GskTransform *transform,
1244                                   GskTransform *next)
1245 {
1246   GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1247 
1248   return gsk_transform_perspective (next, - self->depth);
1249 }
1250 
1251 static gboolean
gsk_perspective_transform_equal(GskTransform * first_transform,GskTransform * second_transform)1252 gsk_perspective_transform_equal (GskTransform *first_transform,
1253                                  GskTransform *second_transform)
1254 {
1255   GskPerspectiveTransform *first = (GskPerspectiveTransform *) first_transform;
1256   GskPerspectiveTransform *second = (GskPerspectiveTransform *) second_transform;
1257 
1258   return G_APPROX_VALUE (first->depth, second->depth, 0.001f);
1259 }
1260 
1261 static void
gsk_perspective_transform_print(GskTransform * transform,GString * string)1262 gsk_perspective_transform_print (GskTransform *transform,
1263                                  GString      *string)
1264 {
1265   GskPerspectiveTransform *self = (GskPerspectiveTransform *) transform;
1266 
1267   g_string_append (string, "perspective(");
1268   string_append_double (string, self->depth);
1269   g_string_append (string, ")");
1270 }
1271 
1272 static const GskTransformClass GSK_PERSPECTIVE_TRANSFORM_CLASS =
1273 {
1274   sizeof (GskPerspectiveTransform),
1275   "GskPerspectiveTransform",
1276   gsk_perspective_transform_finalize,
1277   gsk_perspective_transform_to_matrix,
1278   NULL,
1279   NULL,
1280   NULL,
1281   gsk_perspective_transform_print,
1282   gsk_perspective_transform_apply,
1283   gsk_perspective_transform_invert,
1284   gsk_perspective_transform_equal,
1285 };
1286 
1287 /**
1288  * gsk_transform_perspective:
1289  * @next: (nullable) (transfer full): the next transform
1290  * @depth: distance of the z=0 plane. Lower values give a more
1291  *   flattened pyramid and therefore a more pronounced
1292  *   perspective effect.
1293  *
1294  * Applies a perspective projection transform.
1295  *
1296  * This transform scales points in X and Y based on their Z value,
1297  * scaling points with positive Z values away from the origin, and
1298  * those with negative Z values towards the origin. Points
1299  * on the z=0 plane are unchanged.
1300  *
1301  * Returns: The new transform
1302  */
1303 GskTransform *
gsk_transform_perspective(GskTransform * next,float depth)1304 gsk_transform_perspective (GskTransform *next,
1305                            float         depth)
1306 {
1307   GskPerspectiveTransform *result;
1308 
1309   if (gsk_transform_has_class (next, &GSK_PERSPECTIVE_TRANSFORM_CLASS))
1310     {
1311       GskTransform *r = gsk_transform_perspective (gsk_transform_ref (next->next),
1312                                                    ((GskPerspectiveTransform *) next)->depth + depth);
1313       gsk_transform_unref (next);
1314       return r;
1315     }
1316 
1317   result = gsk_transform_alloc (&GSK_PERSPECTIVE_TRANSFORM_CLASS,
1318                                 GSK_TRANSFORM_CATEGORY_ANY,
1319                                 next);
1320 
1321   result->depth = depth;
1322 
1323   return &result->parent;
1324 }
1325 
1326 /*** PUBLIC API ***/
1327 
1328 static void
gsk_transform_finalize(GskTransform * self)1329 gsk_transform_finalize (GskTransform *self)
1330 {
1331   self->transform_class->finalize (self);
1332 
1333   gsk_transform_unref (self->next);
1334 }
1335 
1336 /**
1337  * gsk_transform_ref:
1338  * @self: (nullable): a `GskTransform`
1339  *
1340  * Acquires a reference on the given `GskTransform`.
1341  *
1342  * Returns: (transfer none): the `GskTransform` with an additional reference
1343  */
1344 GskTransform *
gsk_transform_ref(GskTransform * self)1345 gsk_transform_ref (GskTransform *self)
1346 {
1347   if (self == NULL)
1348     return NULL;
1349 
1350   return g_atomic_rc_box_acquire (self);
1351 }
1352 
1353 /**
1354  * gsk_transform_unref:
1355  * @self: (nullable): a `GskTransform`
1356  *
1357  * Releases a reference on the given `GskTransform`.
1358  *
1359  * If the reference was the last, the resources associated to the @self are
1360  * freed.
1361  */
1362 void
gsk_transform_unref(GskTransform * self)1363 gsk_transform_unref (GskTransform *self)
1364 {
1365   if (self == NULL)
1366     return;
1367 
1368   g_atomic_rc_box_release_full (self, (GDestroyNotify) gsk_transform_finalize);
1369 }
1370 
1371 /**
1372  * gsk_transform_print:
1373  * @self: (nullable): a `GskTransform`
1374  * @string:  The string to print into
1375  *
1376  * Converts @self into a human-readable string representation suitable
1377  * for printing.
1378  *
1379  * The result of this function can later be parsed with
1380  * [func@Gsk.Transform.parse].
1381  */
1382 void
gsk_transform_print(GskTransform * self,GString * string)1383 gsk_transform_print (GskTransform *self,
1384                      GString      *string)
1385 {
1386   g_return_if_fail (string != NULL);
1387 
1388   if (self == NULL)
1389     {
1390       g_string_append (string, "none");
1391       return;
1392     }
1393 
1394   if (self->next != NULL)
1395     {
1396       gsk_transform_print (self->next, string);
1397       g_string_append (string, " ");
1398     }
1399 
1400   self->transform_class->print (self, string);
1401 }
1402 
1403 /**
1404  * gsk_transform_to_string:
1405  * @self: (nullable): a `GskTransform`
1406  *
1407  * Converts a matrix into a string that is suitable for printing.
1408  *
1409  * The resulting string can be parsed with [func@Gsk.Transform.parse].
1410  *
1411  * This is a wrapper around [method@Gsk.Transform.print].
1412  *
1413  * Returns: A new string for @self
1414  */
1415 char *
gsk_transform_to_string(GskTransform * self)1416 gsk_transform_to_string (GskTransform *self)
1417 {
1418   GString *string;
1419 
1420   string = g_string_new ("");
1421 
1422   gsk_transform_print (self, string);
1423 
1424   return g_string_free (string, FALSE);
1425 }
1426 
1427 /**
1428  * gsk_transform_to_matrix:
1429  * @self: (nullable): a `GskTransform`
1430  * @out_matrix: (out caller-allocates): The matrix to set
1431  *
1432  * Computes the actual value of @self and stores it in @out_matrix.
1433  *
1434  * The previous value of @out_matrix will be ignored.
1435  */
1436 void
gsk_transform_to_matrix(GskTransform * self,graphene_matrix_t * out_matrix)1437 gsk_transform_to_matrix (GskTransform      *self,
1438                          graphene_matrix_t *out_matrix)
1439 {
1440   graphene_matrix_t m;
1441 
1442   if (self == NULL)
1443     {
1444       graphene_matrix_init_identity (out_matrix);
1445       return;
1446     }
1447 
1448   gsk_transform_to_matrix (self->next, out_matrix);
1449   self->transform_class->to_matrix (self, &m);
1450   graphene_matrix_multiply (&m, out_matrix, out_matrix);
1451 }
1452 
1453 /**
1454  * gsk_transform_to_2d:
1455  * @self: a 2D `GskTransform`
1456  * @out_xx: (out): return location for the xx member
1457  * @out_yx: (out): return location for the yx member
1458  * @out_xy: (out): return location for the xy member
1459  * @out_yy: (out): return location for the yy member
1460  * @out_dx: (out): return location for the x0 member
1461  * @out_dy: (out): return location for the y0 member
1462  *
1463  * Converts a `GskTransform` to a 2D transformation matrix.
1464  *
1465  * @self must be a 2D transformation. If you are not
1466  * sure, use gsk_transform_get_category() >=
1467  * %GSK_TRANSFORM_CATEGORY_2D to check.
1468  *
1469  * The returned values have the following layout:
1470  *
1471  * ```
1472  *   | xx yx |   |  a  b  0 |
1473  *   | xy yy | = |  c  d  0 |
1474  *   | dx dy |   | tx ty  1 |
1475  * ```
1476  *
1477  * This function can be used to convert between a `GskTransform`
1478  * and a matrix type from other 2D drawing libraries, in particular
1479  * Cairo.
1480  */
1481 void
gsk_transform_to_2d(GskTransform * self,float * out_xx,float * out_yx,float * out_xy,float * out_yy,float * out_dx,float * out_dy)1482 gsk_transform_to_2d (GskTransform *self,
1483                      float        *out_xx,
1484                      float        *out_yx,
1485                      float        *out_xy,
1486                      float        *out_yy,
1487                      float        *out_dx,
1488                      float        *out_dy)
1489 {
1490   *out_xx = 1.0f;
1491   *out_yx = 0.0f;
1492   *out_xy = 0.0f;
1493   *out_yy = 1.0f;
1494   *out_dx = 0.0f;
1495   *out_dy = 0.0f;
1496 
1497   if (self == NULL)
1498     return;
1499 
1500   if (G_UNLIKELY (self->category < GSK_TRANSFORM_CATEGORY_2D))
1501     {
1502       char *s = gsk_transform_to_string (self);
1503       g_warning ("Given transform \"%s\" is not a 2D transform.", s);
1504       g_free (s);
1505       return;
1506     }
1507 
1508   gsk_transform_to_2d (self->next,
1509                        out_xx, out_yx,
1510                        out_xy, out_yy,
1511                        out_dx, out_dy);
1512 
1513   self->transform_class->apply_2d (self,
1514                                    out_xx, out_yx,
1515                                    out_xy, out_yy,
1516                                    out_dx, out_dy);
1517 }
1518 
1519 /**
1520  * gsk_transform_to_affine:
1521  * @self: a `GskTransform`
1522  * @out_scale_x: (out): return location for the scale
1523  *   factor in the x direction
1524  * @out_scale_y: (out): return location for the scale
1525  *   factor in the y direction
1526  * @out_dx: (out): return location for the translation
1527  *   in the x direction
1528  * @out_dy: (out): return location for the translation
1529  *   in the y direction
1530  *
1531  * Converts a `GskTransform` to 2D affine transformation factors.
1532  *
1533  * @self must be a 2D transformation. If you are not
1534  * sure, use
1535  *
1536  *     gsk_transform_get_category() >= %GSK_TRANSFORM_CATEGORY_2D_AFFINE
1537  *
1538  * to check.
1539  */
1540 void
gsk_transform_to_affine(GskTransform * self,float * out_scale_x,float * out_scale_y,float * out_dx,float * out_dy)1541 gsk_transform_to_affine (GskTransform *self,
1542                          float        *out_scale_x,
1543                          float        *out_scale_y,
1544                          float        *out_dx,
1545                          float        *out_dy)
1546 {
1547   *out_scale_x = 1.0f;
1548   *out_scale_y = 1.0f;
1549   *out_dx = 0.0f;
1550   *out_dy = 0.0f;
1551 
1552   if (self == NULL)
1553     return;
1554 
1555   if (G_UNLIKELY (self->category < GSK_TRANSFORM_CATEGORY_2D_AFFINE))
1556     {
1557       char *s = gsk_transform_to_string (self);
1558       g_warning ("Given transform \"%s\" is not an affine 2D transform.", s);
1559       g_free (s);
1560       return;
1561     }
1562 
1563   gsk_transform_to_affine (self->next,
1564                            out_scale_x, out_scale_y,
1565                            out_dx, out_dy);
1566 
1567   self->transform_class->apply_affine (self,
1568                                        out_scale_x, out_scale_y,
1569                                        out_dx, out_dy);
1570 }
1571 
1572 /**
1573  * gsk_transform_to_translate:
1574  * @self: a `GskTransform`
1575  * @out_dx: (out): return location for the translation
1576  *   in the x direction
1577  * @out_dy: (out): return location for the translation
1578  *   in the y direction
1579  *
1580  * Converts a `GskTransform` to a translation operation.
1581  *
1582  * @self must be a 2D transformation. If you are not
1583  * sure, use
1584  *
1585  *     gsk_transform_get_category() >= %GSK_TRANSFORM_CATEGORY_2D_TRANSLATE
1586  *
1587  * to check.
1588  */
1589 void
gsk_transform_to_translate(GskTransform * self,float * out_dx,float * out_dy)1590 gsk_transform_to_translate (GskTransform *self,
1591                             float        *out_dx,
1592                             float        *out_dy)
1593 {
1594   *out_dx = 0.0f;
1595   *out_dy = 0.0f;
1596 
1597   if (self == NULL)
1598     return;
1599 
1600   if (G_UNLIKELY (self->category < GSK_TRANSFORM_CATEGORY_2D_TRANSLATE))
1601     {
1602       char *s = gsk_transform_to_string (self);
1603       g_warning ("Given transform \"%s\" is not an affine 2D translation.", s);
1604       g_free (s);
1605 
1606       return;
1607     }
1608 
1609   gsk_transform_to_translate (self->next, out_dx, out_dy);
1610 
1611   self->transform_class->apply_translate (self, out_dx, out_dy);
1612 }
1613 
1614 /**
1615  * gsk_transform_transform:
1616  * @next: (nullable) (transfer full): Transform to apply @other to
1617  * @other: (nullable):  Transform to apply
1618  *
1619  * Applies all the operations from @other to @next.
1620  *
1621  * Returns: The new transform
1622  */
1623 GskTransform *
gsk_transform_transform(GskTransform * next,GskTransform * other)1624 gsk_transform_transform (GskTransform *next,
1625                          GskTransform *other)
1626 {
1627   if (other == NULL)
1628     return next;
1629 
1630   if (next == NULL)
1631     return gsk_transform_ref (other);
1632 
1633   if (gsk_transform_is_identity (next))
1634     {
1635       /* ref before unref to avoid catastrophe when other == next */
1636       other = gsk_transform_ref (other);
1637       gsk_transform_unref (next);
1638       return other;
1639     }
1640 
1641   next = gsk_transform_transform (next, other->next);
1642   return other->transform_class->apply (other, next);
1643 }
1644 
1645 /**
1646  * gsk_transform_invert:
1647  * @self: (nullable) (transfer full): Transform to invert
1648  *
1649  * Inverts the given transform.
1650  *
1651  * If @self is not invertible, %NULL is returned.
1652  * Note that inverting %NULL also returns %NULL, which is
1653  * the correct inverse of %NULL. If you need to differentiate
1654  * between those cases, you should check @self is not %NULL
1655  * before calling this function.
1656  *
1657  * Returns: (nullable): The inverted transform
1658  */
1659 GskTransform *
gsk_transform_invert(GskTransform * self)1660 gsk_transform_invert (GskTransform *self)
1661 {
1662   GskTransform *result = NULL;
1663   GskTransform *cur;
1664 
1665   for (cur = self; cur; cur = cur->next)
1666     {
1667       result = cur->transform_class->invert (cur, result);
1668       if (result == NULL)
1669         break;
1670     }
1671 
1672   gsk_transform_unref (self);
1673 
1674   return result;
1675 }
1676 
1677 /**
1678  * gsk_transform_equal:
1679  * @first: (nullable): the first transform
1680  * @second: (nullable): the second transform
1681  *
1682  * Checks two transforms for equality.
1683  *
1684  * Returns: %TRUE if the two transforms perform the same operation
1685  */
1686 gboolean
gsk_transform_equal(GskTransform * first,GskTransform * second)1687 gsk_transform_equal (GskTransform *first,
1688                      GskTransform *second)
1689 {
1690   if (first == second)
1691     return TRUE;
1692 
1693   if (first == NULL)
1694     return gsk_transform_is_identity (second);
1695 
1696   if (second == NULL)
1697     return gsk_transform_is_identity (first);
1698 
1699   if (first->transform_class != second->transform_class)
1700     return FALSE;
1701 
1702   if (!gsk_transform_equal (first->next, second->next))
1703     return FALSE;
1704 
1705   return first->transform_class->equal (first, second);
1706 }
1707 
1708 /**
1709  * gsk_transform_get_category:
1710  * @self: (nullable): A `GskTransform`
1711  *
1712  * Returns the category this transform belongs to.
1713  *
1714  * Returns: The category of the transform
1715  **/
GskTransformCategory(gsk_transform_get_category)1716 GskTransformCategory
1717 (gsk_transform_get_category) (GskTransform *self)
1718 {
1719   if (self == NULL)
1720     return GSK_TRANSFORM_CATEGORY_IDENTITY;
1721 
1722   return self->category;
1723 }
1724 
1725 /*
1726  * gsk_transform_new: (constructor):
1727  *
1728  * Creates a new identity transform.
1729  *
1730  * This function is meant to be used by language
1731  * bindings. For C code, this is equivalent to using %NULL.
1732  *
1733  * Returns: A new identity transform
1734  */
1735 GskTransform *
gsk_transform_new(void)1736 gsk_transform_new (void)
1737 {
1738   return gsk_transform_alloc (&GSK_IDENTITY_TRANSFORM_CLASS, GSK_TRANSFORM_CATEGORY_IDENTITY, NULL);
1739 }
1740 
1741 /**
1742  * gsk_transform_transform_bounds:
1743  * @self: a `GskTransform`
1744  * @rect: a `graphene_rect_t`
1745  * @out_rect: (out caller-allocates): return location for the bounds
1746  *   of the transformed rectangle
1747  *
1748  * Transforms a `graphene_rect_t` using the given transform @self.
1749  *
1750  * The result is the bounding box containing the coplanar quad.
1751  */
1752 void
gsk_transform_transform_bounds(GskTransform * self,const graphene_rect_t * rect,graphene_rect_t * out_rect)1753 gsk_transform_transform_bounds (GskTransform          *self,
1754                                 const graphene_rect_t *rect,
1755                                 graphene_rect_t       *out_rect)
1756 {
1757   switch (gsk_transform_get_category (self))
1758     {
1759     case GSK_TRANSFORM_CATEGORY_IDENTITY:
1760       graphene_rect_init_from_rect (out_rect, rect);
1761       break;
1762 
1763     case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
1764       {
1765         float dx, dy;
1766 
1767         gsk_transform_to_translate (self, &dx, &dy);
1768         graphene_rect_init (out_rect,
1769                             rect->origin.x + dx,
1770                             rect->origin.y + dy,
1771                             rect->size.width,
1772                             rect->size.height);
1773       }
1774     break;
1775 
1776     case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
1777       {
1778         float dx, dy, scale_x, scale_y;
1779 
1780         gsk_transform_to_affine (self, &scale_x, &scale_y, &dx, &dy);
1781 
1782         graphene_rect_init (out_rect,
1783                             (rect->origin.x * scale_x) + dx,
1784                             (rect->origin.y * scale_y) + dy,
1785                             rect->size.width * scale_x,
1786                             rect->size.height * scale_y);
1787       }
1788     break;
1789 
1790     case GSK_TRANSFORM_CATEGORY_UNKNOWN:
1791     case GSK_TRANSFORM_CATEGORY_ANY:
1792     case GSK_TRANSFORM_CATEGORY_3D:
1793     case GSK_TRANSFORM_CATEGORY_2D:
1794     default:
1795       {
1796         graphene_matrix_t mat;
1797 
1798         gsk_transform_to_matrix (self, &mat);
1799         gsk_matrix_transform_bounds (&mat, rect, out_rect);
1800       }
1801       break;
1802     }
1803 }
1804 
1805 /**
1806  * gsk_transform_transform_point:
1807  * @self: a `GskTransform`
1808  * @point: a `graphene_point_t`
1809  * @out_point: (out caller-allocates): return location for
1810  *   the transformed point
1811  *
1812  * Transforms a `graphene_point_t` using the given transform @self.
1813  */
1814 void
gsk_transform_transform_point(GskTransform * self,const graphene_point_t * point,graphene_point_t * out_point)1815 gsk_transform_transform_point (GskTransform           *self,
1816                                const graphene_point_t *point,
1817                                graphene_point_t       *out_point)
1818 {
1819   switch (gsk_transform_get_category (self))
1820     {
1821     case GSK_TRANSFORM_CATEGORY_IDENTITY:
1822       *out_point = *point;
1823       break;
1824 
1825     case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
1826       {
1827         float dx, dy;
1828 
1829         gsk_transform_to_translate (self, &dx, &dy);
1830         out_point->x = point->x + dx;
1831         out_point->y = point->y + dy;
1832       }
1833     break;
1834 
1835     case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
1836       {
1837         float dx, dy, scale_x, scale_y;
1838 
1839         gsk_transform_to_affine (self, &scale_x, &scale_y, &dx, &dy);
1840 
1841         out_point->x = (point->x * scale_x) + dx;
1842         out_point->y = (point->y * scale_y) + dy;
1843       }
1844     break;
1845 
1846     case GSK_TRANSFORM_CATEGORY_UNKNOWN:
1847     case GSK_TRANSFORM_CATEGORY_ANY:
1848     case GSK_TRANSFORM_CATEGORY_3D:
1849     case GSK_TRANSFORM_CATEGORY_2D:
1850     default:
1851       {
1852         graphene_matrix_t mat;
1853 
1854         gsk_transform_to_matrix (self, &mat);
1855         gsk_matrix_transform_point (&mat, point, out_point);
1856       }
1857       break;
1858     }
1859 }
1860 
1861 static guint
gsk_transform_parse_float(GtkCssParser * parser,guint n,gpointer data)1862 gsk_transform_parse_float (GtkCssParser *parser,
1863                            guint         n,
1864                            gpointer      data)
1865 {
1866   float *f = data;
1867   double d;
1868 
1869   if (!gtk_css_parser_consume_number (parser, &d))
1870     return 0;
1871 
1872   f[n] = d;
1873   return 1;
1874 }
1875 
1876 static guint
gsk_transform_parse_scale(GtkCssParser * parser,guint n,gpointer data)1877 gsk_transform_parse_scale (GtkCssParser *parser,
1878                            guint         n,
1879                            gpointer      data)
1880 {
1881   float *f = data;
1882   double d;
1883 
1884   if (!gtk_css_parser_consume_number (parser, &d))
1885     return 0;
1886 
1887   f[n] = d;
1888   f[1] = d;
1889   return 1;
1890 }
1891 
1892 gboolean
gsk_transform_parser_parse(GtkCssParser * parser,GskTransform ** out_transform)1893 gsk_transform_parser_parse (GtkCssParser  *parser,
1894                             GskTransform **out_transform)
1895 {
1896   const GtkCssToken *token;
1897   GskTransform *transform = NULL;
1898   float f[16] = { 0, };
1899   gboolean parsed_something = FALSE;
1900 
1901   token = gtk_css_parser_get_token (parser);
1902   if (gtk_css_token_is_ident (token, "none"))
1903     {
1904       gtk_css_parser_consume_token (parser);
1905       *out_transform = NULL;
1906       return TRUE;
1907     }
1908 
1909   while (TRUE)
1910     {
1911       if (gtk_css_token_is_function (token, "matrix"))
1912         {
1913           graphene_matrix_t matrix;
1914           if (!gtk_css_parser_consume_function (parser, 6, 6, gsk_transform_parse_float, f))
1915             goto fail;
1916 
1917           graphene_matrix_init_from_2d (&matrix, f[0], f[1], f[2], f[3], f[4], f[5]);
1918           transform = gsk_transform_matrix_with_category (transform,
1919                                                           &matrix,
1920                                                           GSK_TRANSFORM_CATEGORY_2D);
1921         }
1922       else if (gtk_css_token_is_function (token, "matrix3d"))
1923         {
1924           graphene_matrix_t matrix;
1925           if (!gtk_css_parser_consume_function (parser, 16, 16, gsk_transform_parse_float, f))
1926             goto fail;
1927 
1928           graphene_matrix_init_from_float (&matrix, f);
1929           transform = gsk_transform_matrix (transform, &matrix);
1930         }
1931       else if (gtk_css_token_is_function (token, "perspective"))
1932         {
1933           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1934             goto fail;
1935 
1936           transform = gsk_transform_perspective (transform, f[0]);
1937         }
1938       else if (gtk_css_token_is_function (token, "rotate") ||
1939                gtk_css_token_is_function (token, "rotateZ"))
1940         {
1941           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1942             goto fail;
1943 
1944           transform = gsk_transform_rotate (transform, f[0]);
1945         }
1946       else if (gtk_css_token_is_function (token, "rotate3d"))
1947         {
1948           graphene_vec3_t axis;
1949 
1950           if (!gtk_css_parser_consume_function (parser, 4, 4, gsk_transform_parse_float, f))
1951             goto fail;
1952 
1953           graphene_vec3_init (&axis, f[0], f[1], f[2]);
1954           transform = gsk_transform_rotate_3d (transform, f[3], &axis);
1955         }
1956       else if (gtk_css_token_is_function (token, "rotateX"))
1957         {
1958           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1959             goto fail;
1960 
1961           transform = gsk_transform_rotate_3d (transform, f[0], graphene_vec3_x_axis ());
1962         }
1963       else if (gtk_css_token_is_function (token, "rotateY"))
1964         {
1965           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1966             goto fail;
1967 
1968           transform = gsk_transform_rotate_3d (transform, f[0], graphene_vec3_y_axis ());
1969         }
1970       else if (gtk_css_token_is_function (token, "scale"))
1971         {
1972           if (!gtk_css_parser_consume_function (parser, 1, 2, gsk_transform_parse_scale, f))
1973             goto fail;
1974 
1975           transform = gsk_transform_scale (transform, f[0], f[1]);
1976         }
1977       else if (gtk_css_token_is_function (token, "scale3d"))
1978         {
1979           if (!gtk_css_parser_consume_function (parser, 3, 3, gsk_transform_parse_float, f))
1980             goto fail;
1981 
1982           transform = gsk_transform_scale_3d (transform, f[0], f[1], f[2]);
1983         }
1984       else if (gtk_css_token_is_function (token, "scaleX"))
1985         {
1986           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1987             goto fail;
1988 
1989           transform = gsk_transform_scale (transform, f[0], 1.f);
1990         }
1991       else if (gtk_css_token_is_function (token, "scaleY"))
1992         {
1993           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
1994             goto fail;
1995 
1996           transform = gsk_transform_scale (transform, 1.f, f[0]);
1997         }
1998       else if (gtk_css_token_is_function (token, "scaleZ"))
1999         {
2000           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2001             goto fail;
2002 
2003           transform = gsk_transform_scale_3d (transform, 1.f, 1.f, f[0]);
2004         }
2005       else if (gtk_css_token_is_function (token, "translate"))
2006         {
2007           f[1] = 0.f;
2008           if (!gtk_css_parser_consume_function (parser, 1, 2, gsk_transform_parse_float, f))
2009             goto fail;
2010 
2011           transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (f[0], f[1]));
2012         }
2013       else if (gtk_css_token_is_function (token, "translate3d"))
2014         {
2015           if (!gtk_css_parser_consume_function (parser, 3, 3, gsk_transform_parse_float, f))
2016             goto fail;
2017 
2018           transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (f[0], f[1], f[2]));
2019         }
2020       else if (gtk_css_token_is_function (token, "translateX"))
2021         {
2022           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2023             goto fail;
2024 
2025           transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (f[0], 0.f));
2026         }
2027       else if (gtk_css_token_is_function (token, "translateY"))
2028         {
2029           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2030             goto fail;
2031 
2032           transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (0.f, f[0]));
2033         }
2034       else if (gtk_css_token_is_function (token, "translateZ"))
2035         {
2036           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2037             goto fail;
2038 
2039           transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (0.f, 0.f, f[0]));
2040         }
2041       else if (gtk_css_token_is_function (token, "skew"))
2042         {
2043           graphene_matrix_t matrix;
2044 
2045           if (!gtk_css_parser_consume_function (parser, 2, 2, gsk_transform_parse_float, f))
2046             goto fail;
2047 
2048           f[0] = f[0] / 180.0 * G_PI;
2049           f[1] = f[1] / 180.0 * G_PI;
2050 
2051           graphene_matrix_init_skew (&matrix, f[0], f[1]);
2052           transform = gsk_transform_matrix (transform, &matrix);
2053         }
2054       else if (gtk_css_token_is_function (token, "skewX"))
2055         {
2056           graphene_matrix_t matrix;
2057 
2058           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2059             goto fail;
2060 
2061           f[0] = f[0] / 180.0 * G_PI;
2062 
2063           graphene_matrix_init_skew (&matrix, f[0], 0);
2064           transform = gsk_transform_matrix (transform, &matrix);
2065         }
2066       else if (gtk_css_token_is_function (token, "skewY"))
2067         {
2068           graphene_matrix_t matrix;
2069 
2070           if (!gtk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
2071             goto fail;
2072 
2073           f[0] = f[0] / 180.0 * G_PI;
2074 
2075           graphene_matrix_init_skew (&matrix, 0, f[0]);
2076           transform = gsk_transform_matrix (transform, &matrix);
2077         }
2078       else
2079         {
2080           break;
2081         }
2082 
2083       parsed_something = TRUE;
2084       token = gtk_css_parser_get_token (parser);
2085     }
2086 
2087   if (!parsed_something)
2088     {
2089       gtk_css_parser_error_syntax (parser, "Expected a transform");
2090       goto fail;
2091     }
2092 
2093   *out_transform = transform;
2094   return TRUE;
2095 
2096 fail:
2097   gsk_transform_unref (transform);
2098   *out_transform = NULL;
2099   return FALSE;
2100 }
2101 
2102 /**
2103  * gsk_transform_parse:
2104  * @string: the string to parse
2105  * @out_transform: (out): The location to put the transform in
2106  *
2107  * Parses the given @string into a transform and puts it in
2108  * @out_transform.
2109  *
2110  * Strings printed via [method@Gsk.Transform.to_string]
2111  * can be read in again successfully using this function.
2112  *
2113  * If @string does not describe a valid transform, %FALSE is
2114  * returned and %NULL is put in @out_transform.
2115  *
2116  * Returns: %TRUE if @string described a valid transform.
2117  */
2118 gboolean
gsk_transform_parse(const char * string,GskTransform ** out_transform)2119 gsk_transform_parse (const char    *string,
2120                      GskTransform **out_transform)
2121 {
2122   GtkCssParser *parser;
2123   GBytes *bytes;
2124   gboolean result;
2125 
2126   g_return_val_if_fail (string != NULL, FALSE);
2127   g_return_val_if_fail (out_transform != NULL, FALSE);
2128 
2129   bytes = g_bytes_new_static (string, strlen (string));
2130   parser = gtk_css_parser_new_for_bytes (bytes, NULL, NULL, NULL, NULL, NULL);
2131 
2132   result = gsk_transform_parser_parse (parser, out_transform);
2133 
2134   if (result && !gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
2135     {
2136       g_clear_pointer (out_transform, gsk_transform_unref);
2137       result = FALSE;
2138     }
2139   gtk_css_parser_unref (parser);
2140   g_bytes_unref (bytes);
2141 
2142   return result;
2143 }
2144 
2145 /* Some of the graphene_matrix_transform apis yield unexpected
2146  * results with projective matrices, since they silently drop
2147  * the w component, so we provide working alternatives here.
2148  */
2149 void
gsk_matrix_transform_point(const graphene_matrix_t * m,const graphene_point_t * p,graphene_point_t * res)2150 gsk_matrix_transform_point (const graphene_matrix_t *m,
2151                             const graphene_point_t  *p,
2152                             graphene_point_t        *res)
2153 {
2154   graphene_vec4_t vec4;
2155   float w;
2156 
2157   graphene_vec4_init (&vec4, p->x, p->y, 0.0f, 1.0f);
2158   graphene_matrix_transform_vec4 (m, &vec4, &vec4);
2159 
2160   w = graphene_vec4_get_w (&vec4);
2161   res->x = graphene_vec4_get_x (&vec4) / w;
2162   res->y = graphene_vec4_get_y (&vec4) / w;
2163 }
2164 
2165 void
gsk_matrix_transform_point3d(const graphene_matrix_t * m,const graphene_point3d_t * p,graphene_point3d_t * res)2166 gsk_matrix_transform_point3d (const graphene_matrix_t  *m,
2167                               const graphene_point3d_t *p,
2168                               graphene_point3d_t       *res)
2169 {
2170   graphene_vec4_t vec4;
2171   float w;
2172 
2173   graphene_vec4_init (&vec4, p->x, p->y, 0.0f, 1.0f);
2174   graphene_matrix_transform_vec4 (m, &vec4, &vec4);
2175 
2176   w = graphene_vec4_get_w (&vec4);
2177   res->x = graphene_vec4_get_x (&vec4) / w;
2178   res->y = graphene_vec4_get_y (&vec4) / w;
2179   res->z = graphene_vec4_get_z (&vec4) / w;
2180 }
2181 
2182 void
gsk_matrix_transform_rect(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_quad_t * res)2183 gsk_matrix_transform_rect (const graphene_matrix_t *m,
2184                            const graphene_rect_t   *r,
2185                            graphene_quad_t         *res)
2186 {
2187   graphene_point_t ret[4];
2188   graphene_rect_t rr;
2189 
2190   graphene_rect_normalize_r (r, &rr);
2191 
2192 #define TRANSFORM_POINT(matrix, rect, corner, out_p)   do {\
2193   graphene_vec4_t __s; \
2194   graphene_point_t __p; \
2195   float w; \
2196   graphene_rect_get_ ## corner (rect, &__p); \
2197   graphene_vec4_init (&__s, __p.x, __p.y, 0.f, 1.f); \
2198   graphene_matrix_transform_vec4 (matrix, &__s, &__s); \
2199   w = graphene_vec4_get_w (&__s); \
2200   out_p.x = graphene_vec4_get_x (&__s) / w; \
2201   out_p.y = graphene_vec4_get_y (&__s) / w;           } while (0)
2202 
2203   TRANSFORM_POINT (m, &rr, top_left, ret[0]);
2204   TRANSFORM_POINT (m, &rr, top_right, ret[1]);
2205   TRANSFORM_POINT (m, &rr, bottom_right, ret[2]);
2206   TRANSFORM_POINT (m, &rr, bottom_left, ret[3]);
2207 
2208 #undef TRANSFORM_POINT
2209 
2210   graphene_quad_init (res, &ret[0], &ret[1], &ret[2], &ret[3]);
2211 }
2212 void
gsk_matrix_transform_bounds(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_rect_t * res)2213 gsk_matrix_transform_bounds (const graphene_matrix_t *m,
2214                              const graphene_rect_t   *r,
2215                              graphene_rect_t         *res)
2216 {
2217   graphene_quad_t q;
2218 
2219   gsk_matrix_transform_rect (m, r, &q);
2220   graphene_quad_bounds (&q, res);
2221 }
2222