Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gnome/gtk/svg/   (Gnome Linux Desktop Version 4.23.2©)  Datei vom 30.5.2026 mit Größe 49 kB image not shown  

Quelle  gtksvgtransform.c

  Sprache: C
 

/*
 * Copyright © 2025 Red Hat, Inc
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Matthias Clasen <mclasen@redhat.com>
 */


#include "config.h"

#include "gtksvgtransformprivate.h"
#include "gtksvgvalueprivate.h"
#include "gtksvgnumberprivate.h"
#include "gtksvgstringutilsprivate.h"
#include "gtksvgutilsprivate.h"

#include <tgmath.h>

typedef struct
{
  TransformType type;
  union {
    struct {
      double x, y;
    } translate, scale;
    struct {
      double angle;
      double x, y;
    } rotate;
    struct {
      double angle;
    } skew_x;
    struct {
      double angle;
    } skew_y;
    union {
      struct {
        double xx, yx, xy, yy, dx, dy;
      };
      double m[6];
    } matrix;
    struct {
      double x, y, z;
    } translate3d, scale3d;
    struct {
      double x, y;
    } skew;
    struct {
      double x, y, z;
      double angle;
    } rotate3d;
    struct {
      double depth;
    } perspective;
    struct {
      graphene_matrix_t m;
    } matrix3d;
  };
} PrimitiveTransform;

typedef struct
{
  SvgValue base;
  unsigned int n_transforms;
  PrimitiveTransform transforms[1];
} SvgTransform;

static unsigned int
svg_transform_size (unsigned int n)
{
  return sizeof (SvgTransform) + (n - 1) * sizeof (PrimitiveTransform);
}

static gboolean
primitive_transform_equal (const PrimitiveTransform *t0,
                           const PrimitiveTransform *t1)
{
  if (t0->type != t1->type)
    return FALSE;

  switch (t0->type)
    {
    case TRANSFORM_NONE:
      return TRUE;
    case TRANSFORM_TRANSLATE:
      return t0->translate.x == t1->translate.x &&
             t0->translate.y == t1->translate.y;
    case TRANSFORM_SCALE:
      return t0->scale.x == t1->scale.x &&
             t0->scale.y == t1->scale.y;
    case TRANSFORM_ROTATE:
      return t0->rotate.angle == t1->rotate.angle &&
             t0->rotate.x == t1->rotate.x &&
             t0->rotate.y == t1->rotate.y;
    case TRANSFORM_SKEW_X:
      return t0->skew_x.angle == t1->skew_x.angle;
    case TRANSFORM_SKEW_Y:
      return t0->skew_y.angle == t1->skew_y.angle;
    case TRANSFORM_MATRIX:
      return t0->matrix.xx == t1->matrix.xx && t0->matrix.yx == t1->matrix.yx &&
             t0->matrix.xy == t1->matrix.xy && t0->matrix.yy == t1->matrix.yy &&
             t0->matrix.dx == t1->matrix.dx && t0->matrix.dy == t1->matrix.dy;
    case TRANSFORM_TRANSLATE_3D:
      return t0->translate3d.x == t1->translate3d.x &&
             t0->translate3d.y == t1->translate3d.y &&
             t0->translate3d.z == t1->translate3d.z;
    case TRANSFORM_SCALE_3D:
      return t0->scale3d.x == t1->scale3d.x &&
             t0->scale3d.y == t1->scale3d.y &&
             t0->scale3d.z == t1->scale3d.z;
    case TRANSFORM_ROTATE_3D:
      return t0->rotate3d.angle == t1->rotate3d.angle &&
             t0->rotate3d.x == t1->rotate3d.x &&
             t0->rotate3d.y == t1->rotate3d.y &&
             t0->rotate3d.z == t1->rotate3d.z;
    case TRANSFORM_SKEW:
      return t0->skew.x == t1->skew.x &&
             t0->skew.y == t1->skew.y;
    case TRANSFORM_PERSPECTIVE:
      return t0->perspective.depth == t1->perspective.depth;
    case TRANSFORM_MATRIX_3D:
      return graphene_matrix_equal (&t0->matrix3d.m, &t1->matrix3d.m);
    default:
      g_assert_not_reached ();
    }
}

static gboolean
svg_transform_equal (const SvgValue *value0,
                     const SvgValue *value1)
{
  const SvgTransform *t0 = (const SvgTransform *) value0;
  const SvgTransform *t1 = (const SvgTransform *) value1;

  if (t0->n_transforms != t1->n_transforms)
    return FALSE;

  for (unsigned int i = 0; i < t1->n_transforms; i++)
    {
      if (!primitive_transform_equal (&t0->transforms[i], &t1->transforms[i]))
        return FALSE;
    }

  return TRUE;
}

static SvgValue *svg_transform_interpolate (const SvgValue    *v0,
                                            const SvgValue    *v1,
                                            SvgComputeContext *context,
                                            double             t);
static SvgValue *svg_transform_accumulate  (const SvgValue    *v0,
                                            const SvgValue    *v1,
                                            SvgComputeContext *context,
                                            int                n);
static void      svg_transform_print       (const SvgValue    *v,
                                            GString           *string);
static double    svg_transform_distance    (const SvgValue    *v0,
                                            const SvgValue    *v1);


static const SvgValueClass SVG_TRANSFORM_CLASS = {
  "SvgTransform",
  svg_value_default_free,
  svg_transform_equal,
  svg_transform_interpolate,
  svg_transform_accumulate,
  svg_transform_print,
  svg_transform_distance,
  svg_value_default_resolve,
};

static SvgTransform *
svg_transform_alloc (unsigned int n)
{
  SvgTransform *t;

  t = (SvgTransform *) svg_value_alloc (&SVG_TRANSFORM_CLASS, svg_transform_size (n));
  t->n_transforms = n;

  return t;
}

SvgValue *
svg_transform_new_none (void)
{
  static SvgTransform none = { { &SVG_TRANSFORM_CLASS, 0 }, 1, { { TRANSFORM_NONE, } }};
  return (SvgValue *) &none;
}

