Merge branch 'wip/otte/conic' into 'master'

Add support for conic gradients

See merge request GNOME/gtk!2911
This commit is contained in:
Matthias Clasen 2020-12-03 03:54:24 +00:00
commit 35d2cbefe6
16 changed files with 1153 additions and 33 deletions

View File

@ -83,6 +83,11 @@ gsk_radial_gradient_node_get_hradius
gsk_radial_gradient_node_get_vradius
gsk_radial_gradient_node_get_center
gsk_repeating_radial_gradient_node_new
gsk_conic_gradient_node_new
gsk_conic_gradient_node_get_n_color_stops
gsk_conic_gradient_node_get_color_stops
gsk_conic_gradient_node_get_center
gsk_conic_gradient_node_get_rotation
gsk_border_node_new
gsk_border_node_get_outline
gsk_border_node_get_widths

View File

@ -4304,6 +4304,7 @@ gtk_snapshot_append_color
gtk_snapshot_append_layout
gtk_snapshot_append_linear_gradient
gtk_snapshot_append_repeating_linear_gradient
gtk_snapshot_append_conic_gradient
gtk_snapshot_append_border
gtk_snapshot_append_inset_shadow
gtk_snapshot_append_outset_shadow

View File

@ -264,6 +264,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_REPEAT_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
@ -848,6 +849,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_REPEAT_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:

View File

