/*
* 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 "gtksvgpathdataprivate.h"
#include "gsk/gskpathprivate.h"
#include "gtksvgutilsprivate.h"
#include "gtksvgstringutilsprivate.h"
/* We have our own path data structure here instead of
* just relying on GskPath for two reasons :
* - SVG considers A to be a primitive and requires
* it to be interpolated as such
* - SVG does not have conics ( O )
*/
#define SVG_PATH_ARC 22
typedef struct
{
unsigned int op;
union {
struct {
graphene_point_t pts[4 ];
} seg;
struct {
float rx;
float ry;
float x_axis_rotation;
gboolean large_arc;
gboolean positive_sweep;
float x;
float y;
} arc;
};
} SvgPathOp;
#define GDK_ARRAY_NAME svg_path_ops
#define GDK_ARRAY_TYPE_NAME SvgPathOps
#define GDK_ARRAY_ELEMENT_TYPE SvgPathOp
#define GDK_ARRAY_PREALLOC 112
#define GDK_ARRAY_NO_MEMSET 1
#include "gdk/gdkarrayimpl.c"
/* We want to choose the array preallocations above so that the
* struct fits in 1 page
*/
G_STATIC_ASSERT (sizeof (SvgPathOps) < 4096 );
struct _SvgPathData
{
SvgPathOp *ops;
size_t n_ops;
};
static SvgPathData *
svg_path_data_new (size_t n_ops,
SvgPathOp *ops)
{
SvgPathData *p = g_new0 (SvgPathData, 1 );
p->n_ops = n_ops;
p->ops = ops;
return p;
}
void
svg_path_data_free (SvgPathData *p)
{
g_free (p->ops);
g_free (p);
}
static inline size_t
path_data_points_per_op (GskPathOperation op)
{
size_t n_pts[] = {
[GSK_PATH_MOVE] = 1 ,
[GSK_PATH_CLOSE] = 0 ,
[GSK_PATH_LINE] = 2 ,
[GSK_PATH_QUAD] = 3 ,
[GSK_PATH_CUBIC] = 4 ,
[GSK_PATH_CONIC] = 3
};
return n_pts[op];
}
static gboolean
path_data_add_op (GskPathOperation op,
const graphene_point_t *pts,
size_t n_pts,
float weight,
gpointer user_data)
{
SvgPathOps *ops = user_data;
size_t size;
SvgPathOp *pop;
if (op == GSK_PATH_CONIC)
return FALSE ;
size = svg_path_ops_get_size (ops);
svg_path_ops_set_size (ops, size + 1 );
pop = svg_path_ops_index (ops, size);
pop->op = op;
memset (pop->seg.pts, 0 , sizeof (graphene_point_t) * 4 );
memcpy (pop->seg.pts, pts, sizeof (graphene_point_t) * n_pts);
return TRUE ;
}
static gboolean
path_data_add_arc (float rx,
float ry,
float x_axis_rotation,
gboolean large_arc,
gboolean positive_sweep,
float x,
float y,
gpointer user_data)
{
SvgPathOps *ops = user_data;
size_t size;
SvgPathOp *pop;
size = svg_path_ops_get_size (ops);
svg_path_ops_set_size (ops, size + 1 );
pop = svg_path_ops_index (ops, size);
pop->op = SVG_PATH_ARC;
pop->arc.rx = rx;
pop->arc.ry = ry;
pop->arc.x_axis_rotation = x_axis_rotation;
pop->arc.large_arc = large_arc;
pop->arc.positive_sweep = positive_sweep;
pop->arc.x = x;
pop->arc.y = y;
return TRUE ;
}
gboolean
svg_path_data_parse_full (const char *string,
SvgPathData **path_data)
{
GskPathParser parser = {
path_data_add_op,
path_data_add_arc,
NULL, NULL, NULL,
};
SvgPathOps ops;
size_t size;
SvgPathOp *data;
gboolean ret;
svg_path_ops_init (&ops);
ret = gsk_path_parse_full (string, &parser, &ops);
size = svg_path_ops_get_size (&ops);
data = svg_path_ops_steal (&ops);
*path_data = svg_path_data_new (size, data);
return ret;
}
SvgPathData *
svg_path_data_from_gsk (GskPath *path)
{
SvgPathOps ops;
size_t size;
SvgPathOp *data;
svg_path_ops_init (&ops);
gsk_path_foreach (path,
GSK_PATH_FOREACH_ALLOW_QUAD |
GSK_PATH_FOREACH_ALLOW_CUBIC,
path_data_add_op,
&ops);
size = svg_path_ops_get_size (&ops);
data = svg_path_ops_steal (&ops);
return svg_path_data_new (size, data);
}
void
svg_path_data_print (SvgPathData *p,
GString *s)
{
for (unsigned int i = 0 ; i < p->n_ops; i++)
{
SvgPathOp *op = &p->ops[i];
if (i > 0 )
g_string_append_c (s, ' ' );
switch (op->op)
{
case GSK_PATH_MOVE:
string_append_point (s, "M " , &op->seg.pts[0 ]);
break ;
case GSK_PATH_CLOSE:
g_string_append (s, "Z" );
break ;
case GSK_PATH_LINE:
string_append_point (s, "L " , &op->seg.pts[1 ]);
break ;
case GSK_PATH_QUAD:
string_append_point (s, "Q " , &op->seg.pts[1 ]);
string_append_point (s, ", " , &op->seg.pts[2 ]);
break ;
case GSK_PATH_CUBIC:
string_append_point (s, "C " , &op->seg.pts[1 ]);
string_append_point (s, ", " , &op->seg.pts[2 ]);
string_append_point (s, ", " , &op->seg.pts[3 ]);
break ;
case SVG_PATH_ARC:
string_append_double (s, "A " , op->arc.rx);
string_append_double (s, " " , op->arc.ry);
string_append_double (s, " " , op->arc.x_axis_rotation);
g_string_append_printf (s, " %d %d" , op->arc.large_arc, op->arc.positive_sweep);
string_append_double (s, " " , op->arc.x);
string_append_double (s, " " , op->arc.y);
break ;
default :
g_assert_not_reached ();
}
}
}
SvgPathData *
svg_path_data_interpolate (SvgPathData *p0,
SvgPathData *p1,
double t)
{
SvgPathOp *ops;
if (p0->n_ops != p1->n_ops)
return NULL;
ops = g_new0 (SvgPathOp, p0->n_ops);
for (unsigned int i = 0 ; i < p0->n_ops; i++)
{
SvgPathOp *op0 = &p0->ops[i];
SvgPathOp *op1 = &p1->ops[i];
SvgPathOp *op = &ops[i];
if (op0->op != op1->op)
{
g_free (ops);
return NULL;
}
op->op = op0->op;
switch (op->op)
{
case GSK_PATH_CUBIC:
lerp_point (t, &op0->seg.pts[3 ], &op1->seg.pts[3 ], &op->seg.pts[3 ]);
G_GNUC_FALLTHROUGH;
case GSK_PATH_QUAD:
lerp_point (t, &op0->seg.pts[2 ], &op1->seg.pts[2 ], &op->seg.pts[2 ]);
G_GNUC_FALLTHROUGH;
case GSK_PATH_LINE:
case GSK_PATH_MOVE:
lerp_point (t, &op0->seg.pts[1 ], &op1->seg.pts[1 ], &op->seg.pts[1 ]);
G_GNUC_FALLTHROUGH;
case GSK_PATH_CLOSE:
lerp_point (t, &op0->seg.pts[0 ], &op1->seg.pts[0 ], &op->seg.pts[0 ]);
break ;
case SVG_PATH_ARC:
op->arc.rx = lerp (t, op0->arc.rx, op1->arc.rx);
op->arc.ry = lerp (t, op0->arc.ry, op1->arc.ry);
op->arc.x_axis_rotation = lerp (t, op0->arc.x_axis_rotation, op1->arc.x_axis_rotation);
op->arc.large_arc = lerp_bool (t, op0->arc.large_arc, op1->arc.large_arc);
op->arc.positive_sweep = lerp_bool (t, op0->arc.positive_sweep, op1->arc.positive_sweep);
op->arc.x = lerp (t, op0->arc.x, op1->arc.x);
op->arc.y = lerp (t, op0->arc.y, op1->arc.y);
break ;
default :
g_assert_not_reached ();
}
}
return svg_path_data_new (p0->n_ops, ops);
}
GskPath *
svg_path_data_to_gsk (SvgPathData *p)
{
GskPathBuilder *builder;
builder = gsk_path_builder_new ();
for (unsigned int i = 0 ; i < p->n_ops; i++)
{
SvgPathOp *op = &p->ops[i];
if (op->op == SVG_PATH_ARC)
gsk_path_builder_svg_arc_to (builder,
op->arc.rx, op->arc.ry,
op->arc.x_axis_rotation,
op->arc.large_arc,
op->arc.positive_sweep,
op->arc.x, op->arc.y);
else
gsk_path_builder_add_op (builder, op->op, op->seg.pts, path_data_points_per_op (op->op), 1 );
}
return gsk_path_builder_free_to_path (builder);
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ 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.0.16Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-07-02)
¤
*Bot Zugriff