SvgValue *
svg_transform_new_translate (double x, double y)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_TRANSLATE;
  tf->transforms[0].translate.x = x;
  tf->transforms[0].translate.y = y;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_scale (double x, double y)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_SCALE;
  tf->transforms[0].scale.x = x;
  tf->transforms[0].scale.y = y;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_rotate (double angle, double x, double y)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_ROTATE;
  tf->transforms[0].rotate.angle = angle;
  tf->transforms[0].rotate.x = x;
  tf->transforms[0].rotate.y = y;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_skew_x (double angle)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_SKEW_X;
  tf->transforms[0].skew_x.angle = angle;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_skew_y (double angle)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_SKEW_Y;
  tf->transforms[0].skew_y.angle = angle;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_matrix (double params[6])
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_MATRIX;
  memcpy (tf->transforms[0].matrix.m, params, sizeof (double) * 6);
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_rotate_and_shift (double            angle,
                                    graphene_point_t *orig,
                                    graphene_point_t *final)
{
  SvgTransform *tf = svg_transform_alloc (3);
  tf->transforms[0].type = TRANSFORM_TRANSLATE;
  tf->transforms[0].translate.x = final->x;
  tf->transforms[0].translate.y = final->y;
  tf->transforms[1].type = TRANSFORM_ROTATE;
  tf->transforms[1].rotate.angle = angle;
  tf->transforms[1].rotate.x = 0;
  tf->transforms[1].rotate.y = 0;
  tf->transforms[2].type = TRANSFORM_TRANSLATE;
  tf->transforms[2].translate.x = - orig->x;
  tf->transforms[2].translate.y = - orig->y;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_translate_3d (double x, double y, double z)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_TRANSLATE_3D;
  tf->transforms[0].translate3d.x = x;
  tf->transforms[0].translate3d.y = y;
  tf->transforms[0].translate3d.z = z;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_scale_3d (double x, double y, double z)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_SCALE_3D;
  tf->transforms[0].scale3d.x = x;
  tf->transforms[0].scale3d.y = y;
  tf->transforms[0].scale3d.z = z;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_rotate_3d (double angle,
                             double x, double y, double z)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_ROTATE_3D;
  tf->transforms[0].rotate3d.angle = angle;
  tf->transforms[0].rotate3d.x = x;
  tf->transforms[0].rotate3d.y = y;
  tf->transforms[0].rotate3d.z = z;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_skew (double x, double y)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_SKEW;
  tf->transforms[0].skew.x = x;
  tf->transforms[0].skew.y = y;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_perspective (double depth)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_PERSPECTIVE;
  tf->transforms[0].perspective.depth = depth;
  return (SvgValue *) tf;
}

SvgValue *
svg_transform_new_matrix_3d (const graphene_matrix_t *m)
{
  SvgTransform *tf = svg_transform_alloc (1);
  tf->transforms[0].type = TRANSFORM_MATRIX_3D;
  graphene_matrix_init_from_matrix (&tf->transforms[0].matrix3d.m, m);
  return (SvgValue *) tf;
}

unsigned int
css_parser_parse_number (GtkCssParser *parser,
                         unsigned int  n,
                         gpointer      data)
{
  ParserNumber *val = data;
  double d;

  if (!gtk_css_parser_consume_number (parser, &d))
    return 0;

  val[n].value = d;
  val[n].unit = SVG_UNIT_NUMBER;

  return 1;
}

unsigned int
css_parser_parse_number_length (GtkCssParser *parser,
                                unsigned int  n,
                                gpointer      data)
{
  ParserNumber *val = data;
  const GtkCssToken *token;

  token = gtk_css_parser_get_token (parser);
  if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_NUMBER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_NUMBER))
    {
      val[n].value = ((const GtkCssNumberToken *) token)->number;
      val[n].unit = SVG_UNIT_NUMBER;
      gtk_css_parser_consume_token (parser);
      return 1;
    }

  if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_DIMENSION) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_DIMENSION))
    {
      for (unsigned int i = 0; i < SVG_UNIT_TURN; i++)
        {
          if (svg_unit_dimension (i) == SVG_DIMENSION_LENGTH &&
              strcmp (svg_unit_name (i), ((const GtkCssDimensionToken *) token)->dimension) == 0)
            {
              val[n].value = ((const GtkCssDimensionToken *) token)->value;
              val[n].unit = i;
              gtk_css_parser_consume_token (parser);
              return TRUE;
            }
        }
    }

  return 0;
}

unsigned int
css_parser_parse_number_angle (GtkCssParser *parser,
                               unsigned int  n,
                               gpointer      data)
{
  ParserNumber *val = data;
  const GtkCssToken *token;

  token = gtk_css_parser_get_token (parser);
  if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_NUMBER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_NUMBER))
    {
      val[n].value = ((const GtkCssNumberToken *) token)->number;
      val[n].unit = SVG_UNIT_NUMBER;
      gtk_css_parser_consume_token (parser);
      return 1;
    }

  if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_DIMENSION) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_DIMENSION))
    {
      for (unsigned int i = 0; i <= SVG_UNIT_TURN; i++)
        {
          if (svg_unit_dimension (i) == SVG_DIMENSION_ANGLE &&
              strcmp (svg_unit_name (i), ((const GtkCssDimensionToken *) token)->dimension) == 0)
            {
              val[n].value = ((const GtkCssDimensionToken *) token)->value;
              val[n].unit = i;
              gtk_css_parser_consume_token (parser);
              return TRUE;
            }
        }
    }

  return 0;
}

unsigned int
css_parser_parse_number_percentage (GtkCssParser *parser,
                                    unsigned int  n,
                                    gpointer      data)
{
  ParserNumber *val = data;
  const GtkCssToken *token;

  token = gtk_css_parser_get_token (parser);
  if (gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_INTEGER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_INTEGER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNED_NUMBER) ||
      gtk_css_token_is (token, GTK_CSS_TOKEN_SIGNLESS_NUMBER))
    {
      val[n].value = ((const GtkCssNumberToken *) token)->number;
      val[n].unit = SVG_UNIT_NUMBER;
      gtk_css_parser_consume_token (parser);
      return 1;
    }

  if (gtk_css_token_is (token, GTK_CSS_TOKEN_PERCENTAGE))
    {
      val[n].value = ((const GtkCssNumberToken *) token)->number;
      val[n].unit = SVG_UNIT_PERCENTAGE;
      gtk_css_parser_consume_token (parser);
      return 1;
    }

  return 0;
}