@ -3723,6 +3723,7 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_CAIRO_NODE:
default:
{

View File

@ -32,6 +32,7 @@
* @GSK_REPEATING_LINEAR_GRADIENT_NODE: A node drawing a repeating linear gradient
* @GSK_RADIAL_GRADIENT_NODE: A node drawing a radial gradient
* @GSK_REPEATING_RADIAL_GRADIENT_NODE: A node drawing a repeating radial gradient
* @GSK_CONIC_GRADIENT_NODE: A node drawing a conic gradient
* @GSK_BORDER_NODE: A node stroking a border around an area
* @GSK_TEXTURE_NODE: A node drawing a #GdkTexture
* @GSK_INSET_SHADOW_NODE: A node drawing an inset shadow
@ -61,6 +62,7 @@ typedef enum {
GSK_REPEATING_LINEAR_GRADIENT_NODE,
GSK_RADIAL_GRADIENT_NODE,
GSK_REPEATING_RADIAL_GRADIENT_NODE,
GSK_CONIC_GRADIENT_NODE,
GSK_BORDER_NODE,
GSK_TEXTURE_NODE,
GSK_INSET_SHADOW_NODE,

View File

@ -146,6 +146,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE (gsk_repeating_linear_gradient_node_get_type())
#define GSK_TYPE_RADIAL_GRADIENT_NODE (gsk_radial_gradient_node_get_type())
#define GSK_TYPE_REPEATING_RADIAL_GRADIENT_NODE (gsk_repeating_radial_gradient_node_get_type())
#define GSK_TYPE_CONIC_GRADIENT_NODE (gsk_conic_gradient_node_get_type())
#define GSK_TYPE_BORDER_NODE (gsk_border_node_get_type())
#define GSK_TYPE_INSET_SHADOW_NODE (gsk_inset_shadow_node_get_type())
#define GSK_TYPE_OUTSET_SHADOW_NODE (gsk_outset_shadow_node_get_type())
@ -171,6 +172,7 @@ typedef struct _GskLinearGradientNode GskLinearGradientNode;
typedef struct _GskRepeatingLinearGradientNode GskRepeatingLinearGradientNode;
typedef struct _GskRadialGradientNode GskRadialGradientNode;
typedef struct _GskRepeatingRadialGradientNode GskRepeatingRadialGradientNode;
typedef struct _GskConicGradientNode GskConicGradientNode;
typedef struct _GskBorderNode GskBorderNode;
typedef struct _GskInsetShadowNode GskInsetShadowNode;
typedef struct _GskOutsetShadowNode GskOutsetShadowNode;
@ -242,6 +244,24 @@ GskRenderNode * gsk_repeating_linear_gradient_node_new (const graph
const GskColorStop *color_stops,
gsize n_color_stops);
GDK_AVAILABLE_IN_ALL
GType gsk_conic_gradient_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_conic_gradient_node_new (const graphene_rect_t *bounds,
const graphene_point_t *center,
float rotation,
const GskColorStop *color_stops,
gsize n_color_stops);
GDK_AVAILABLE_IN_ALL
const graphene_point_t * gsk_conic_gradient_node_get_center (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
float gsk_conic_gradient_node_get_rotation (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
gsize gsk_conic_gradient_node_get_n_color_stops (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
const GskColorStop * gsk_conic_gradient_node_get_color_stops (GskRenderNode *node,
gsize *n_stops);
GDK_AVAILABLE_IN_ALL
GType gsk_radial_gradient_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL

View File

@ -768,6 +768,327 @@ gsk_radial_gradient_node_get_end (GskRenderNode *node)
return self->end;
}
/*** GSK_CONIC_GRADIENT_NODE ***/
struct _GskConicGradientNode
{
GskRenderNode render_node;
graphene_point_t center;
float rotation;
gsize n_stops;
GskColorStop *stops;
};
static void
gsk_conic_gradient_node_finalize (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_CONIC_GRADIENT_NODE));
g_free (self->stops);
parent_class->finalize (node);
}
#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
static void
_cairo_mesh_pattern_set_corner_rgba (cairo_pattern_t *pattern,
guint corner_num,
const GdkRGBA *rgba)
{
cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, rgba->red, rgba->green, rgba->blue, rgba->alpha);
}
static void
project (double angle,
double radius,
double *x_out,
double *y_out)
{
double x, y;
x = radius * cos (angle);
y = radius * sin (angle);
if (copysign (x, 1.0) > copysign (y, 1.0))
{
*x_out = copysign (radius, x);
*y_out = y * radius / copysign (x, 1.0);
}
else
{
*x_out = x * radius / copysign (y, 1.0);
*y_out = copysign (radius, y);
}
}
static void
gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
float radius,
float start_angle,
const GdkRGBA *start_color,
float end_angle,
const GdkRGBA *end_color)
{
double x, y;
cairo_mesh_pattern_begin_patch (pattern);
cairo_mesh_pattern_move_to (pattern, 0, 0);
project (start_angle, radius, &x, &y);
cairo_mesh_pattern_line_to (pattern, x, y);
project (end_angle, radius, &x, &y);
cairo_mesh_pattern_line_to (pattern, x, y);
cairo_mesh_pattern_line_to (pattern, 0, 0);
_cairo_mesh_pattern_set_corner_rgba (pattern, 0, start_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 1, start_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 2, end_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 3, end_color);
cairo_mesh_pattern_end_patch (pattern);
}
static void
gdk_rgba_color_interpolate (GdkRGBA *dest,
const GdkRGBA *src1,
const GdkRGBA *src2,
double progress)
{
double alpha = src1->alpha * (1.0 - progress) + src2->alpha * progress;
dest->alpha = alpha;
if (alpha == 0)
{
dest->red = src1->red * (1.0 - progress) + src2->red * progress;
dest->green = src1->green * (1.0 - progress) + src2->green * progress;
dest->blue = src1->blue * (1.0 - progress) + src2->blue * progress;
}
else
{
dest->red = (src1->red * src1->alpha * (1.0 - progress) + src2->red * src2->alpha * progress) / alpha;
dest->green = (src1->green * src1->alpha * (1.0 - progress) + src2->green * src2->alpha * progress) / alpha;
dest->blue = (src1->blue * src1->alpha * (1.0 - progress) + src2->blue * src2->alpha * progress) / alpha;
}
}
static void
gsk_conic_gradient_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
cairo_pattern_t *pattern;
graphene_point_t corner;
float radius;
gsize i;
pattern = cairo_pattern_create_mesh ();
graphene_rect_get_top_right (&node->bounds, &corner);
radius = graphene_point_distance (&self->center, &corner, NULL, NULL);
graphene_rect_get_bottom_right (&node->bounds, &corner);
radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
graphene_rect_get_bottom_left (&node->bounds, &corner);
radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
graphene_rect_get_top_left (&node->bounds, &corner);
radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
for (i = 0; i <= self->n_stops; i++)
{
GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
double offset1 = i > 0 ? stop1->offset : 0;
double offset2 = i < self->n_stops ? stop2->offset : 1;
double start_angle, end_angle;
offset1 = offset1 * 360 + self->rotation - 90;
offset2 = offset2 * 360 + self->rotation - 90;
for (start_angle = offset1; start_angle < offset2; start_angle = end_angle)
{
GdkRGBA start_color, end_color;
end_angle = (floor (start_angle / 45) + 1) * 45;
end_angle = MIN (end_angle, offset2);
gdk_rgba_color_interpolate (&start_color,
&stop1->color,
&stop2->color,
(start_angle - offset1) / (offset2 - offset1));
gdk_rgba_color_interpolate (&end_color,
&stop1->color,
&stop2->color,
(end_angle - offset1) / (offset2 - offset1));
gsk_conic_gradient_node_add_patch (pattern,
radius,
DEG_TO_RAD (start_angle),
&start_color,
DEG_TO_RAD (end_angle),
&end_color);
}
}
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
gsk_cairo_rectangle (cr, &node->bounds);
cairo_translate (cr, self->center.x, self->center.y);
cairo_set_source (cr, pattern);
cairo_fill (cr);
cairo_pattern_destroy (pattern);
}
static void
gsk_conic_gradient_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskConicGradientNode *self1 = (GskConicGradientNode *) node1;
GskConicGradientNode *self2 = (GskConicGradientNode *) node2;
gsize i;
if (!graphene_point_equal (&self1->center, &self2->center) ||
self1->rotation != self2->rotation ||
self1->n_stops != self2->n_stops)
{
gsk_render_node_diff_impossible (node1, node2, region);
return;
}
for (i = 0; i < self1->n_stops; i++)
{
GskColorStop *stop1 = &self1->stops[i];
GskColorStop *stop2 = &self2->stops[i];
if (stop1->offset != stop2->offset ||
!gdk_rgba_equal (&stop1->color, &stop2->color))
{
gsk_render_node_diff_impossible (node1, node2, region);
return;
}
}
}
/**
* gsk_conic_gradient_node_new:
* @bounds: the bounds of the node
* @center: the center of the gradient
* @rotation: the rotation of the gradient in degrees
* @color_stops: (array length=n_color_stops): a pointer to an array of #GskColorStop defining the gradient
* The offsets of all color steps must be increasing. The first stop's offset must be >= 0 and the last
* stop's offset must be <= 1.
* @n_color_stops: the number of elements in @color_stops
*
* Creates a #GskRenderNode that draws a conic gradient. The conic gradient
* starts around @center in the direction of @rotation. A rotation of 0 means
* that the gradient points up. Color stops are then added clockwise.
*
* Returns: (transfer full) (type GskConicGradientNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_conic_gradient_node_new (const graphene_rect_t *bounds,
const graphene_point_t *center,
float rotation,
const GskColorStop *color_stops,
gsize n_color_stops)
{
GskConicGradientNode *self;
GskRenderNode *node;
gsize i;
g_return_val_if_fail (bounds != NULL, NULL);
g_return_val_if_fail (center != NULL, NULL);
g_return_val_if_fail (color_stops != NULL, NULL);
g_return_val_if_fail (n_color_stops >= 2, NULL);
g_return_val_if_fail (color_stops[0].offset >= 0, NULL);
for (i = 1; i < n_color_stops; i++)
g_return_val_if_fail (color_stops[i].offset >= color_stops[i - 1].offset, NULL);
g_return_val_if_fail (color_stops[n_color_stops - 1].offset <= 1, NULL);
self = gsk_render_node_alloc (GSK_CONIC_GRADIENT_NODE);
node = (GskRenderNode *) self;
graphene_rect_init_from_rect (&node->bounds, bounds);
graphene_point_init_from_point (&self->center, center);
self->rotation = rotation;
self->n_stops = n_color_stops;
self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
return node;
}
/**
* gsk_conic_gradient_node_get_n_color_stops:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
*
* Retrieves the number of color stops in the gradient.
*
* Returns: the number of color stops
*/
gsize
gsk_conic_gradient_node_get_n_color_stops (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
return self->n_stops;
}
/**
* gsk_conic_gradient_node_get_color_stops:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
* @n_stops: (out) (optional): the number of color stops in the returned array
*
* Retrieves the color stops in the gradient.
*
* Returns: (array length=n_stops): the color stops in the gradient
*/
const GskColorStop *
gsk_conic_gradient_node_get_color_stops (GskRenderNode *node,
gsize *n_stops)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
if (n_stops != NULL)
*n_stops = self->n_stops;
return self->stops;
}
/**
* gsk_conic_gradient_node_get_center:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
*
* Retrieves the center pointer for the gradient.
*
* Returns: the center point for the gradient
*/
const graphene_point_t *
gsk_conic_gradient_node_get_center (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
return &self->center;
}
/**
* gsk_conic_gradient_node_get_rotation:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
*
* Retrieves the rotation for the gradient in degrees.
*
* Returns: the rotation for the gradient
*/
float
gsk_conic_gradient_node_get_rotation (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
return self->rotation;
}
/*** GSK_BORDER_NODE ***/
/**
@ -4875,6 +5196,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_linear_gradient_node, GSK_LINEAR_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_linear_gradient_node, GSK_REPEATING_LINEAR_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_radial_gradient_node, GSK_RADIAL_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_radial_gradient_node, GSK_REPEATING_RADIAL_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_conic_gradient_node, GSK_CONIC_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_border_node, GSK_BORDER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_node, GSK_TEXTURE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_inset_shadow_node, GSK_INSET_SHADOW_NODE)
@ -5008,6 +5330,22 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_REPEATING_RADIAL_GRADIENT_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_REPEATING_RADIAL_GRADIENT_NODE,
sizeof (GskRadialGradientNode),
NULL,
gsk_conic_gradient_node_finalize,
gsk_conic_gradient_node_draw,
NULL,
gsk_conic_gradient_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskConicGradientNode"), &node_info);
gsk_render_node_types[GSK_CONIC_GRADIENT_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{

View File

@ -1070,6 +1070,40 @@ parse_repeating_radial_gradient_node (GtkCssParser *parser)
return parse_radial_gradient_node_internal (parser, TRUE);
}
static GskRenderNode *
parse_conic_gradient_node (GtkCssParser *parser)
{
graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
graphene_point_t center = GRAPHENE_POINT_INIT (25, 25);
double rotation = 0.0;
GArray *stops = NULL;
const Declaration declarations[] = {
{ "bounds", parse_rect, NULL, &bounds },
{ "center", parse_point, NULL, &center },
{ "rotation", parse_double, NULL, &rotation },
{ "stops", parse_stops, clear_stops, &stops },
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
if (stops == NULL)
{
GskColorStop from = { 0.0, GDK_RGBA("AAFF00") };
GskColorStop to = { 1.0, GDK_RGBA("FF00CC") };
stops = g_array_new (FALSE, FALSE, sizeof (GskColorStop));
g_array_append_val (stops, from);
g_array_append_val (stops, to);
}
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
(GskColorStop *) stops->data, stops->len);
g_array_free (stops, TRUE);
return result;
}
static GskRenderNode *
parse_inset_shadow_node (GtkCssParser *parser)
{
@ -1797,6 +1831,7 @@ parse_node (GtkCssParser *parser,
{ "inset-shadow", parse_inset_shadow_node },
{ "linear-gradient", parse_linear_gradient_node },
{ "radial-gradient", parse_radial_gradient_node },
{ "conic-gradient", parse_conic_gradient_node },
{ "opacity", parse_opacity_node },
{ "outset-shadow", parse_outset_shadow_node },
{ "repeat", parse_repeat_node },
@ -2184,6 +2219,30 @@ append_node_param (Printer *p,
render_node_print (p, node);
}
static void
append_stops_param (Printer *p,
const char *param_name,
const GskColorStop *stops,
gsize n_stops)
{
gsize i;
_indent (p);
g_string_append (p->str, param_name);
g_string_append (p->str, ": ");
for (i = 0; i < n_stops; i ++)
{
if (i > 0)
g_string_append (p->str, ", ");
string_append_double (p->str, stops[i].offset);
g_string_append_c (p->str, ' ');
append_rgba (p->str, &stops[i].color);
}
g_string_append (p->str, ";\n");
}
static cairo_status_t
cairo_write_array (void *closure,
const unsigned char *data,
@ -2294,10 +2353,6 @@ render_node_print (Printer *p,
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_LINEAR_GRADIENT_NODE:
{
const gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (node);
const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL);
gsize i;
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
start_node (p, "repeating-linear-gradient");
else
@ -2306,19 +2361,8 @@ render_node_print (Printer *p,
append_rect_param (p, "bounds", &node->bounds);
append_point_param (p, "end", gsk_linear_gradient_node_get_end (node));
append_point_param (p, "start", gsk_linear_gradient_node_get_start (node));
_indent (p);
g_string_append (p->str, "stops: ");
for (i = 0; i < n_stops; i ++)
{
if (i > 0)
g_string_append (p->str, ", ");
string_append_double (p->str, stops[i].offset);
g_string_append_c (p->str, ' ');
append_rgba (p->str, &stops[i].color);
}
g_string_append (p->str, ";\n");
append_stops_param (p, "stops", gsk_linear_gradient_node_get_color_stops (node, NULL),
gsk_linear_gradient_node_get_n_color_stops (node));
end_node (p);
}
@ -2327,10 +2371,6 @@ render_node_print (Printer *p,
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_RADIAL_GRADIENT_NODE:
{
const gsize n_stops = gsk_radial_gradient_node_get_n_color_stops (node);
const GskColorStop *stops = gsk_radial_gradient_node_get_color_stops (node, NULL);
gsize i;
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE)
start_node (p, "repeating-radial-gradient");
else
@ -2343,18 +2383,23 @@ render_node_print (Printer *p,
append_float_param (p, "start", gsk_radial_gradient_node_get_start (node), 0.0f);
append_float_param (p, "end", gsk_radial_gradient_node_get_end (node), 1.0f);
_indent (p);
g_string_append (p->str, "stops: ");
for (i = 0; i < n_stops; i ++)
{
if (i > 0)
g_string_append (p->str, ", ");
append_stops_param (p, "stops", gsk_radial_gradient_node_get_color_stops (node, NULL),
gsk_radial_gradient_node_get_n_color_stops (node));
string_append_double (p->str, stops[i].offset);
g_string_append_c (p->str, ' ');
append_rgba (p->str, &stops[i].color);
}
g_string_append (p->str, ";\n");
end_node (p);
}
break;
case GSK_CONIC_GRADIENT_NODE:
{
start_node (p, "conic-gradient");
append_rect_param (p, "bounds", &node->bounds);
append_point_param (p, "center", gsk_conic_gradient_node_get_center (node));
append_float_param (p, "rotation", gsk_conic_gradient_node_get_rotation (node), 0.0f);
append_stops_param (p, "stops", gsk_conic_gradient_node_get_color_stops (node, NULL),
gsk_conic_gradient_node_get_n_color_stops (node));
end_node (p);
}

View File

@ -259,6 +259,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_SHADOW_NODE:
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
default:
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));

View File

@ -26,14 +26,15 @@
#include "gtkprivate.h"
/* for the types only */
#include "gtk/gtkcssimageconicprivate.h"
#include "gtk/gtkcssimagecrossfadeprivate.h"
#include "gtk/gtkcssimagefallbackprivate.h"
#include "gtk/gtkcssimageiconthemeprivate.h"
#include "gtk/gtkcssimagelinearprivate.h"
#include "gtk/gtkcssimageradialprivate.h"
#include "gtk/gtkcssimageurlprivate.h"
#include "gtk/gtkcssimagescaledprivate.h"
#include "gtk/gtkcssimagerecolorprivate.h"
#include "gtk/gtkcssimagefallbackprivate.h"
G_DEFINE_ABSTRACT_TYPE (GtkCssImage, _gtk_css_image, G_TYPE_OBJECT)
@ -521,6 +522,7 @@ gtk_css_image_get_parser_type (GtkCssParser *parser)
{ "repeating-linear-gradient", _gtk_css_image_linear_get_type },
{ "radial-gradient", _gtk_css_image_radial_get_type },
{ "repeating-radial-gradient", _gtk_css_image_radial_get_type },
{ "conic-gradient", gtk_css_image_conic_get_type },
{ "cross-fade", gtk_css_image_cross_fade_get_type },
{ "image", _gtk_css_image_fallback_get_type }
};

