static void gtk_css_image_linear_snapshot (GtkCssImage *image, GtkSnapshot *snapshot, double width, double height) { GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); GskColorStop *stops; 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) { /* special casing the regular cases here so we don't get rounding errors */ switch (linear->side) { case 1 << GTK_CSS_RIGHT: angle = 90; break; case 1 << GTK_CSS_LEFT: angle = 270; break; case 1 << GTK_CSS_TOP: angle = 0; break; case 1 << GTK_CSS_BOTTOM: angle = 180; break; default: angle = atan2 (linear->side & 1 << GTK_CSS_TOP ? -width : width, linear->side & 1 << GTK_CSS_LEFT ? -height : height); angle = 180 * angle / G_PI + 90; break; } } else { angle = _gtk_css_number_value_get (linear->angle, 100); } gtk_css_image_linear_compute_start_point (angle, width, height, &x, &y); length = sqrt (x * x + y * y); gtk_css_image_linear_get_start_end (linear, length, &start, &end); if (start == end) { /* repeating gradients with all color stops sharing the same offset * get the color of the last color stop */ GtkCssImageLinearColorStop *stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, linear->stops->len - 1); gtk_snapshot_append_color_node (snapshot, _gtk_css_rgba_value_get_rgba (stop->color), &GRAPHENE_RECT_INIT (0, 0, width, height), "RepeatingLinearGradient<degenerate>"); return; } offset = start; last = -1; stops = g_newa (GskColorStop, linear->stops->len); for (i = 0; i < linear->stops->len; i++) { GtkCssImageLinearColorStop *stop; double pos, step; stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, i); if (stop->offset == NULL) { if (i == 0) pos = 0.0; else if (i + 1 == linear->stops->len) pos = 1.0; else continue; } else pos = _gtk_css_number_value_get (stop->offset, length) / length; pos = MAX (pos, offset); step = (pos - offset) / (i - last); for (last = last + 1; last <= i; last++) { stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, last); offset += step; stops[last].offset = (offset - start) / (end - start); stops[last].color = *_gtk_css_rgba_value_get_rgba (stop->color); } offset = pos; last = i; } gtk_snapshot_get_offset (snapshot, &off_x, &off_y); 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); }
static void gtk_css_image_linear_draw (GtkCssImage *image, cairo_t *cr, double width, double height) { GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); cairo_pattern_t *pattern; 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; if (linear->side) { /* special casing the regular cases here so we don't get rounding errors */ switch (linear->side) { case 1 << GTK_CSS_RIGHT: angle = 90; break; case 1 << GTK_CSS_LEFT: angle = 270; break; case 1 << GTK_CSS_TOP: angle = 0; break; case 1 << GTK_CSS_BOTTOM: angle = 180; break; default: angle = atan2 (linear->side & 1 << GTK_CSS_TOP ? -width : width, linear->side & 1 << GTK_CSS_LEFT ? -height : height); angle = 180 * angle / G_PI + 90; break; } } else { angle = _gtk_css_number_value_get (linear->angle, 100); } gtk_css_image_linear_compute_start_point (angle, width, height, &x, &y); 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; for (i = 0; i < linear->stops->len; i++) { GtkCssImageLinearColorStop *stop; double pos, step; stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, i); if (stop->offset == NULL) { if (i == 0) pos = 0.0; else if (i + 1 == linear->stops->len) pos = 1.0; else continue; } else pos = _gtk_css_number_value_get (stop->offset, length) / length; pos = MAX (pos, offset); 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); } 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); cairo_pattern_destroy (pattern); }