static gboolean
parse_transform_function (GtkCssParser *self,
                          unsigned int  min_args,
                          unsigned int  max_args,
                          double       *values,
                          unsigned int (*parse_one) (GtkCssParser *, unsigned int, gpointer))
{
  const GtkCssToken *token;
  gboolean result = FALSE;
  char func[64];
  unsigned int arg;
  ParserNumber *num;

  num = g_newa (ParserNumber, max_args);

  token = gtk_css_parser_get_token (self);
  g_return_val_if_fail (gtk_css_token_is (token, GTK_CSS_TOKEN_FUNCTION), FALSE);

  g_strlcpy (func, gtk_css_token_get_string (token), sizeof (func));
  gtk_css_parser_start_block (self);

  arg = 0;
  while (TRUE)
    {
      unsigned int parse_args;

      if (arg >= max_args)
        {
          gtk_css_parser_error_syntax (self, "Expected ')' at end of %s()", func);
          break;
        }

      parse_args = parse_one (self, arg, num);
      if (parse_args == 0)
        break;

      if (num[arg].unit == SVG_UNIT_DEG ||
          num[arg].unit == SVG_UNIT_RAD ||
          num[arg].unit == SVG_UNIT_GRAD ||
          num[arg].unit == SVG_UNIT_TURN)
        values[arg] = angle_to_deg (num[arg].value, num[arg].unit);
      else
        values[arg] = num[arg].value;
      arg += parse_args;
      token = gtk_css_parser_get_token (self);
      if (gtk_css_token_is (token, GTK_CSS_TOKEN_EOF))
        {
          if (arg < min_args)
            {
              gtk_css_parser_error_syntax (self, "%s() requires at least %u arguments", func, min_args);
              break;
            }
          else
            {
              result = TRUE;
              break;
            }
        }
      else if (gtk_css_token_is (token, GTK_CSS_TOKEN_COMMA))
        {
          if (arg >= max_args)
            {
              gtk_css_parser_error_syntax (self, "Expected ')' at end of %s()", func);
              break;
            }

          gtk_css_parser_consume_token (self);
          continue;
        }
      else if (!gtk_css_parser_has_number (self))
        {
          gtk_css_parser_error_syntax (self, "Unexpected data at end of %s()", func);
          break;
        }
    }

  gtk_css_parser_end_block (self);

  return result;
}

SvgValue *
svg_transform_parse_css (GtkCssParser *parser)
{
  SvgTransform *tf;
  GArray *array;

  if (gtk_css_parser_try_ident (parser, "none"))
    return svg_transform_new_none ();

  array = g_array_new (FALSEFALSEsizeof (PrimitiveTransform));

    while (TRUE)
    {
      PrimitiveTransform transform;

      memset (&transform, 0sizeof (PrimitiveTransform));

      if (gtk_css_parser_has_function (parser, "rotate"))
        {
           double values[3] = { 000 };

          if (!parse_transform_function (parser, 13, values, css_parser_parse_number_angle))
            goto fail;

          transform.type = TRANSFORM_ROTATE;
          transform.rotate.angle = values[0];
          transform.rotate.x = values[1];
          transform.rotate.y = values[2];
        }
      else if (gtk_css_parser_has_function (parser, "scale"))
        {
          double values[2] = { 00 };

          if (!parse_transform_function (parser, 12, values, css_parser_parse_number))
            goto fail;

          transform.type = TRANSFORM_SCALE;
          transform.scale.x = values[0];
          if (values[1])
            transform.scale.y = values[1];
          else
            transform.scale.y = transform.scale.x;
        }
      else if (gtk_css_parser_has_function (parser, "translate"))
        {
          double values[2] = { 00 };

          if (!parse_transform_function (parser, 12, values, css_parser_parse_number_length))
            goto fail;

          transform.type = TRANSFORM_TRANSLATE;
          transform.translate.x = values[0];
          if (values[1])
            transform.translate.y = values[1];
          else
            transform.translate.y = 0;
        }
      else if (gtk_css_parser_has_function (parser, "skewX"))
        {
          double values[1];

          if (!parse_transform_function (parser, 11, values, css_parser_parse_number_angle))
            goto fail;

          transform.type = TRANSFORM_SKEW_X;
          transform.skew_x.angle = values[0];
        }
      else if (gtk_css_parser_has_function (parser, "skewY"))
        {
          double values[1];

          if (!parse_transform_function (parser, 11, values, css_parser_parse_number_angle))
            goto fail;

          transform.type = TRANSFORM_SKEW_Y;
          transform.skew_y.angle = values[0];
        }
      else if (gtk_css_parser_has_function (parser, "matrix"))
        {
          double values[6];

          if (!parse_transform_function (parser, 66, values, css_parser_parse_number))
            goto fail;

          transform.type = TRANSFORM_MATRIX;
          memcpy (transform.matrix.m, values, sizeof (double) * 6);
        }
      else if (gtk_css_parser_has_function (parser, "translate3d"))
        {
          double values[3];

          if (!parse_transform_function (parser, 33, values, css_parser_parse_number_length))
            goto fail;

          transform.type = TRANSFORM_TRANSLATE_3D;
          transform.translate3d.x = values[0];
          transform.translate3d.y = values[1];
          transform.translate3d.z = values[2];
        }
      else if (gtk_css_parser_has_function (parser, "scale3d"))
        {
          double values[3];

          if (!parse_transform_function (parser, 33, values, css_parser_parse_number))
            goto fail;

          transform.type = TRANSFORM_SCALE_3D;
          transform.scale3d.x = values[0];
          transform.scale3d.y = values[1];
          transform.scale3d.z = values[2];
        }
      else if (gtk_css_parser_has_function (parser, "rotate3d"))
        {
          double values[4];

          if (!parse_transform_function (parser, 44, values, css_parser_parse_number_angle))
            goto fail;

          transform.type = TRANSFORM_ROTATE_3D;
          transform.rotate3d.x = values[0];
          transform.rotate3d.y = values[1];
          transform.rotate3d.z = values[2];
          transform.rotate3d.angle = values[3];
        }
      else if (gtk_css_parser_has_function (parser, "skew"))
        {
          double values[2];

          if (!parse_transform_function (parser, 22, values, css_parser_parse_number_angle))
            goto fail;

          transform.type = TRANSFORM_SKEW;
          transform.skew.x = values[0];
          transform.skew.y = values[1];
        }
      else if (gtk_css_parser_has_function (parser, "perspective"))
        {
          double values[1];

          if (!parse_transform_function (parser, 11, values, css_parser_parse_number_length))
            goto fail;

          transform.type = TRANSFORM_PERSPECTIVE;
          transform.perspective.depth = values[0];
        }
      else if (gtk_css_parser_has_function (parser, "matrix3d"))
        {
          double values[16];
          float floats[16];

          if (!parse_transform_function (parser, 1616, values, css_parser_parse_number))
            goto fail;

          transform.type = TRANSFORM_MATRIX_3D;
          for (unsigned int i = 0; i < 16; i++)
            floats[i] = values[i];
          graphene_matrix_init_from_float (&transform.matrix3d.m, floats);
        }
      else
        {
          break;
        }

      g_array_append_val (array, transform);

      if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA))
        gtk_css_parser_skip (parser);
    }

  if (array->len == 0)
    goto fail;

  tf = svg_transform_alloc (array->len);
  memcpy (tf->transforms, array->data, sizeof (PrimitiveTransform) * array->len);

  g_array_free (array, TRUE);

  return (SvgValue *) tf;