529
gtk/gtkcssimageconic.c Normal file
View File

@ -0,0 +1,529 @@
/*
* Copyright © 2012 Red Hat Inc.
*
* 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: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcssimageconicprivate.h"
#include <math.h>
#include "gtkcsscolorvalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcsspositionvalueprivate.h"
#include "gtkcssprovider.h"
G_DEFINE_TYPE (GtkCssImageConic, gtk_css_image_conic, GTK_TYPE_CSS_IMAGE)
static void
gtk_css_image_conic_snapshot (GtkCssImage *image,
GtkSnapshot *snapshot,
double width,
double height)
{
GtkCssImageConic *self = GTK_CSS_IMAGE_CONIC (image);
GskColorStop *stops;
int i, last;
double offset;
stops = g_newa (GskColorStop, self->n_stops);
last = -1;
offset = 0;
for (i = 0; i < self->n_stops; i++)
{
const GtkCssImageConicColorStop *stop = &self->color_stops[i];
double pos, step;
if (stop->offset == NULL)
{
if (i == 0)
pos = 0.0;
else if (i + 1 == self->n_stops)
pos = 1.0;
else
continue;
}
else
{
pos = _gtk_css_number_value_get (stop->offset, 360) / 360;
pos = CLAMP (pos, 0.0, 1.0);
}
pos = MAX (pos, offset);
step = (pos - offset) / (i - last);
for (last = last + 1; last <= i; last++)
{
stop = &self->color_stops[last];
offset += step;
stops[last].offset = offset;
stops[last].color = *gtk_css_color_value_get_rgba (stop->color);
}
offset = pos;
last = i;
}
gtk_snapshot_append_conic_gradient (
snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (_gtk_css_position_value_get_x (self->center, width),
_gtk_css_position_value_get_y (self->center, height)),
_gtk_css_number_value_get (self->rotation, 360),
stops,
self->n_stops);
}
static gboolean
parse_angles (GtkCssParser *parser,
gpointer option_data,
gpointer unused)
{
GtkCssValue **angles = option_data;
angles[0] = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE | GTK_CSS_PARSE_PERCENT);
if (angles[0] == NULL)
return FALSE;
if (gtk_css_number_value_can_parse (parser))
{
angles[1] = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE | GTK_CSS_PARSE_PERCENT);
if (angles[1] == NULL)
return FALSE;
}
return TRUE;
}
static gboolean
parse_color (GtkCssParser *parser,
gpointer option_data,
gpointer unused)
{
GtkCssValue **color = option_data;
*color = _gtk_css_color_value_parse (parser);
if (*color == NULL)
return FALSE;
return TRUE;
}
static guint
gtk_css_image_conic_parse_color_stop (GtkCssImageConic *self,
GtkCssParser *parser,
GArray *stop_array)
{
GtkCssValue *angles[2] = { NULL, NULL };
GtkCssValue *color = NULL;
GtkCssParseOption options[] =
{
{ (void *) gtk_css_number_value_can_parse, parse_angles, &angles },
{ (void *) gtk_css_color_value_can_parse, parse_color, &color },
};
if (!gtk_css_parser_consume_any (parser, options, G_N_ELEMENTS (options), NULL))
goto fail;
if (color == NULL)
{
gtk_css_parser_error_syntax (parser, "Expected shadow value to contain a length");
goto fail;
}
g_array_append_vals (stop_array, (GtkCssImageConicColorStop[1]) {
{ angles[0], color }
},
1);
if (angles[1])
g_array_append_vals (stop_array, (GtkCssImageConicColorStop[1]) {
{ angles[1], gtk_css_value_ref (color) }
},
1);
return 1;
fail:
g_clear_pointer (&angles[0], gtk_css_value_unref);
g_clear_pointer (&angles[1], gtk_css_value_unref);
g_clear_pointer (&color, gtk_css_value_unref);
return 0;
}
static guint
gtk_css_image_conic_parse_first_arg (GtkCssImageConic *self,
GtkCssParser *parser,
GArray *stop_array)
{
gboolean nothing_parsed = TRUE;
if (gtk_css_parser_try_ident (parser, "from"))
{
self->rotation = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE);
if (self->rotation == NULL)
return 0;
nothing_parsed = FALSE;
}
else
{
self->rotation = _gtk_css_number_value_new (0, GTK_CSS_DEG);
}
if (gtk_css_parser_try_ident (parser, "at"))
{
self->center = _gtk_css_position_value_parse (parser);
if (self->center == NULL)
return 0;
nothing_parsed = FALSE;
}
else
{
self->center = _gtk_css_position_value_new (_gtk_css_number_value_new (50, GTK_CSS_PERCENT),
_gtk_css_number_value_new (50, GTK_CSS_PERCENT));
}
if (!nothing_parsed)
return 1;
return 1 + gtk_css_image_conic_parse_color_stop (self, parser, stop_array);
}
typedef struct
{
GtkCssImageConic *self;
GArray *stop_array;
} ParseData;
static guint
gtk_css_image_conic_parse_arg (GtkCssParser *parser,
guint arg,
gpointer user_data)
{
ParseData *parse_data = user_data;
GtkCssImageConic *self = parse_data->self;
if (arg == 0)
return gtk_css_image_conic_parse_first_arg (self, parser, parse_data->stop_array);
else
return gtk_css_image_conic_parse_color_stop (self, parser, parse_data->stop_array);
}
static gboolean
gtk_css_image_conic_parse (GtkCssImage *image,
GtkCssParser *parser)
{
GtkCssImageConic *self = GTK_CSS_IMAGE_CONIC (image);
ParseData parse_data;
gboolean success;
if (!gtk_css_parser_has_function (parser, "conic-gradient"))
{
gtk_css_parser_error_syntax (parser, "Not a conic gradient");
return FALSE;
}
parse_data.self = self;
parse_data.stop_array = g_array_new (TRUE, FALSE, sizeof (GtkCssImageConicColorStop));
success = gtk_css_parser_consume_function (parser, 3, G_MAXUINT, gtk_css_image_conic_parse_arg, &parse_data);
if (!success)
{
g_array_free (parse_data.stop_array, TRUE);
}
else
{
self->n_stops = parse_data.stop_array->len;
self->color_stops = (GtkCssImageConicColorStop *)g_array_free (parse_data.stop_array, FALSE);
}
return success;
}
static void
gtk_css_image_conic_print (GtkCssImage *image,
GString *string)
{
GtkCssImageConic *self = GTK_CSS_IMAGE_CONIC (image);
gboolean written = FALSE;
guint i;
g_string_append (string, "self-gradient(");
if (self->center)
{
GtkCssValue *compare = _gtk_css_position_value_new (_gtk_css_number_value_new (50, GTK_CSS_PERCENT),
_gtk_css_number_value_new (50, GTK_CSS_PERCENT));
if (!_gtk_css_value_equal (self->center, compare))
{
g_string_append (string, "from ");
_gtk_css_value_print (self->center, string);
written = TRUE;
}
gtk_css_value_unref (compare);
}
if (self->rotation && _gtk_css_number_value_get (self->rotation, 360) != 0)
{
if (written)
g_string_append_c (string, ' ');
g_string_append (string, "at ");
_gtk_css_value_print (self->rotation, string);
}
if (written)
g_string_append (string, ", ");
for (i = 0; i < self->n_stops; i++)
{
const GtkCssImageConicColorStop *stop = &self->color_stops[i];
if (i > 0)
g_string_append (string, ", ");
_gtk_css_value_print (stop->color, string);
if (stop->offset)
{
g_string_append (string, " ");
_gtk_css_value_print (stop->offset, string);
}
}
g_string_append (string, ")");
}
static GtkCssImage *
gtk_css_image_conic_compute (GtkCssImage *image,
guint property_id,
GtkStyleProvider *provider,
GtkCssStyle *style,
GtkCssStyle *parent_style)
{
GtkCssImageConic *self = GTK_CSS_IMAGE_CONIC (image);
GtkCssImageConic *copy;
guint i;
copy = g_object_new (GTK_TYPE_CSS_IMAGE_CONIC, NULL);
copy->center = _gtk_css_value_compute (self->center, property_id, provider, style, parent_style);
copy->rotation = _gtk_css_value_compute (self->rotation, property_id, provider, style, parent_style);
copy->n_stops = self->n_stops;
copy->color_stops = g_malloc (sizeof (GtkCssImageConicColorStop) * copy->n_stops);
for (i = 0; i < self->n_stops; i++)
{
const GtkCssImageConicColorStop *stop = &self->color_stops[i];
GtkCssImageConicColorStop *scopy = &copy->color_stops[i];
scopy->color = _gtk_css_value_compute (stop->color, property_id, provider, style, parent_style);
if (stop->offset)
{
scopy->offset = _gtk_css_value_compute (stop->offset, property_id, provider, style, parent_style);
}
else
{
scopy->offset = NULL;
}
}
return GTK_CSS_IMAGE (copy);
}
static GtkCssImage *
gtk_css_image_conic_transition (GtkCssImage *start_image,
GtkCssImage *end_image,
guint property_id,
double progress)
{
GtkCssImageConic *start, *end, *result;
guint i;
start = GTK_CSS_IMAGE_CONIC (start_image);
if (end_image == NULL)
return GTK_CSS_IMAGE_CLASS (gtk_css_image_conic_parent_class)->transition (start_image, end_image, property_id, progress);
if (!GTK_IS_CSS_IMAGE_CONIC (end_image))
return GTK_CSS_IMAGE_CLASS (gtk_css_image_conic_parent_class)->transition (start_image, end_image, property_id, progress);
end = GTK_CSS_IMAGE_CONIC (end_image);
if (start->n_stops != end->n_stops)
return GTK_CSS_IMAGE_CLASS (gtk_css_image_conic_parent_class)->transition (start_image, end_image, property_id, progress);
result = g_object_new (GTK_TYPE_CSS_IMAGE_CONIC, NULL);
result->center = _gtk_css_value_transition (start->center, end->center, property_id, progress);
if (result->center == NULL)
goto fail;
result->rotation = _gtk_css_value_transition (start->rotation, end->rotation, property_id, progress);
if (result->rotation == NULL)
goto fail;
result->color_stops = g_malloc (sizeof (GtkCssImageConicColorStop) * start->n_stops);
result->n_stops = 0;
for (i = 0; i < start->n_stops; i++)
{
const GtkCssImageConicColorStop *start_stop = &start->color_stops[i];
const GtkCssImageConicColorStop *end_stop = &end->color_stops[i];
GtkCssImageConicColorStop *stop = &result->color_stops[i];
if ((start_stop->offset != NULL) != (end_stop->offset != NULL))
goto fail;
if (start_stop->offset == NULL)
{
stop->offset = NULL;
}
else
{
stop->offset = _gtk_css_value_transition (start_stop->offset,
end_stop->offset,
property_id,
progress);
if (stop->offset == NULL)
goto fail;
}
stop->color = _gtk_css_value_transition (start_stop->color,
end_stop->color,
property_id,
progress);
if (stop->color == NULL)
{
if (stop->offset)
_gtk_css_value_unref (stop->offset);
goto fail;
}
result->n_stops ++;
}
return GTK_CSS_IMAGE (result);
fail:
g_object_unref (result);
return GTK_CSS_IMAGE_CLASS (gtk_css_image_conic_parent_class)->transition (start_image, end_image, property_id, progress);
}
static gboolean
gtk_css_image_conic_equal (GtkCssImage *image1,
GtkCssImage *image2)
{
GtkCssImageConic *conic1 = (GtkCssImageConic *) image1;
GtkCssImageConic *conic2 = (GtkCssImageConic *) image2;
guint i;
if (!_gtk_css_value_equal (conic1->center, conic2->center) ||
!_gtk_css_value_equal (conic1->rotation, conic2->rotation))
return FALSE;
for (i = 0; i < conic1->n_stops; i++)
{
const GtkCssImageConicColorStop *stop1 = &conic1->color_stops[i];
const GtkCssImageConicColorStop *stop2 = &conic2->color_stops[i];
if (!_gtk_css_value_equal0 (stop1->offset, stop2->offset) ||
!_gtk_css_value_equal (stop1->color, stop2->color))
return FALSE;
}
return TRUE;
}
static void
gtk_css_image_conic_dispose (GObject *object)
{
GtkCssImageConic *self = GTK_CSS_IMAGE_CONIC (object);
guint i;
for (i = 0; i < self->n_stops; i ++)
{
GtkCssImageConicColorStop *stop = &self->color_stops[i];
_gtk_css_value_unref (stop->color);
if (stop->offset)
_gtk_css_value_unref (stop->offset);
}
g_free (self->color_stops);
g_clear_pointer (&self->center, gtk_css_value_unref);
g_clear_pointer (&self->rotation, gtk_css_value_unref);
G_OBJECT_CLASS (gtk_css_image_conic_parent_class)->dispose (object);
}
static gboolean
gtk_css_image_conic_is_computed (GtkCssImage *image)
{
GtkCssImageConic *self = GTK_CSS_IMAGE_CONIC (image);
guint i;
gboolean computed = TRUE;
computed = !self->center || gtk_css_value_is_computed (self->center);
computed &= !self->rotation || gtk_css_value_is_computed (self->rotation);
for (i = 0; i < self->n_stops; i ++)
{
const GtkCssImageConicColorStop *stop = &self->color_stops[i];
if (stop->offset && !gtk_css_value_is_computed (stop->offset))
{
computed = FALSE;
break;
}
if (!gtk_css_value_is_computed (stop->color))
{
computed = FALSE;
break;
}
}
return computed;
}
static void
gtk_css_image_conic_class_init (GtkCssImageConicClass *klass)
{
GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
image_class->snapshot = gtk_css_image_conic_snapshot;
image_class->parse = gtk_css_image_conic_parse;
image_class->print = gtk_css_image_conic_print;
image_class->compute = gtk_css_image_conic_compute;
image_class->equal = gtk_css_image_conic_equal;
image_class->transition = gtk_css_image_conic_transition;
image_class->is_computed = gtk_css_image_conic_is_computed;
object_class->dispose = gtk_css_image_conic_dispose;
}
static void
gtk_css_image_conic_init (GtkCssImageConic *self)
{
}

View File

@ -0,0 +1,64 @@
/*
* Copyright © 2012 Red Hat Inc.
*
* 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: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_CSS_IMAGE_CONIC_PRIVATE_H__
#define __GTK_CSS_IMAGE_CONIC_PRIVATE_H__
#include "gtk/gtkcssimageprivate.h"
#include "gtk/gtkcssvalueprivate.h"
G_BEGIN_DECLS
#define GTK_TYPE_CSS_IMAGE_CONIC (gtk_css_image_conic_get_type ())
#define GTK_CSS_IMAGE_CONIC(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_IMAGE_CONIC, GtkCssImageConic))
#define GTK_CSS_IMAGE_CONIC_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_IMAGE_CONIC, GtkCssImageConicClass))
#define GTK_IS_CSS_IMAGE_CONIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_IMAGE_CONIC))
#define GTK_IS_CSS_IMAGE_CONIC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_IMAGE_CONIC))
#define GTK_CSS_IMAGE_CONIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_IMAGE_CONIC, GtkCssImageConicClass))
typedef struct _GtkCssImageConic GtkCssImageConic;
typedef struct _GtkCssImageConicClass GtkCssImageConicClass;
typedef struct _GtkCssImageConicColorStop GtkCssImageConicColorStop;
struct _GtkCssImageConicColorStop {
GtkCssValue *offset;
GtkCssValue *color;
};
struct _GtkCssImageConic
{
GtkCssImage parent;
GtkCssValue *center;
GtkCssValue *rotation;
guint n_stops;
GtkCssImageConicColorStop *color_stops;
};
struct _GtkCssImageConicClass
{
GtkCssImageClass parent_class;
};
GType gtk_css_image_conic_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GTK_CSS_IMAGE_CONIC_PRIVATE_H__ */

