From 71cb7c2063a85d4f8d5424e466319f30be318913 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 2 Dec 2020 21:48:35 +0100 Subject: [PATCH 1/4] rendernodeparser: Split out a function --- gsk/gskrendernodeparser.c | 61 ++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index fd62b6d4c3..61f94853a1 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -2184,6 +2184,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 +2318,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 +2326,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 +2336,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 +2348,8 @@ 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, ", "); - - 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_radial_gradient_node_get_color_stops (node, NULL), + gsk_radial_gradient_node_get_n_color_stops (node)); end_node (p); } From 55a242bd816893e8b3c777b053a3c5fc0266b5d6 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 3 Dec 2020 00:47:54 +0100 Subject: [PATCH 2/4] gsk: Add GskConicGradientNode --- docs/reference/gsk/gsk4-sections.txt | 5 + gsk/broadway/gskbroadwayrenderer.c | 2 + gsk/gl/gskglrenderer.c | 1 + gsk/gskenums.h | 2 + gsk/gskrendernode.h | 20 ++ gsk/gskrendernodeimpl.c | 338 +++++++++++++++++++++++++++ gsk/gskrendernodeparser.c | 50 ++++ gsk/vulkan/gskvulkanrenderpass.c | 1 + gtk/inspector/recorder.c | 42 ++++ 9 files changed, 461 insertions(+) diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt index f671732880..6cd1fa0d17 100644 --- a/docs/reference/gsk/gsk4-sections.txt +++ b/docs/reference/gsk/gsk4-sections.txt @@ -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 diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c index 80a3f84968..5ac62e7ee0 100644 --- a/gsk/broadway/gskbroadwayrenderer.c +++ b/gsk/broadway/gskbroadwayrenderer.c @@ -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: diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index cd93966d5c..bb09be8bdd 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -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: { diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 0cd0c284fc..243ab67cc8 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -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, diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index dba436afda..76771a9d8e 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -120,6 +120,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()) @@ -145,6 +146,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; @@ -216,6 +218,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 diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index e5228d88c9..44fb918ddd 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -741,6 +741,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 ***/ struct _GskBorderNode @@ -4752,6 +5073,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) @@ -4885,6 +5207,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 = { diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index 61f94853a1..756147adb2 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -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, ¢er }, + { "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, ¢er, 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 }, @@ -2355,6 +2390,21 @@ render_node_print (Printer *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); + } + break; + case GSK_OPACITY_NODE: { start_node (p, "opacity"); diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index 103412487f..d81d38fa33 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -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)); diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index dd9a1de67e..7e07e4413a 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -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); From 8706d69e60bd0dd63aa720cee2522626eb3e3806 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 3 Dec 2020 01:15:53 +0100 Subject: [PATCH 3/4] snapshot: Add gsk_snapshot_append_conic_gradient() --- docs/reference/gtk/gtk4-sections.txt | 1 + gtk/gtksnapshot.c | 60 ++++++++++++++++++++++++++++ gtk/gtksnapshot.h | 7 ++++ 3 files changed, 68 insertions(+) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index c6e8f87a58..a13864c7d1 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -4329,6 +4329,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 diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c index 40122f6338..7c5c744842 100644 --- a/gtk/gtksnapshot.c +++ b/gtk/gtksnapshot.c @@ -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 diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h index 19e0045b17..6125e44ecc 100644 --- a/gtk/gtksnapshot.h +++ b/gtk/gtksnapshot.h @@ -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], From e622013f7e4bf35e41b405b52f8b5bf41f3b5023 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 3 Dec 2020 01:24:45 +0100 Subject: [PATCH 4/4] css: Add support for conic-gradient() This comes complete with animation support. For a good time, try: @keyframes conic { 100% { background-image: conic-gradient(from 1turn, red, lime, blue, yellow, red); } } window { background-image: conic-gradient(red, lime, blue, yellow, red); animation: conic infinite linear 5s; } --- gtk/gtkcssimage.c | 4 +- gtk/gtkcssimageconic.c | 529 ++++++++++++++++++++++++++++++++++ gtk/gtkcssimageconicprivate.h | 64 ++++ gtk/meson.build | 1 + 4 files changed, 597 insertions(+), 1 deletion(-) create mode 100644 gtk/gtkcssimageconic.c create mode 100644 gtk/gtkcssimageconicprivate.h diff --git a/gtk/gtkcssimage.c b/gtk/gtkcssimage.c index 62839d2cf5..616ac74f0a 100644 --- a/gtk/gtkcssimage.c +++ b/gtk/gtkcssimage.c @@ -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 } }; diff --git a/gtk/gtkcssimageconic.c b/gtk/gtkcssimageconic.c new file mode 100644 index 0000000000..b046c0df4e --- /dev/null +++ b/gtk/gtkcssimageconic.c @@ -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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkcssimageconicprivate.h" + +#include + +#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 = ©->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) +{ +} + diff --git a/gtk/gtkcssimageconicprivate.h b/gtk/gtkcssimageconicprivate.h new file mode 100644 index 0000000000..89d9c26739 --- /dev/null +++ b/gtk/gtkcssimageconicprivate.h @@ -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 . + * + * Authors: Benjamin Otte + */ + +#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__ */ diff --git a/gtk/meson.build b/gtk/meson.build index d7f206c231..00f8a500df 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -63,6 +63,7 @@ gtk_private_sources = files([ 'gtkcssfontfeaturesvalue.c', 'gtkcssfontvariationsvalue.c', 'gtkcssimage.c', + 'gtkcssimageconic.c', 'gtkcssimagecrossfade.c', 'gtkcssimagefallback.c', 'gtkcssimageicontheme.c',