fail:
  g_array_free (array, TRUE);
  gtk_css_parser_error_syntax (parser, "Expected a transform");
  return NULL;
}

SvgValue *
svg_transform_parse (const char *value)
{
  GtkCssParser *parser = parser_new_for_string (value);
  SvgValue *tf;

  tf = svg_transform_parse_css (parser);

  if (!gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF))
    g_clear_pointer (&tf, svg_value_unref);

  gtk_css_parser_unref (parser);

  return tf;
}

SvgValue *
primitive_transform_parse (TransformType  type,
                           const char    *string)
{
  GtkCssParser *parser = parser_new_for_string (string);
  double angle, x, y;
  SvgUnit u;
  SvgValue *value = NULL;

  gtk_css_parser_skip_whitespace (parser);
  switch ((unsigned int) type)
    {
    case TRANSFORM_TRANSLATE:
      if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &x, &u))
        {
          skip_whitespace_and_optional_comma (parser);
          if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &y, &u))
            value = svg_transform_new_translate (x, y);
          else
            value = svg_transform_new_translate (x, 0);
        }
      break;

    case TRANSFORM_SCALE:
      if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &x, &u))
        {
          skip_whitespace_and_optional_comma (parser);
          if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &y, &u))
            value = svg_transform_new_scale (x, y);
          else
            value = svg_transform_new_scale (x, x);
        }
      break;

    case TRANSFORM_ROTATE:
      if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &angle, &u))
        {
          skip_whitespace_and_optional_comma (parser);
          if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &x, &u))
            {
              skip_whitespace_and_optional_comma (parser);
              if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &y, &u))
                value = svg_transform_new_rotate (angle, x, y);
              else
                value = svg_transform_new_rotate (angle, x, 0);
            }
          else
            value = svg_transform_new_rotate (angle, 00);
        }
      break;

    case TRANSFORM_SKEW_X:
      if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &angle, &u))
        value = svg_transform_new_skew_x (angle);
      break;

    case TRANSFORM_SKEW_Y:
      if (svg_number_parse2 (parser, -DBL_MAX, DBL_MAX, SVG_PARSE_NUMBER, &angle, &u))
        value = svg_transform_new_skew_y (angle);
      break;

    default:
      g_assert_not_reached ();
    }

  gtk_css_parser_unref (parser);
  return value;
}

void
svg_primitive_transform_print (const SvgValue *value,
                               GString        *s)
{
  const SvgTransform *tf = (const SvgTransform *) value;

  g_assert (tf->n_transforms == 1);

  switch (tf->transforms[0].type)
    {
    case TRANSFORM_TRANSLATE:
      string_append_double (s, "", tf->transforms[0].translate.x);
      string_append_double (s, " ", tf->transforms[0].translate.y);
      break;

    case TRANSFORM_SCALE:
      string_append_double (s, "", tf->transforms[0].scale.x);
      string_append_double (s, " ", tf->transforms[0].scale.y);
      break;

    case TRANSFORM_ROTATE:
      string_append_double (s, "", tf->transforms[0].rotate.angle);
      string_append_double (s, " ", tf->transforms[0].rotate.x);
      string_append_double (s, " ", tf->transforms[0].rotate.y);
      break;

    case TRANSFORM_SKEW_X:
      string_append_double (s, "", tf->transforms[0].skew_x.angle);
      break;

    case TRANSFORM_SKEW_Y:
      string_append_double (s, "", tf->transforms[0].skew_y.angle);
      break;

    case TRANSFORM_MATRIX:
    case TRANSFORM_TRANSLATE_3D:
    case TRANSFORM_SCALE_3D:
    case TRANSFORM_ROTATE_3D:
    case TRANSFORM_SKEW:
    case TRANSFORM_PERSPECTIVE:
    case TRANSFORM_MATRIX_3D:
    case TRANSFORM_NONE:
    default:
      g_assert_not_reached ();
    }
}