View File

@ -2249,6 +2249,66 @@ gtk_snapshot_append_repeating_linear_gradient (GtkSnapshot *snapshot,
gtk_snapshot_append_node_internal (snapshot, node);
}
/**
* gtk_snapshot_append_conic_gradient:
* @snapshot: a #GtkSnapshot
* @bounds: the rectangle to render the gradient into
* @center: the center point of the conic gradient
* @rotation: the clockwise rotation in degrees of the starting angle. 0 means the
* starting angle is the top.
* @stops: (array length=n_stops): a pointer to an array of #GskColorStop defining the gradient
* @n_stops: the number of elements in @stops
*
* Appends a conic gradient node with the given stops to @snapshot.
*/
void
gtk_snapshot_append_conic_gradient (GtkSnapshot *snapshot,
const graphene_rect_t *bounds,
const graphene_point_t *center,
float rotation,
const GskColorStop *stops,
gsize n_stops)
{
GskRenderNode *node;
graphene_rect_t real_bounds;
float dx, dy;
const GdkRGBA *first_color;
gboolean need_gradient = FALSE;
int i;
g_return_if_fail (snapshot != NULL);
g_return_if_fail (center != NULL);
g_return_if_fail (stops != NULL);
g_return_if_fail (n_stops > 1);
gtk_snapshot_ensure_translate (snapshot, &dx, &dy);
graphene_rect_offset_r (bounds, dx, dy, &real_bounds);
first_color = &stops[0].color;
for (i = 0; i < n_stops; i ++)
{
if (!gdk_rgba_equal (first_color, &stops[i].color))
{
need_gradient = TRUE;
break;
}
}
if (need_gradient)
node = gsk_conic_gradient_node_new (&real_bounds,
&GRAPHENE_POINT_INIT(
center->x + dx,
center->y + dy
),
rotation,
stops,
n_stops);
else
node = gsk_color_node_new (first_color, &real_bounds);
gtk_snapshot_append_node_internal (snapshot, node);
}
/**
* gtk_snapshot_append_radial_gradient:
* @snapshot: a #GtkSnapshot

View File

@ -191,6 +191,13 @@ void gtk_snapshot_append_repeating_radial_gradient (GtkSnapshot
const GskColorStop *stops,
gsize n_stops);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_append_conic_gradient (GtkSnapshot *snapshot,
const graphene_rect_t *bounds,
const graphene_point_t *center,
float rotation,
const GskColorStop *stops,
gsize n_stops);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_append_border (GtkSnapshot *snapshot,
const GskRoundedRect *outline,
const float border_width[4],

View File

@ -133,6 +133,7 @@ create_list_model_for_render_node (GskRenderNode *node)
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_BORDER_NODE:
case GSK_INSET_SHADOW_NODE:
case GSK_OUTSET_SHADOW_NODE:
@ -259,6 +260,8 @@ node_type_name (GskRenderNodeType type)
return "Radial Gradient";
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
return "Repeating Radial Gradient";
case GSK_CONIC_GRADIENT_NODE:
return "Conic Gradient";
case GSK_BORDER_NODE:
return "Border";
case GSK_TEXTURE_NODE:
@ -308,6 +311,7 @@ node_name (GskRenderNode *node)
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_BORDER_NODE:
case GSK_INSET_SHADOW_NODE:
case GSK_OUTSET_SHADOW_NODE:
@ -729,6 +733,44 @@ populate_render_node_properties (GtkListStore *store,
}
break;
case GSK_CONIC_GRADIENT_NODE:
{
const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
const float rotation = gsk_conic_gradient_node_get_rotation (node);
const gsize n_stops = gsk_conic_gradient_node_get_n_color_stops (node);
const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
gsize i;
GString *s;
GdkTexture *texture;
tmp = g_strdup_printf ("%.2f, %.2f", center->x, center->y);
add_text_row (store, "Center", tmp);
g_free (tmp);
tmp = g_strdup_printf ("%.2f", rotation);
add_text_row (store, "Rotation", tmp);
g_free (tmp);
s = g_string_new ("");
for (i = 0; i < n_stops; i++)
{
tmp = gdk_rgba_to_string (&stops[i].color);
g_string_append_printf (s, "%.2f, %s\n", stops[i].offset, tmp);
g_free (tmp);
}
texture = get_linear_gradient_texture (n_stops, stops);
gtk_list_store_insert_with_values (store, NULL, -1,
0, "Color Stops",
1, s->str,
2, TRUE,
3, texture,
-1);
g_string_free (s, TRUE);
g_object_unref (texture);
}
break;
case GSK_TEXT_NODE:
{
const PangoFont *font = gsk_text_node_get_font (node);

View File

@ -63,6 +63,7 @@ gtk_private_sources = files([
'gtkcssfontfeaturesvalue.c',
'gtkcssfontvariationsvalue.c',
'gtkcssimage.c',
'gtkcssimageconic.c',
'gtkcssimagecrossfade.c',
'gtkcssimagefallback.c',
'gtkcssimageicontheme.c',