diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt index c0448644b4..59e1a3938d 100644 --- a/docs/reference/gsk/gsk4-sections.txt +++ b/docs/reference/gsk/gsk4-sections.txt @@ -33,6 +33,8 @@ GskScalingFilter gsk_render_node_set_scaling_filters gsk_render_node_set_name gsk_color_node_new +gsk_linear_gradient_node_new +gsk_repeating_linear_gradient_node_new gsk_texture_node_new gsk_cairo_node_new gsk_cairo_node_get_draw_context diff --git a/gsk/gskenums.h b/gsk/gskenums.h index d647495d6d..4e0e0bf35d 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -28,6 +28,9 @@ * @GSK_CONTAINER_NODE: A node containing a stack of children * @GSK_CAIRO_NODE: A node drawing a #cairo_surface_t * @GSK_COLOR_NODE: A node drawing a single color rectangle + * @GSK_LINEAR_GRADIENT_NODE: A node drawing a linear gradient + * @GSK_REPEATING_LINEAR_GRADIENT_NODE: A node drawing a repeating + * linear gradient * @GSK_TEXTURE_NODE: A node drawing a #GskTexture * @GSK_TRANSFORM_NODE: A node that renders its child after applying a * matrix transform @@ -45,6 +48,8 @@ typedef enum { GSK_CONTAINER_NODE, GSK_CAIRO_NODE, GSK_COLOR_NODE, + GSK_LINEAR_GRADIENT_NODE, + GSK_REPEATING_LINEAR_GRADIENT_NODE, GSK_TEXTURE_NODE, GSK_TRANSFORM_NODE, GSK_OPACITY_NODE, diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index 5d92c73729..7562fb92ef 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -33,6 +33,13 @@ G_BEGIN_DECLS #define GSK_IS_RENDER_NODE(obj) ((obj) != NULL) typedef struct _GskRenderNode GskRenderNode; +typedef struct _GskColorStop GskColorStop; + +struct _GskColorStop +{ + double offset; + GdkRGBA color; +}; GDK_AVAILABLE_IN_3_90 GType gsk_render_node_get_type (void) G_GNUC_CONST; @@ -53,6 +60,19 @@ GDK_AVAILABLE_IN_3_90 GskRenderNode * gsk_texture_node_new (GskTexture *texture, const graphene_rect_t *bounds); +GDK_AVAILABLE_IN_3_90 +GskRenderNode * gsk_linear_gradient_node_new (const graphene_rect_t *bounds, + const graphene_point_t *start, + const graphene_point_t *end, + const GskColorStop *color_stops, + gsize n_color_stops); +GDK_AVAILABLE_IN_3_90 +GskRenderNode * gsk_repeating_linear_gradient_node_new (const graphene_rect_t *bounds, + const graphene_point_t *start, + const graphene_point_t *end, + const GskColorStop *color_stops, + gsize n_color_stops); + GDK_AVAILABLE_IN_3_90 GskRenderNode * gsk_cairo_node_new (const graphene_rect_t *bounds); GDK_AVAILABLE_IN_3_90 diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 90f90f184a..5ab0d22beb 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -115,6 +115,162 @@ gsk_color_node_new (const GdkRGBA *rgba, return &self->render_node; } +/*** GSK_LINEAR_GRADIENT_NODE ***/ + +typedef struct _GskLinearGradientNode GskLinearGradientNode; + +struct _GskLinearGradientNode +{ + GskRenderNode render_node; + + graphene_rect_t bounds; + + graphene_point_t start; + graphene_point_t end; + + GskColorStop *stops; + gsize n_stops; +}; + +static void +gsk_linear_gradient_node_finalize (GskRenderNode *node) +{ + GskLinearGradientNode *self = (GskLinearGradientNode *) node; + + g_free (self->stops); +} + +static void +gsk_linear_gradient_node_make_immutable (GskRenderNode *node) +{ +} + +static void +gsk_linear_gradient_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskLinearGradientNode *self = (GskLinearGradientNode *) node; + cairo_pattern_t *pattern; + gsize i; + + pattern = cairo_pattern_create_linear (self->start.x, self->start.y, + self->end.x, self->end.y); + + if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE) + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + + for (i = 0; i < self->n_stops; i++) + { + cairo_pattern_add_color_stop_rgba (pattern, + self->stops[i].offset, + self->stops[i].color.red, + self->stops[i].color.green, + self->stops[i].color.blue, + self->stops[i].color.alpha); + } + + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + cairo_rectangle (cr, + self->bounds.origin.x, self->bounds.origin.y, + self->bounds.size.width, self->bounds.size.height); + cairo_fill (cr); +} + +static void +gsk_linear_gradient_node_get_bounds (GskRenderNode *node, + graphene_rect_t *bounds) +{ + GskLinearGradientNode *self = (GskLinearGradientNode *) node; + + graphene_rect_init_from_rect (bounds, &self->bounds); +} + +static const GskRenderNodeClass GSK_LINEAR_GRADIENT_NODE_CLASS = { + GSK_LINEAR_GRADIENT_NODE, + sizeof (GskLinearGradientNode), + "GskLinearGradientNode", + gsk_linear_gradient_node_finalize, + gsk_linear_gradient_node_make_immutable, + gsk_linear_gradient_node_draw, + gsk_linear_gradient_node_get_bounds +}; + +static const GskRenderNodeClass GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS = { + GSK_REPEATING_LINEAR_GRADIENT_NODE, + sizeof (GskLinearGradientNode), + "GskLinearGradientNode", + gsk_linear_gradient_node_finalize, + gsk_linear_gradient_node_make_immutable, + gsk_linear_gradient_node_draw, + gsk_linear_gradient_node_get_bounds +}; + +/** + * gsk_linear_gradient_node_new: + * @linear_gradient: the #GskLinearGradient + * @bounds: the rectangle to render the linear_gradient into + * + * Creates a #GskRenderNode that will render the given + * @linear_gradient into the area given by @bounds. + * + * Returns: A new #GskRenderNode + * + * Since: 3.90 + */ +GskRenderNode * +gsk_linear_gradient_node_new (const graphene_rect_t *bounds, + const graphene_point_t *start, + const graphene_point_t *end, + const GskColorStop *color_stops, + gsize n_color_stops) +{ + GskLinearGradientNode *self; + + g_return_val_if_fail (bounds != NULL, NULL); + g_return_val_if_fail (start != NULL, NULL); + g_return_val_if_fail (end != NULL, NULL); + g_return_val_if_fail (color_stops != NULL, NULL); + + self = (GskLinearGradientNode *) gsk_render_node_new (&GSK_LINEAR_GRADIENT_NODE_CLASS); + + graphene_rect_init_from_rect (&self->bounds, bounds); + graphene_point_init_from_point (&self->start, start); + graphene_point_init_from_point (&self->end, end); + + self->stops = g_memdup (color_stops, sizeof (GskColorStop) * n_color_stops); + self->n_stops = n_color_stops; + + return &self->render_node; +} + +GskRenderNode * +gsk_repeating_linear_gradient_node_new (const graphene_rect_t *bounds, + const graphene_point_t *start, + const graphene_point_t *end, + const GskColorStop *color_stops, + gsize n_color_stops) +{ + GskLinearGradientNode *self; + + g_return_val_if_fail (bounds != NULL, NULL); + g_return_val_if_fail (start != NULL, NULL); + g_return_val_if_fail (end != NULL, NULL); + g_return_val_if_fail (color_stops != NULL, NULL); + + self = (GskLinearGradientNode *) gsk_render_node_new (&GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS); + + graphene_rect_init_from_rect (&self->bounds, bounds); + graphene_point_init_from_point (&self->start, start); + graphene_point_init_from_point (&self->end, end); + + self->stops = g_memdup (color_stops, sizeof (GskColorStop) * n_color_stops); + self->n_stops = n_color_stops; + + return &self->render_node; +} + /*** GSK_TEXTURE_NODE ***/ typedef struct _GskTextureNode GskTextureNode; diff --git a/gtk/gtkcssimagelinear.c b/gtk/gtkcssimagelinear.c index 07ab3eacf5..0e415d6457 100644 --- a/gtk/gtkcssimagelinear.c +++ b/gtk/gtkcssimagelinear.c @@ -128,19 +128,22 @@ gtk_css_image_linear_compute_start_point (double angle_in_degrees, } static void -gtk_css_image_linear_draw (GtkCssImage *image, - cairo_t *cr, - double width, - double height) +gtk_css_image_linear_snapshot (GtkCssImage *image, + GtkSnapshot *snapshot, + double width, + double height) { GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); - cairo_pattern_t *pattern; + GskColorStop stops[linear->stops->len]; + GskRenderNode *node; + double off_x, off_y; /* snapshot offset */ double angle; /* actual angle of the gradiant line in degrees */ double x, y; /* coordinates of start point */ double length; /* distance in pixels for 100% */ double start, end; /* position of first/last point on gradient line - with gradient line being [0, 1] */ double offset; int i, last; + char *name; if (linear->side) { @@ -177,12 +180,6 @@ gtk_css_image_linear_draw (GtkCssImage *image, length = sqrt (x * x + y * y); gtk_css_image_linear_get_start_end (linear, length, &start, &end); - pattern = cairo_pattern_create_linear (x * (start - 0.5), y * (start - 0.5), - x * (end - 0.5), y * (end - 0.5)); - if (linear->repeating) - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - else - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); offset = start; last = -1; @@ -209,31 +206,45 @@ gtk_css_image_linear_draw (GtkCssImage *image, step = (pos - offset) / (i - last); for (last = last + 1; last <= i; last++) { - const GdkRGBA *rgba; - stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, last); - rgba = _gtk_css_rgba_value_get_rgba (stop->color); offset += step; - cairo_pattern_add_color_stop_rgba (pattern, - (offset - start) / (end - start), - rgba->red, - rgba->green, - rgba->blue, - rgba->alpha); + stops[last].offset = (offset - start) / (end - start); + stops[last].color = *_gtk_css_rgba_value_get_rgba (stop->color); } offset = pos; last = i; } - cairo_rectangle (cr, 0, 0, width, height); - cairo_translate (cr, width / 2, height / 2); - cairo_set_source (cr, pattern); - cairo_fill (cr); + gtk_snapshot_get_offset (snapshot, &off_x, &off_y); - cairo_pattern_destroy (pattern); + if (linear->repeating) + { + node = gsk_repeating_linear_gradient_node_new ( + &GRAPHENE_RECT_INIT (off_x, off_y, width, height), + &GRAPHENE_POINT_INIT (off_x + width / 2 + x * (start - 0.5), off_y + height / 2 + y * (start - 0.5)), + &GRAPHENE_POINT_INIT (off_x + width / 2 + x * (end - 0.5), off_y + height / 2 + y * (end - 0.5)), + stops, + linear->stops->len); + } + else + { + node = gsk_linear_gradient_node_new ( + &GRAPHENE_RECT_INIT (off_x, off_y, width, height), + &GRAPHENE_POINT_INIT (off_x + width / 2 + x * (start - 0.5), off_y + height / 2 + y * (start - 0.5)), + &GRAPHENE_POINT_INIT (off_x + width / 2 + x * (end - 0.5), off_y + height / 2 + y * (end - 0.5)), + stops, + linear->stops->len); + } + name = g_strdup_printf ("%sLinearGradient<%ustops>", linear->repeating ? "Repeating" : "", linear->stops->len); + gsk_render_node_set_name (node, name); + g_free (name); + + gtk_snapshot_append_node (snapshot, node); + + gsk_render_node_unref (node); } @@ -604,7 +615,7 @@ _gtk_css_image_linear_class_init (GtkCssImageLinearClass *klass) GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); - image_class->draw = gtk_css_image_linear_draw; + image_class->snapshot = gtk_css_image_linear_snapshot; image_class->parse = gtk_css_image_linear_parse; image_class->print = gtk_css_image_linear_print; image_class->compute = gtk_css_image_linear_compute; diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c index 5426250e0e..6ac5b3238b 100644 --- a/gtk/inspector/gtktreemodelrendernode.c +++ b/gtk/inspector/gtktreemodelrendernode.c @@ -525,6 +525,8 @@ append_node (GtkTreeModelRenderNode *nodemodel, case GSK_CAIRO_NODE: case GSK_TEXTURE_NODE: case GSK_COLOR_NODE: + case GSK_LINEAR_GRADIENT_NODE: + case GSK_REPEATING_LINEAR_GRADIENT_NODE: /* no children */ break;