static void
svg_transform_print (const SvgValue *value,
                     GString        *s)
{
  const SvgTransform *tf = (const SvgTransform *) value;

  for (unsigned int i = 0; i < tf->n_transforms; i++)
    {
      const PrimitiveTransform *t = &tf->transforms[i];

      if (i > 0)
        g_string_append_c (s, ' ');

      switch (t->type)
        {
        case TRANSFORM_NONE:
          g_string_append (s, "none");
          break;
        case TRANSFORM_TRANSLATE:
          string_append_double (s, "translate(", t->translate.x);
          string_append_double (s, ", ", t->translate.y);
          g_string_append (s, ")");
          break;
        case TRANSFORM_SCALE:
          string_append_double (s, "scale(", t->scale.x);
          string_append_double (s, ", ", t->scale.y);
          g_string_append (s, ")");
          break;
        case TRANSFORM_ROTATE:
          string_append_double (s, "rotate(", t->rotate.angle);
          string_append_double (s, ", ", t->rotate.x);
          string_append_double (s, ", ", t->rotate.y);
          g_string_append (s, ")");
          break;
        case TRANSFORM_SKEW_X:
          string_append_double (s, "skewX(",  t->skew_x.angle);
          g_string_append (s, ")");
          break;
        case TRANSFORM_SKEW_Y:
          string_append_double (s, "skewY(", t->skew_y.angle);
          g_string_append (s, ")");
          break;
        case TRANSFORM_MATRIX:
          string_append_double (s, "matrix(", t->matrix.xx);
          string_append_double (s, ", ", t->matrix.yx);
          string_append_double (s, ", ", t->matrix.xy);
          string_append_double (s, ", ", t->matrix.yy);
          string_append_double (s, ", ", t->matrix.dx);
          string_append_double (s, ", ", t->matrix.dy);
          g_string_append (s, ")");
          break;
        case TRANSFORM_TRANSLATE_3D:
          string_append_double (s, "translate3d(", t->translate3d.x);
          string_append_double (s, ", ", t->translate3d.y);
          string_append_double (s, ", ", t->translate3d.z);
          g_string_append (s, ")");
          break;
        case TRANSFORM_SCALE_3D:
          string_append_double (s, "scale3d(", t->scale3d.x);
          string_append_double (s, ", ", t->scale3d.y);
          string_append_double (s, ", ", t->scale3d.z);
          g_string_append (s, ")");
          break;
        case TRANSFORM_ROTATE_3D:
          string_append_double (s, "rotate3d(", t->rotate3d.x);
          string_append_double (s, ", ", t->rotate3d.y);
          string_append_double (s, ", ", t->rotate3d.z);
          string_append_double (s, ", ", t->rotate3d.angle);
          g_string_append (s, ")");
          break;
        case TRANSFORM_PERSPECTIVE:
          string_append_double (s, "perspective(", t->perspective.depth);
          g_string_append (s, ")");
          break;
        case TRANSFORM_SKEW:
          string_append_double (s, "skew(", t->skew.x);
          string_append_double (s, ", ", t->skew.y);
          g_string_append (s, ")");
          break;
        case TRANSFORM_MATRIX_3D:
          {
            float v[16];
            graphene_matrix_to_float (&t->matrix3d.m, v);
            string_append_double (s, "matrix(", v[0]);
            for (unsigned int j = 1; j < 16; j++)
              {
                if (j % 4 == 0)
                  string_append_double (s, ",\n       ", v[j]);
                else
                  string_append_double (s, ", ", v[j]);
              }
            g_string_append (s, ")");
          }
          break;
        default:
          g_assert_not_reached ();
        }
    }
}

static void
interpolate_matrices (double       t,
                      const double m0[6],
                      const double m1[6],
                      double       m[6])
{
  graphene_matrix_t mat0, mat1, res;

  graphene_matrix_init_from_2d (&mat0, m0[0], m0[1], m0[2], m0[3], m0[4], m0[5]);
  graphene_matrix_init_from_2d (&mat1, m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
  graphene_matrix_interpolate (&mat0, &mat1, t, &res);
  graphene_matrix_to_2d (&res, &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]);
}

static GskTransform *
primitive_transform_apply (const PrimitiveTransform *transform,
                           GskTransform             *next)
{
  switch (transform->type)
    {
    case TRANSFORM_NONE:
      return next;

    case TRANSFORM_TRANSLATE:
      return gsk_transform_translate (next,
                                      &GRAPHENE_POINT_INIT (
                                        transform->translate.x,
                                        transform->translate.y
                                      ));

    case TRANSFORM_SCALE:
      return gsk_transform_scale (next,
                                  transform->scale.x,
                                  transform->scale.y);

    case TRANSFORM_ROTATE:
      return gsk_transform_translate (
                  gsk_transform_rotate (
                      gsk_transform_translate (next,
                                               &GRAPHENE_POINT_INIT (
                                                 transform->rotate.x,
                                                 transform->rotate.y
                                               )),
                      transform->rotate.angle),
                  &GRAPHENE_POINT_INIT (
                    -transform->rotate.x,
                    -transform->rotate.y
                  ));

    case TRANSFORM_SKEW_X:
      return gsk_transform_skew (next, transform->skew_x.angle, 0);

    case TRANSFORM_SKEW_Y:
      return gsk_transform_skew (next, 0, transform->skew_y.angle);

    case TRANSFORM_MATRIX:
      return gsk_transform_matrix_2d (next,
                                      transform->matrix.xx, transform->matrix.yx,
                                      transform->matrix.xy, transform->matrix.yy,
                                      transform->matrix.dx, transform->matrix.dy);

    case TRANSFORM_TRANSLATE_3D:
      return gsk_transform_translate_3d (next,
                                         &GRAPHENE_POINT3D_INIT (
                                           transform->translate3d.x,
                                           transform->translate3d.y,
                                           transform->translate3d.z));
    case TRANSFORM_SCALE_3D:
      return gsk_transform_scale_3d (next,
                                     transform->scale3d.x,
                                     transform->scale3d.y,
                                     transform->scale3d.z);

    case TRANSFORM_ROTATE_3D:
      {
        graphene_vec3_t v3;
        return gsk_transform_rotate_3d (next,
                                        transform->rotate3d.angle,
                                        graphene_vec3_init (&v3,
                                          transform->rotate3d.x,
                                          transform->rotate3d.y,
                                          transform->rotate3d.z));
      }

    case TRANSFORM_SKEW:
      return gsk_transform_skew (next, transform->skew.x, transform->skew.y);

    case TRANSFORM_PERSPECTIVE:
      return gsk_transform_perspective (next, transform->perspective.depth);

    case TRANSFORM_MATRIX_3D:
      return gsk_transform_matrix (next, &transform->matrix3d.m);

    default:
      g_assert_not_reached ();
    }
}

