/*
* 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 "gtksvgclipprivate.h"
#include "gtksvgvalueprivate.h"
#include "gtksvgpathdataprivate.h"
#include "gtksvgelementinternal.h"
#define FILL_RULE_OMITTED 0 xffff
typedef struct
{
SvgValue base;
ClipKind kind;
union {
struct {
SvgPathData *pdata;
GskPath *path;
unsigned int fill_rule;
} path;
struct {
char *ref;
SvgElement *shape;
} ref;
};
} SvgClip;
static void
svg_clip_free (SvgValue *value)
{
SvgClip *clip = (SvgClip *) value;
if (clip->kind == CLIP_PATH)
{
gsk_path_unref (clip->path.path);
svg_path_data_free (clip->path.pdata);
}
else if (clip->kind == CLIP_URL)
{
g_free (clip->ref.ref);
}
g_free (value);
}
static gboolean
svg_clip_equal (const SvgValue *value0,
const SvgValue *value1)
{
const SvgClip *c0 = (const SvgClip *) value0;
const SvgClip *c1 = (const SvgClip *) value1;
if (c0->kind != c1->kind)
return FALSE ;
switch (c0->kind)
{
case CLIP_NONE:
return TRUE ;
case CLIP_PATH:
return c0->path.fill_rule == c1->path.fill_rule &&
gsk_path_equal (c0->path.path, c1->path.path);
case CLIP_URL:
return c0->ref.shape == c1->ref.shape;
default :
g_assert_not_reached ();
}
}
static SvgValue * svg_clip_interpolate (const SvgValue *value0,
const SvgValue *value1,
SvgComputeContext *context,
double t);
static SvgValue *
svg_clip_accumulate (const SvgValue *value0,
const SvgValue *value1,
SvgComputeContext *context,
int n)
{
return NULL;
}
static void
svg_clip_print (const SvgValue *value,
GString *string)
{
const SvgClip *c = (const SvgClip *) value;
const char *rule[] = {
[GSK_FILL_RULE_WINDING] = "nonzero" ,
[GSK_FILL_RULE_EVEN_ODD] = "evenodd"
};
switch (c->kind)
{
case CLIP_NONE:
g_string_append (string, "none" );
break ;
case CLIP_PATH:
g_string_append (string, "path(" );
if (c->path.fill_rule != FILL_RULE_OMITTED)
{
g_string_append (string, rule[c->path.fill_rule]);
g_string_append (string, ", " );
}
g_string_append (string, "\" ");
svg_path_data_print (c->path.pdata, string);
g_string_append (string, "\" )");
break ;
case CLIP_URL:
g_string_append_printf (string, "url(%s)" , c->ref.ref);
break ;
default :
g_assert_not_reached ();
}
}
static const SvgValueClass SVG_CLIP_CLASS = {
"SvgClip" ,
svg_clip_free,
svg_clip_equal,
svg_clip_interpolate,
svg_clip_accumulate,
svg_clip_print,
svg_value_default_distance,
svg_value_default_resolve,
};
SvgValue *
svg_clip_new_none (void )
{
static SvgClip none = { { &SVG_CLIP_CLASS, 0 }, CLIP_NONE };
return (SvgValue *) &none;
}
static SvgValue *
svg_clip_new_from_data (SvgPathData *pdata,
unsigned int fill_rule)
{
SvgClip *result;
if (pdata == NULL)
return NULL;
result = (SvgClip *) svg_value_alloc (&SVG_CLIP_CLASS, sizeof (SvgClip));
result->kind = CLIP_PATH;
result->path.pdata = pdata;
result->path.path = svg_path_data_to_gsk (pdata);
result->path.fill_rule = fill_rule;
return (SvgValue *) result;
}
SvgValue *
svg_clip_new_path (const char *string,
unsigned int fill_rule)
{
SvgPathData *data = NULL;
if (!svg_path_data_parse_full (string, &data))
return NULL;
return svg_clip_new_from_data (data, fill_rule);
}
SvgValue *
svg_clip_new_url_take (const char *string)
{
SvgClip *result;
result = (SvgClip *) svg_value_alloc (&SVG_CLIP_CLASS, sizeof (SvgClip));
result->kind = CLIP_URL;
result->ref.ref = (char *) string;
result->ref.shape = NULL;
return (SvgValue *) result;
}
static SvgValue *
svg_clip_interpolate (const SvgValue *value0,
const SvgValue *value1,
SvgComputeContext *context,
double t)
{
const SvgClip *c0 = (const SvgClip *) value0;
const SvgClip *c1 = (const SvgClip *) value1;
if (c0->kind == c1->kind)
{
switch (c0->kind)
{
case CLIP_NONE:
return svg_clip_new_none ();
case CLIP_PATH:
{
SvgPathData *p;
unsigned int fill_rule;
p = svg_path_data_interpolate (c0->path.pdata,
c1->path.pdata,
t);
if (t < 0 .5 )
fill_rule = c0->path.fill_rule;
else
fill_rule = c1->path.fill_rule;
if (p)
return svg_clip_new_from_data (p, fill_rule);
}
break ;
case CLIP_URL:
break ;
default :
g_assert_not_reached ();
}
}
if (t < 0 .5 )
return svg_value_ref ((SvgValue *) value0);
else
return svg_value_ref ((SvgValue *) value1);
}
typedef struct
{
unsigned int fill_rule;
char *string;
} ClipPathArgs;
static unsigned int
parse_clip_path_arg (GtkCssParser *parser,
unsigned int n,
gpointer data)
{
ClipPathArgs *args = data;
if (n == 0 )
{
if (gtk_css_parser_try_ident (parser, "nonzero" ))
{
args->fill_rule = GSK_FILL_RULE_WINDING;
return 1 ;
}
else if (gtk_css_parser_try_ident (parser, "evenodd" ))
{
args->fill_rule = GSK_FILL_RULE_EVEN_ODD;
return 1 ;
}
}
args->string = gtk_css_parser_consume_string (parser);
if (!args->string)
return 0 ;
return 1 ;
}
SvgValue *
svg_clip_parse (GtkCssParser *parser)
{
SvgValue *value = NULL;
if (gtk_css_parser_try_ident (parser, "none" ))
return svg_clip_new_none ();
if (gtk_css_parser_has_function (parser, "path" ))
{
ClipPathArgs args = { .fill_rule = FILL_RULE_OMITTED, .string = NULL, };
GtkCssLocation start;
start = *gtk_css_parser_get_start_location (parser);
if (gtk_css_parser_consume_function (parser, 1 , 2 , parse_clip_path_arg, &args))
{
SvgPathData *data = NULL;
if (!svg_path_data_parse_full (args.string, &data))
{
GError *error;
error = g_error_new_literal (GTK_CSS_PARSER_ERROR,
GTK_CSS_PARSER_ERROR_UNKNOWN_VALUE,
"Path data is invalid" );
gtk_css_parser_emit_error (parser,
&start,
gtk_css_parser_get_end_location (parser),
error);
g_error_free (error);
}
value = svg_clip_new_from_data (data, args.fill_rule);
g_free (args.string);
}
}
else
{
char *url = gtk_css_parser_consume_url (parser);
if (url != NULL)
value = svg_clip_new_url_take (url);
}
if (value == NULL)
gtk_css_parser_error_syntax (parser, "Expected a path" );
return value;
}
ClipKind
svg_clip_get_kind (const SvgValue *value)
{
const SvgClip *clip = (const SvgClip *) value;
g_assert (value->class == &SVG_CLIP_CLASS);
return clip->kind;
}
const char *
svg_clip_get_id (const SvgValue *value)
{
const SvgClip *clip = (const SvgClip *) value;
g_assert (value->class == &SVG_CLIP_CLASS);
g_assert (clip->kind == CLIP_URL);
if (clip->ref.ref[0 ] == '#' )
return clip->ref.ref + 1 ;
else
return clip->ref.ref;
}
SvgElement *
svg_clip_get_shape (const SvgValue *value)
{
const SvgClip *clip = (const SvgClip *) value;
g_assert (value->class == &SVG_CLIP_CLASS);
g_assert (clip->kind == CLIP_URL);
return clip->ref.shape;
}
GskFillRule
svg_clip_get_fill_rule (const SvgValue *value)
{
const SvgClip *clip = (const SvgClip *) value;
g_assert (value->class == &SVG_CLIP_CLASS);
g_assert (clip->kind == CLIP_PATH);
return clip->path.fill_rule;
}
GskPath *
svg_clip_get_path (const SvgValue *value)
{
const SvgClip *clip = (const SvgClip *) value;
g_assert (value->class == &SVG_CLIP_CLASS);
g_assert (clip->kind == CLIP_PATH);
return clip->path.path;
}
void
svg_clip_set_shape (SvgValue *value,
SvgElement *shape)
{
SvgClip *clip = (SvgClip *) value;
g_assert (value->class == &SVG_CLIP_CLASS);
g_assert (clip->kind == CLIP_URL);
clip->ref.shape = shape;
}
Messung V0.5 in Prozent C=99 H=98 G=98
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland