[gtk+] gsk: Add GskBorderNode
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] gsk: Add GskBorderNode
- Date: Tue, 20 Dec 2016 17:15:13 +0000 (UTC)
commit 75b76af2214277856efe9629939859deebcf914d
Author: Benjamin Otte <otte redhat com>
Date: Sun Dec 18 18:14:53 2016 +0100
gsk: Add GskBorderNode
The node draws a solid CSS border, which can be used to cover everything
but dashed and dotted borders (double, groove, inset, ...).
For different border styles, we overlay multiple nodes and set their
colors to transparent for sides with non-matching styles.
docs/reference/gsk/gsk4-sections.txt | 1 +
gsk/gskenums.h | 2 +
gsk/gskrendernode.h | 5 +
gsk/gskrendernodeimpl.c | 186 ++++++++++++++++++++++++++++
gsk/gskrendernodeprivate.h | 4 +
gtk/gtkrenderborder.c | 209 ++++++++++++++++++++++++++++---
gtk/gtkrenderborderprivate.h | 4 +-
gtk/inspector/gtktreemodelrendernode.c | 1 +
8 files changed, 389 insertions(+), 23 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 3b47dab..023abe5 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -35,6 +35,7 @@ gsk_render_node_set_name
gsk_color_node_new
gsk_linear_gradient_node_new
gsk_repeating_linear_gradient_node_new
+gsk_border_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 1d46c92..7bad39d 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -31,6 +31,7 @@
* @GSK_LINEAR_GRADIENT_NODE: A node drawing a linear gradient
* @GSK_REPEATING_LINEAR_GRADIENT_NODE: A node drawing a repeating
* linear gradient
+ * @GSK_BORDER_NODE: A node stroking a border around an area
* @GSK_TEXTURE_NODE: A node drawing a #GskTexture
* @GSK_TRANSFORM_NODE: A node that renders its child after applying a
* matrix transform
@@ -51,6 +52,7 @@ typedef enum {
GSK_COLOR_NODE,
GSK_LINEAR_GRADIENT_NODE,
GSK_REPEATING_LINEAR_GRADIENT_NODE,
+ GSK_BORDER_NODE,
GSK_TEXTURE_NODE,
GSK_TRANSFORM_NODE,
GSK_OPACITY_NODE,
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 14fa797..a349b8b 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -74,6 +74,11 @@ GskRenderNode * gsk_repeating_linear_gradient_node_new (const graphene_
gsize n_color_stops);
GDK_AVAILABLE_IN_3_90
+GskRenderNode * gsk_border_node_new (const GskRoundedRect *outline,
+ const float border_width[4],
+ const GdkRGBA border_color[4]);
+
+GDK_AVAILABLE_IN_3_90
GskRenderNode * gsk_cairo_node_new (const graphene_rect_t *bounds);
GDK_AVAILABLE_IN_3_90
cairo_t * gsk_cairo_node_get_draw_context (GskRenderNode *node,
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index ee3cb46..c5d8d61 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -271,6 +271,192 @@ gsk_repeating_linear_gradient_node_new (const graphene_rect_t *bounds,
return &self->render_node;
}
+/*** GSK_BORDER_NODE ***/
+
+typedef struct _GskBorderNode GskBorderNode;
+
+struct _GskBorderNode
+{
+ GskRenderNode render_node;
+
+ GskRoundedRect outline;
+ float border_width[4];
+ GdkRGBA border_color[4];
+};
+
+static void
+gsk_border_node_finalize (GskRenderNode *node)
+{
+}
+
+static void
+gsk_border_node_make_immutable (GskRenderNode *node)
+{
+}
+
+static void
+gsk_border_node_draw (GskRenderNode *node,
+ cairo_t *cr)
+{
+ GskBorderNode *self = (GskBorderNode *) node;
+ GskRoundedRect inside;
+
+ cairo_save (cr);
+
+ gsk_rounded_rect_init_copy (&inside, &self->outline);
+ gsk_rounded_rect_shrink (&inside,
+ self->border_width[0], self->border_width[1],
+ self->border_width[2], self->border_width[3]);
+
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+ gsk_rounded_rect_path (&self->outline, cr);
+ gsk_rounded_rect_path (&inside, cr);
+
+ if (gdk_rgba_equal (&self->border_color[0], &self->border_color[1]) &&
+ gdk_rgba_equal (&self->border_color[0], &self->border_color[2]) &&
+ gdk_rgba_equal (&self->border_color[0], &self->border_color[3]))
+ {
+ gdk_cairo_set_source_rgba (cr, &self->border_color[0]);
+ cairo_fill (cr);
+ }
+ else
+ {
+ const graphene_rect_t *bounds = &self->outline.bounds;
+ /* distance to center "line":
+ * +-------------------------+
+ * | |
+ * | |
+ * | ---this-line--- |
+ * | |
+ * | |
+ * +-------------------------+
+ * That line is equidistant from all sides. It's either horiontal
+ * or vertical, depending on if the rect is wider or taller.
+ * We use the 4 sides spanned up by connecting the line to the corner
+ * points to color the regions of the rectangle differently.
+ * Note that the call to cairo_fill() will add the potential final
+ * segment by closing the path, so we don't have to care.
+ */
+ float dst = MIN (bounds->size.width, bounds->size.height) / 2.0;
+
+ cairo_clip (cr);
+
+ /* top */
+ cairo_move_to (cr, bounds->origin.x + dst, bounds->origin.y + dst);
+ cairo_rel_line_to (cr, - dst, - dst);
+ cairo_rel_line_to (cr, bounds->size.width, 0);
+ cairo_rel_line_to (cr, - dst, dst);
+ gdk_cairo_set_source_rgba (cr, &self->border_color[0]);
+ cairo_fill (cr);
+
+ /* right */
+ cairo_move_to (cr, bounds->origin.x + bounds->size.width - dst, bounds->origin.y + dst);
+ cairo_rel_line_to (cr, dst, - dst);
+ cairo_rel_line_to (cr, 0, bounds->size.height);
+ cairo_rel_line_to (cr, - dst, - dst);
+ gdk_cairo_set_source_rgba (cr, &self->border_color[1]);
+ cairo_fill (cr);
+
+ /* bottom */
+ cairo_move_to (cr, bounds->origin.x + bounds->size.width - dst, bounds->origin.y + bounds->size.height
- dst);
+ cairo_rel_line_to (cr, dst, dst);
+ cairo_rel_line_to (cr, - bounds->size.width, 0);
+ cairo_rel_line_to (cr, dst, - dst);
+ gdk_cairo_set_source_rgba (cr, &self->border_color[2]);
+ cairo_fill (cr);
+
+ /* left */
+ cairo_move_to (cr, bounds->origin.x + dst, bounds->origin.y + bounds->size.height - dst);
+ cairo_rel_line_to (cr, - dst, dst);
+ cairo_rel_line_to (cr, 0, - bounds->size.height);
+ cairo_rel_line_to (cr, dst, dst);
+ gdk_cairo_set_source_rgba (cr, &self->border_color[3]);
+ cairo_fill (cr);
+ }
+
+ cairo_restore (cr);
+}
+
+static void
+gsk_border_node_get_bounds (GskRenderNode *node,
+ graphene_rect_t *bounds)
+{
+ GskBorderNode *self = (GskBorderNode *) node;
+
+ graphene_rect_init_from_rect (bounds, &self->outline.bounds);
+}
+
+static const GskRenderNodeClass GSK_BORDER_NODE_CLASS = {
+ GSK_BORDER_NODE,
+ sizeof (GskBorderNode),
+ "GskBorderNode",
+ gsk_border_node_finalize,
+ gsk_border_node_make_immutable,
+ gsk_border_node_draw,
+ gsk_border_node_get_bounds
+};
+
+const GskRoundedRect *
+gsk_border_node_peek_outline (GskRenderNode *node)
+{
+ GskBorderNode *self = (GskBorderNode *) node;
+
+ return &self->outline;
+}
+
+float
+gsk_border_node_get_width (GskRenderNode *node,
+ guint i)
+{
+ GskBorderNode *self = (GskBorderNode *) node;
+
+ return self->border_width[i];
+}
+
+const GdkRGBA *
+gsk_border_node_peek_color (GskRenderNode *node,
+ guint i)
+{
+ GskBorderNode *self = (GskBorderNode *) node;
+
+ return &self->border_color[i];
+}
+
+/**
+ * gsk_border_node_new:
+ * @outline: a #GskRoundedRect describing the outline of the border
+ * @border_width: the stroke width of the border on the top, right, bottom and
+ * left side respectively.
+ * @border_color: the color used on the top, right, bottom and left side.
+ *
+ * Creates a #GskRenderNode that will stroke a border rectangle inside the
+ * given @outline. The 4 sides of the border can have different widths and
+ * colors.
+ *
+ * Returns: A new #GskRenderNode
+ *
+ * Since: 3.90
+ */
+GskRenderNode *
+gsk_border_node_new (const GskRoundedRect *outline,
+ const float border_width[4],
+ const GdkRGBA border_color[4])
+{
+ GskBorderNode *self;
+
+ g_return_val_if_fail (outline != NULL, NULL);
+ g_return_val_if_fail (border_width != NULL, NULL);
+ g_return_val_if_fail (border_color != NULL, NULL);
+
+ self = (GskBorderNode *) gsk_render_node_new (&GSK_BORDER_NODE_CLASS);
+
+ gsk_rounded_rect_init_copy (&self->outline, outline);
+ memcpy (self->border_width, border_width, sizeof (self->border_width));
+ memcpy (self->border_color, border_color, sizeof (self->border_color));
+
+ return &self->render_node;
+}
+
/*** GSK_TEXTURE_NODE ***/
typedef struct _GskTextureNode GskTextureNode;
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index e945ee7..24e87e8 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -48,6 +48,10 @@ void gsk_render_node_get_bounds (GskRenderNode *node,
graphene_rect_t *frame);
double gsk_opacity_node_get_opacity (GskRenderNode *node);
+const GskRoundedRect * gsk_border_node_peek_outline (GskRenderNode *node);
+float gsk_border_node_get_width (GskRenderNode *node, guint i);
+const GdkRGBA * gsk_border_node_peek_color (GskRenderNode *node, guint i);
+
cairo_surface_t *gsk_cairo_node_get_surface (GskRenderNode *node);
GskTexture *gsk_texture_node_get_texture (GskRenderNode *node);
diff --git a/gtk/gtkrenderborder.c b/gtk/gtkrenderborder.c
index 5a17516..7f739c3 100644
--- a/gtk/gtkrenderborder.c
+++ b/gtk/gtkrenderborder.c
@@ -406,6 +406,44 @@ render_frame_fill (cairo_t *cr,
}
static void
+snapshot_frame_fill (GtkSnapshot *snapshot,
+ const GskRoundedRect *outline,
+ const float border_width[4],
+ const GdkRGBA colors[4],
+ guint hidden_side)
+{
+ GskRoundedRect offset_outline;
+ GskRenderNode *node;
+ double off_x, off_y;
+
+ if (hidden_side)
+ {
+ GdkRGBA real_colors[4];
+ guint i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (hidden_side & (1 << i))
+ real_colors[i] = (GdkRGBA) { 0, 0, 0, 0 };
+ else
+ real_colors[i] = colors[i];
+ }
+
+ snapshot_frame_fill (snapshot, outline, border_width, real_colors, 0);
+ return;
+ }
+
+ gtk_snapshot_get_offset (snapshot, &off_x, &off_y);
+ gsk_rounded_rect_init_copy (&offset_outline, outline);
+ gsk_rounded_rect_offset (&offset_outline, off_x, off_y);
+
+ node = gsk_border_node_new (&offset_outline, border_width, colors);
+ gsk_render_node_set_name (node, "Border");
+ gtk_snapshot_append_node (snapshot, node);
+ gsk_render_node_unref (node);
+}
+
+static void
set_stroke_style (cairo_t *cr,
double line_width,
GtkBorderStyle style,
@@ -540,6 +578,24 @@ render_frame_stroke (cairo_t *cr,
}
static void
+snapshot_frame_stroke (GtkSnapshot *snapshot,
+ GskRoundedRect *outline,
+ const float border_width[4],
+ GdkRGBA colors[4],
+ guint hidden_side,
+ GtkBorderStyle stroke_style)
+{
+ double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] };
+ cairo_t *cr;
+
+ cr = gtk_snapshot_append_cairo_node (snapshot,
+ &outline->bounds,
+ "BorderStroke");
+ render_frame_stroke (cr, outline, double_width, colors, hidden_side, stroke_style);
+ cairo_destroy (cr);
+}
+
+static void
color_shade (const GdkRGBA *color,
gdouble factor,
GdkRGBA *color_return)
@@ -679,6 +735,128 @@ render_border (cairo_t *cr,
cairo_restore (cr);
}
+static void
+snapshot_border (GtkSnapshot *snapshot,
+ GskRoundedRect *border_box,
+ const float border_width[4],
+ GdkRGBA colors[4],
+ GtkBorderStyle border_style[4])
+{
+ guint hidden_side = 0;
+ guint i, j;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (hidden_side & (1 << i))
+ continue;
+
+ /* NB: code below divides by this value */
+ /* a border smaller than this will not noticably modify
+ * pixels on screen, and since we don't compare with 0,
+ * we'll use this value */
+ if (border_width[i] < 1.0 / 1024)
+ continue;
+
+ switch (border_style[i])
+ {
+ case GTK_BORDER_STYLE_NONE:
+ case GTK_BORDER_STYLE_HIDDEN:
+ case GTK_BORDER_STYLE_SOLID:
+ break;
+ case GTK_BORDER_STYLE_INSET:
+ if (i == 1 || i == 2)
+ color_shade (&colors[i], 1.8, &colors[i]);
+ break;
+ case GTK_BORDER_STYLE_OUTSET:
+ if (i == 0 || i == 3)
+ color_shade (&colors[i], 1.8, &colors[i]);
+ break;
+ case GTK_BORDER_STYLE_DOTTED:
+ case GTK_BORDER_STYLE_DASHED:
+ {
+ guint dont_draw = hidden_side;
+
+ for (j = 0; j < 4; j++)
+ {
+ if (border_style[j] == border_style[i])
+ hidden_side |= (1 << j);
+ else
+ dont_draw |= (1 << j);
+ }
+
+ snapshot_frame_stroke (snapshot, border_box, border_width, colors, dont_draw, border_style[i]);
+ }
+ break;
+ case GTK_BORDER_STYLE_DOUBLE:
+ {
+ GskRoundedRect other_box;
+ float other_border[4];
+ guint dont_draw = hidden_side;
+
+ for (j = 0; j < 4; j++)
+ {
+ if (border_style[j] == GTK_BORDER_STYLE_DOUBLE)
+ hidden_side |= (1 << j);
+ else
+ dont_draw |= (1 << j);
+
+ other_border[j] = border_width[j] / 3;
+ }
+
+ snapshot_frame_fill (snapshot, border_box, other_border, colors, dont_draw);
+
+ other_box = *border_box;
+ gsk_rounded_rect_shrink (&other_box,
+ 2 * other_border[GTK_CSS_TOP],
+ 2 * other_border[GTK_CSS_RIGHT],
+ 2 * other_border[GTK_CSS_BOTTOM],
+ 2 * other_border[GTK_CSS_LEFT]);
+ snapshot_frame_fill (snapshot, &other_box, other_border, colors, dont_draw);
+ }
+ break;
+ case GTK_BORDER_STYLE_GROOVE:
+ case GTK_BORDER_STYLE_RIDGE:
+ {
+ GskRoundedRect other_box;
+ GdkRGBA other_colors[4];
+ guint dont_draw = hidden_side;
+ float other_border[4];
+
+ for (j = 0; j < 4; j++)
+ {
+ other_colors[j] = colors[j];
+ if ((j == 0 || j == 3) ^ (border_style[j] == GTK_BORDER_STYLE_RIDGE))
+ color_shade (&other_colors[j], 1.8, &other_colors[j]);
+ else
+ color_shade (&colors[j], 1.8, &colors[j]);
+ if (border_style[j] == GTK_BORDER_STYLE_GROOVE ||
+ border_style[j] == GTK_BORDER_STYLE_RIDGE)
+ hidden_side |= (1 << j);
+ else
+ dont_draw |= (1 << j);
+ other_border[j] = border_width[j] / 2;
+ }
+
+ snapshot_frame_fill (snapshot, border_box, other_border, colors, dont_draw);
+
+ other_box = *border_box;
+ gsk_rounded_rect_shrink (&other_box,
+ other_border[GTK_CSS_TOP],
+ other_border[GTK_CSS_RIGHT],
+ other_border[GTK_CSS_BOTTOM],
+ other_border[GTK_CSS_LEFT]);
+ snapshot_frame_fill (snapshot, &other_box, other_border, other_colors, dont_draw);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ snapshot_frame_fill (snapshot, border_box, border_width, colors, hidden_side);
+}
+
gboolean
gtk_css_style_render_has_border (GtkCssStyle *style)
{
@@ -744,13 +922,13 @@ gtk_css_style_render_border (GtkCssStyle *style,
void
gtk_css_style_snapshot_border (GtkCssStyle *style,
- GtkSnapshot *state,
+ GtkSnapshot *snapshot,
gdouble width,
gdouble height,
GtkJunctionSides junction)
{
GtkBorderImage border_image;
- double border_width[4];
+ float border_width[4];
graphene_rect_t bounds;
cairo_t *cr;
@@ -763,10 +941,12 @@ gtk_css_style_snapshot_border (GtkCssStyle *style,
if (gtk_border_image_init (&border_image, style))
{
- cr = gtk_snapshot_append_cairo_node (state,
+ double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] };
+
+ cr = gtk_snapshot_append_cairo_node (snapshot,
&bounds,
"Border Image");
- gtk_border_image_render (&border_image, border_width, cr, 0, 0, width, height);
+ gtk_border_image_render (&border_image, double_width, cr, 0, 0, width, height);
cairo_destroy (cr);
}
else
@@ -782,10 +962,6 @@ gtk_css_style_snapshot_border (GtkCssStyle *style,
border_width[3] == 0)
return;
- cr = gtk_snapshot_append_cairo_node (state,
- &bounds,
- "Border");
-
border_style[0] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style,
GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
border_style[1] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style,
GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE));
border_style[2] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style,
GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE));
@@ -799,9 +975,7 @@ gtk_css_style_snapshot_border (GtkCssStyle *style,
_gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
_gtk_rounded_box_apply_border_radius_for_style (&border_box, style, junction);
- render_border (cr, &border_box, border_width, colors, border_style);
-
- cairo_destroy (cr);
+ snapshot_border (snapshot, &border_box, border_width, colors, border_style);
}
}
@@ -904,20 +1078,19 @@ gtk_css_style_render_outline (GtkCssStyle *style,
void
gtk_css_style_snapshot_outline (GtkCssStyle *style,
- GtkSnapshot *state,
+ GtkSnapshot *snapshot,
gdouble width,
gdouble height)
{
GtkBorderStyle border_style[4];
GskRoundedRect border_box;
- double border_width[4];
+ float border_width[4];
GdkRGBA colors[4];
border_style[0] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style,
GTK_CSS_PROPERTY_OUTLINE_STYLE));
if (border_style[0] != GTK_BORDER_STYLE_NONE)
{
cairo_rectangle_t rect;
- cairo_t *cr;
compute_outline_rect (style, 0, 0, width, height, &rect);
@@ -930,13 +1103,7 @@ gtk_css_style_snapshot_outline (GtkCssStyle *style,
_gtk_rounded_box_init_rect (&border_box, rect.x, rect.y, rect.width, rect.height);
_gtk_rounded_box_apply_outline_radius_for_style (&border_box, style, GTK_JUNCTION_NONE);
- cr = gtk_snapshot_append_cairo_node (state,
- &GRAPHENE_RECT_INIT (rect.x, rect.y, rect.width, rect.height),
- "Outline");
-
- render_border (cr, &border_box, border_width, colors, border_style);
-
- cairo_destroy (cr);
+ snapshot_border (snapshot, &border_box, border_width, colors, border_style);
}
}
diff --git a/gtk/gtkrenderborderprivate.h b/gtk/gtkrenderborderprivate.h
index f89cdcb..4de9d6d 100644
--- a/gtk/gtkrenderborderprivate.h
+++ b/gtk/gtkrenderborderprivate.h
@@ -44,7 +44,7 @@ gboolean gtk_css_style_render_border_get_clip (GtkCssStyle
gdouble height,
GdkRectangle *out_clip)
G_GNUC_WARN_UNUSED_RESULT;
void gtk_css_style_snapshot_border (GtkCssStyle *style,
- GtkSnapshot *state,
+ GtkSnapshot *snapshot,
gdouble width,
gdouble height,
GtkJunctionSides junction);
@@ -57,7 +57,7 @@ void gtk_css_style_render_outline (GtkCssStyle
gdouble width,
gdouble height);
void gtk_css_style_snapshot_outline (GtkCssStyle *style,
- GtkSnapshot *state,
+ GtkSnapshot *snapshot,
gdouble width,
gdouble height);
gboolean gtk_css_style_render_outline_get_clip (GtkCssStyle *style,
diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c
index fff0a06..96b0d94 100644
--- a/gtk/inspector/gtktreemodelrendernode.c
+++ b/gtk/inspector/gtktreemodelrendernode.c
@@ -527,6 +527,7 @@ append_node (GtkTreeModelRenderNode *nodemodel,
case GSK_COLOR_NODE:
case GSK_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+ case GSK_BORDER_NODE:
/* no children */
break;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]