static SvgValue *
svg_transform_interpolate (const SvgValue    *value0,
                           const SvgValue    *value1,
                           SvgComputeContext *context,
                           double             t)
{
  static const PrimitiveTransform identity[] = {
    { .type = TRANSFORM_TRANSLATE, .translate = { 00 } },
    { .type = TRANSFORM_SCALE, .scale = { 11 } },
    { .type = TRANSFORM_ROTATE, .rotate = { 000 } },
    { .type = TRANSFORM_SKEW_X, .skew_x = { 0 } },
    { .type = TRANSFORM_SKEW_Y, .skew_y = { 0 } },
    { .type = TRANSFORM_MATRIX, .matrix = { .m = { 100100 } } },
    { .type = TRANSFORM_TRANSLATE_3D, .translate3d = { 000 } },
    { .type = TRANSFORM_SCALE_3D, .scale3d = { 111 } },
    { .type = TRANSFORM_ROTATE_3D, .rotate3d = { 0100 } },
    { .type = TRANSFORM_SKEW, .skew = { 00 } },
  };
  static PrimitiveTransform id_mat3 = { 0, };
  const SvgTransform *tf0 = (const SvgTransform *) value0;
  const SvgTransform *tf1 = (const SvgTransform *) value1;
  SvgTransform *tf;

  if (id_mat3.type == 0)
    {
      id_mat3.type = TRANSFORM_MATRIX_3D;
      graphene_matrix_init_identity (&id_mat3.matrix3d.m);
    }

  tf = svg_transform_alloc (MAX (tf0->n_transforms, tf1->n_transforms));

  for (unsigned int i = 0; i < tf->n_transforms; i++)
    {
      const PrimitiveTransform *p0;
      const PrimitiveTransform *p1;
      PrimitiveTransform *p = &tf->transforms[i];

      if (i < tf0->n_transforms)
        p0 = &tf0->transforms[i];
      else if (tf1->transforms[i].type == TRANSFORM_PERSPECTIVE ||
               tf1->transforms[i].type == TRANSFORM_MATRIX_3D)
        p0 = &id_mat3;
      else
        p0 = &identity[tf1->transforms[i].type];

      if (i < tf1->n_transforms)
        p1 = &tf1->transforms[i];
      else if (tf0->transforms[i].type == TRANSFORM_PERSPECTIVE ||
               tf0->transforms[i].type == TRANSFORM_MATRIX_3D)
        p1 = &id_mat3;
      else
        p1 = &identity[tf0->transforms[i].type];

      if (p0->type != p1->type)
        {
          GskTransform *transform = NULL;
          graphene_matrix_t mat1, mat2, res;

          transform = NULL;
          for (unsigned int j = i; j < tf0->n_transforms; j++)
            transform = primitive_transform_apply (&tf0->transforms[j], transform);
          gsk_transform_to_matrix (transform, &mat1);
          g_clear_pointer (&transform, gsk_transform_unref);

          for (unsigned int j = i; j < tf1->n_transforms; j++)
            transform = primitive_transform_apply (&tf1->transforms[j], transform);
          gsk_transform_to_matrix (transform, &mat2);
          gsk_transform_unref (transform);

          graphene_matrix_interpolate (&mat1, &mat2, t, &res);

          p->type = TRANSFORM_MATRIX;
          graphene_matrix_to_2d (&res,
                                 &p->matrix.m[0], &p->matrix.m[1],
                                 &p->matrix.m[2], &p->matrix.m[3],
                                 &p->matrix.m[4], &p->matrix.m[5]);
          tf->n_transforms = i + 1;
          break;
        }

      p->type = p0->type;
      switch (p0->type)
        {
        case TRANSFORM_NONE:
          break;
        case TRANSFORM_TRANSLATE:
          p->translate.x = lerp (t, p0->translate.x, p1->translate.x);
          p->translate.y = lerp (t, p0->translate.y, p1->translate.y);
          break;
        case TRANSFORM_SCALE:
          p->scale.x = lerp (t, p0->scale.x, p1->scale.x);
          p->scale.y = lerp (t, p0->scale.y, p1->scale.y);
          break;
        case TRANSFORM_ROTATE:
          p->rotate.angle = lerp (t, p0->rotate.angle, p1->rotate.angle);
          p->rotate.x = lerp (t, p0->rotate.x, p1->rotate.x);
          p->rotate.y = lerp (t, p0->rotate.y, p1->rotate.y);
          break;
        case TRANSFORM_SKEW_X:
          p->skew_x.angle = lerp (t, p0->skew_x.angle, p1->skew_x.angle);
          break;
        case TRANSFORM_SKEW_Y:
          p->skew_y.angle = lerp (t, p0->skew_y.angle, p1->skew_y.angle);
          break;
        case TRANSFORM_MATRIX:
          interpolate_matrices (t, p0->matrix.m, p1->matrix.m, p->matrix.m);
          break;
        case TRANSFORM_TRANSLATE_3D:
          p->translate3d.x = lerp (t, p0->translate3d.x, p1->translate3d.x);
          p->translate3d.y = lerp (t, p0->translate3d.y, p1->translate3d.y);
          p->translate3d.z = lerp (t, p0->translate3d.z, p1->translate3d.z);
          break;
        case TRANSFORM_SCALE_3D:
          p->scale3d.x = lerp (t, p0->scale3d.x, p1->scale3d.x);
          p->scale3d.y = lerp (t, p0->scale3d.y, p1->scale3d.y);
          p->scale3d.z = lerp (t, p0->scale3d.z, p1->scale3d.z);
          break;
        case TRANSFORM_ROTATE_3D:
          p->rotate3d.angle = lerp (t, p0->rotate3d.angle, p1->rotate3d.angle);
          p->rotate3d.x = lerp (t, p0->rotate3d.x, p1->rotate3d.x);
          p->rotate3d.y = lerp (t, p0->rotate3d.y, p1->rotate3d.y);
          p->rotate3d.z = lerp (t, p0->rotate3d.z, p1->rotate3d.z);
          break;
        case TRANSFORM_SKEW:
          p->skew.x = lerp (t, p0->skew.x, p1->skew.x);
          p->skew.y = lerp (t, p0->skew.y, p1->skew.y);
          break;
        case TRANSFORM_PERSPECTIVE:
          p->perspective.depth = lerp (t, p0->perspective.depth, p1->perspective.depth);
          break;
        case TRANSFORM_MATRIX_3D:
          graphene_matrix_interpolate (&p0->matrix3d.m, &p1->matrix3d.m, t, &p->matrix3d.m);
          break;
        default:
          g_assert_not_reached ();
        }
    }

  return (SvgValue *) tf;
}

static SvgValue *
svg_transform_accumulate (const SvgValue    *value0,
                          const SvgValue    *value1,
                          SvgComputeContext *context,
                          int                n)
{
  const SvgTransform *tf0 = (const SvgTransform *) value0;
  const SvgTransform *tf1 = (const SvgTransform *) value1;
  SvgTransform *tf;
  int n0;

  if (tf1->n_transforms == 1 && tf1->transforms[0].type == TRANSFORM_NONE)
    return svg_value_ref ((SvgValue *) value0);

  if (tf0->n_transforms == 1 && tf0->transforms[0].type == TRANSFORM_NONE)
    {
      if (n == 1)
        return svg_value_ref ((SvgValue *) value1);

      n0 = 0;
    }
  else
    n0 = tf0->n_transforms;

  /* special-case this one */
  if (tf1->n_transforms == 1 &&
      tf1->transforms[0].type != TRANSFORM_MATRIX &&
      tf1->transforms[0].type != TRANSFORM_MATRIX_3D &&
      tf1->transforms[0].type != TRANSFORM_PERSPECTIVE)
    {
      PrimitiveTransform *p;

      tf = svg_transform_alloc (n0 + 1);
      p = &tf->transforms[0];
      memcpy (p, tf1->transforms, sizeof (PrimitiveTransform));

      switch (p->type)
        {
        case TRANSFORM_NONE:
          break;
        case TRANSFORM_TRANSLATE:
          p->translate.x *= n;
          p->translate.y *= n;
          break;
        case TRANSFORM_SCALE:
          p->scale.x = pow (p->scale.x, (double) n);
          p->scale.y = pow (p->scale.y, (double) n);
          break;
        case TRANSFORM_ROTATE:
          p->rotate.angle *= n;
          break;
        case TRANSFORM_SKEW_X:
          p->skew_x.angle *= n;
          break;
        case TRANSFORM_SKEW_Y:
          p->skew_y.angle *= n;
          break;
        case TRANSFORM_TRANSLATE_3D:
          p->translate3d.x *= n;
          p->translate3d.y *= n;
          p->translate3d.z *= n;
          break;
        case TRANSFORM_SCALE_3D:
          p->scale3d.x = pow (p->scale3d.x, (double) n);
          p->scale3d.y = pow (p->scale3d.y, (double) n);
          p->scale3d.z = pow (p->scale3d.z, (double) n);
          break;
        case TRANSFORM_ROTATE_3D:
          p->rotate3d.angle *= n;
          break;
        case TRANSFORM_SKEW:
          p->skew.x *= n; /* No idea if this is correct */
          p->skew.y *= n;
          break;
        case TRANSFORM_PERSPECTIVE:
        case TRANSFORM_MATRIX:
        case TRANSFORM_MATRIX_3D:
        default:
          g_assert_not_reached ();
        }

      if (n0 > 0)
        memcpy (tf->transforms + 1,
                tf0->transforms,
                n0 * sizeof (PrimitiveTransform));

    }
  else
    {
      /* For the general case, simply concatenate all the transforms */
      tf = svg_transform_alloc (n0 + n * tf1->n_transforms);
      for (unsigned int i = 0; i < n; i++)
        memcpy (&tf->transforms[i * tf1->n_transforms],
                tf1->transforms,
                tf1->n_transforms * sizeof (PrimitiveTransform));

      if (n0 > 0)
        memcpy (&tf->transforms[n * tf1->n_transforms],
                tf0->transforms,
                n0 * sizeof (PrimitiveTransform));
    }

  return (SvgValue *) tf;
}

GskTransform *
svg_transform_get_gsk (const SvgValue *value)
{
  const SvgTransform *tf = (const SvgTransform *) value;
  GskTransform *t = NULL;

  g_assert (value->class == &SVG_TRANSFORM_CLASS);

  if (!tf)
    return NULL;

  for (unsigned int i = 0; i < tf->n_transforms; i++)
    t = primitive_transform_apply (&tf->transforms[i], t);

  return t;
}

static double
hypot3 (double x, double y, double z)
{
  return sqrt (x * x + y * y + z * z);
}

static double
svg_transform_distance (const SvgValue *value0,
                        const SvgValue *value1)
{
  const SvgTransform *tf0 = (const SvgTransform *) value0;
  const SvgTransform *tf1 = (const SvgTransform *) value1;
  const PrimitiveTransform *p0 = &tf0->transforms[0];
  const PrimitiveTransform *p1 = &tf1->transforms[0];

  if (tf0->n_transforms > 1 || tf1->n_transforms > 1)
    {
      g_warning ("Can't determine distance between complex transforms");
      return 1;
    }

  if (p0->type != p1->type)
    {
      g_warning ("Can't determine distance between different "
                 "primitive transform types");
      return 1;
    }

  switch (p0->type)
    {
    case TRANSFORM_NONE:
      return 0;
    case TRANSFORM_TRANSLATE:
      return hypot (p0->translate.x - p1->translate.x,
                    p0->translate.y - p1->translate.y);
      break;
    case TRANSFORM_SCALE:
      return hypot (p0->scale.x - p1->scale.x,
                    p0->scale.y - p1->scale.y);
          break;
    case TRANSFORM_ROTATE:
      return hypot (p0->rotate.angle - p1->rotate.angle, 0);
    case TRANSFORM_SKEW_X:
    case TRANSFORM_SKEW_Y:
    case TRANSFORM_MATRIX:
    case TRANSFORM_MATRIX_3D:
    case TRANSFORM_SKEW:
    case TRANSFORM_PERSPECTIVE:
      g_warning ("Can't determine distance between these "
                 "primitive transforms");
      return 1;
    case TRANSFORM_TRANSLATE_3D:
      return hypot3 (p0->translate3d.x - p1->translate3d.x,
                     p0->translate3d.y - p1->translate3d.y,
                     p0->translate3d.z - p1->translate3d.z);
      break;
    case TRANSFORM_SCALE_3D:
      return hypot3 (p0->scale3d.x - p1->scale3d.x,
                     p0->scale3d.y - p1->scale3d.y,
                     p0->scale3d.z - p1->scale3d.z);
          break;
    case TRANSFORM_ROTATE_3D:
      if (p0->rotate3d.x == p1->rotate3d.x &&
          p0->rotate3d.y == p1->rotate3d.y &&
          p0->rotate3d.z == p1->rotate3d.z)
        {
          return hypot (p0->rotate3d.angle - p1->rotate3d.angle, 0);
        }
      else
        {
          g_warning ("Can't determine distance between these "
                     "primitive transforms");
          return 1;
        }
    default:
      g_assert_not_reached ();
    }
}

TransformType
svg_transform_get_type (const SvgValue *value,
                        unsigned int    pos)
{
  const SvgTransform *tf = (const SvgTransform *) value;

  g_assert (value->class == &SVG_TRANSFORM_CLASS);
  g_assert (pos < tf->n_transforms);

  return tf->transforms[pos].type;
}

TransformType
svg_transform_get_primitive (const SvgValue *value,
                             unsigned int    pos,
                             double          params[6])
{
  const SvgTransform *tf = (const SvgTransform *) value;

  g_assert (value->class == &SVG_TRANSFORM_CLASS);
  g_assert (pos < tf->n_transforms);

  switch (tf->transforms[pos].type)
    {
    case TRANSFORM_NONE:
      break;
    case TRANSFORM_TRANSLATE:
      params[0] = tf->transforms[pos].translate.x;
      params[1] = tf->transforms[pos].translate.y;
      break;
    case TRANSFORM_SCALE:
      params[0] = tf->transforms[pos].scale.x;
      params[1] = tf->transforms[pos].scale.y;
      break;
    case TRANSFORM_ROTATE:
      params[0] = tf->transforms[pos].rotate.angle;
      params[1] = tf->transforms[pos].rotate.x;
      params[2] = tf->transforms[pos].rotate.y;
      break;
    case TRANSFORM_SKEW_X:
      params[0] = tf->transforms[pos].skew_x.angle;
      break;
    case TRANSFORM_SKEW_Y:
      params[0] = tf->transforms[pos].skew_y.angle;
      break;
    case TRANSFORM_MATRIX:
      memcpy (params, tf->transforms[pos].matrix.m, sizeof (double) * 6);
      break;
    case TRANSFORM_TRANSLATE_3D:
      params[0] = tf->transforms[pos].translate3d.x;
      params[1] = tf->transforms[pos].translate3d.y;
      params[2] = tf->transforms[pos].translate3d.z;
      break;
    case TRANSFORM_SCALE_3D:
      params[0] = tf->transforms[pos].scale3d.x;
      params[1] = tf->transforms[pos].scale3d.y;
      params[2] = tf->transforms[pos].scale3d.z;
      break;
    case TRANSFORM_ROTATE_3D:
      params[0] = tf->transforms[pos].rotate3d.angle;
      params[1] = tf->transforms[pos].rotate3d.x;
      params[2] = tf->transforms[pos].rotate3d.y;
      params[3] = tf->transforms[pos].rotate3d.z;
      break;
    case TRANSFORM_SKEW:
      params[0] = tf->transforms[pos].skew.x;
      params[1] = tf->transforms[pos].skew.y;
      break;
    case TRANSFORM_PERSPECTIVE:
      params[0] = tf->transforms[pos].perspective.depth;
      break;
    case TRANSFORM_MATRIX_3D:
    default:
      g_assert_not_reached ();
    }

  return tf->transforms[pos].type;
}

gboolean
svg_transform_is_none (const SvgValue *value)
{
  const SvgTransform *tf = (const SvgTransform *) value;

  g_assert (value->class == &SVG_TRANSFORM_CLASS);

  return tf->transforms[0].type == TRANSFORM_NONE;
}

gboolean
svg_value_is_transform (const SvgValue *value)
{
   return value->class == &SVG_TRANSFORM_CLASS;
}

unsigned int
svg_transform_get_length (const SvgValue *value)
{
  const SvgTransform *tf = (const SvgTransform *) value;

  g_assert (value->class == &SVG_TRANSFORM_CLASS);

  return tf->n_transforms;
}

SvgValue *
svg_transform_get_transform (const SvgValue *value,
                             unsigned int    pos)
{
  SvgTransform *tf = (SvgTransform *) value;

  g_assert (value->class == &SVG_TRANSFORM_CLASS);

  switch (tf->transforms[pos].type)
    {
    case TRANSFORM_NONE:
      return svg_transform_new_none ();
    case TRANSFORM_TRANSLATE:
      return svg_transform_new_translate (tf->transforms[pos].translate.x,
                                          tf->transforms[pos].translate.y);
    case TRANSFORM_SCALE:
      return svg_transform_new_scale (tf->transforms[pos].scale.x,
                                      tf->transforms[pos].scale.y);
    case TRANSFORM_ROTATE:
      return svg_transform_new_rotate (tf->transforms[pos].rotate.angle,
                                       tf->transforms[pos].rotate.x,
                                       tf->transforms[pos].rotate.y);
    case TRANSFORM_SKEW_X:
      return svg_transform_new_skew_x (tf->transforms[pos].skew_x.angle);
    case TRANSFORM_SKEW_Y:
      return svg_transform_new_skew_y (tf->transforms[pos].skew_y.angle);
    case TRANSFORM_MATRIX:
      return svg_transform_new_matrix (tf->transforms[pos].matrix.m);
    case TRANSFORM_TRANSLATE_3D:
      return svg_transform_new_translate_3d (tf->transforms[pos].translate3d.x,
                                             tf->transforms[pos].translate3d.y,
                                             tf->transforms[pos].translate3d.z);
    case TRANSFORM_SCALE_3D:
      return svg_transform_new_scale_3d (tf->transforms[pos].scale3d.x,
                                         tf->transforms[pos].scale3d.y,
                                         tf->transforms[pos].scale3d.z);
    case TRANSFORM_ROTATE_3D:
      return svg_transform_new_rotate_3d (tf->transforms[pos].rotate3d.angle,
                                          tf->transforms[pos].rotate3d.x,
                                          tf->transforms[pos].rotate3d.y,
                                          tf->transforms[pos].rotate3d.z);
    case TRANSFORM_SKEW:
      return svg_transform_new_skew (tf->transforms[pos].skew.x,
                                     tf->transforms[pos].skew.y);
    case TRANSFORM_PERSPECTIVE:
      return svg_transform_new_perspective (tf->transforms[pos].perspective.depth);
    case TRANSFORM_MATRIX_3D:
      return svg_transform_new_matrix_3d (&tf->transforms[pos].matrix3d.m);

    default:
      g_assert_not_reached ();
    }
}

Messung V0.5 in Prozent
C=98 H=95 G=96

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.