Compare commits

...

2 Commits

Author SHA1 Message Date
Georges Basile Stavracas Neto
5a4ade857a Port ClutterText to ClutterPaintNode
The port to use ClutterPaintNodes basically mirrors what
it currently does, with the major benefit of not depending
on cogl_get_draw_framebuffer() anymore.

There are various factors that influence the number of
subnodes:

 * The background color adds a color subnode;
 * The selection adds a clip subnode, and a color subnode,
   or a color and a text subnode;

The simplest case is when the text does not overflow and
has no background color nor selection. In that case, the
render tree is simply:

    [ Dummy ]
        ↓
    [ Text ]

In contrast, the most complex case is when drawing the text
with selection, in which case the render tree looks like:

    [ Dummy ]
        ↓
    [ Clip ]
        ↓
    [ Text ] → [ Clip ]
                  ↓
               [ Color ] → [ Text ]

Since the selection may have another text color, the selected
text must be rendered again, but clipped to only cover the
selection rectangle. This is suboptimal, but it's what the
current code already does anyway.
2018-09-28 21:38:12 -03:00
Georges Basile Stavracas Neto
cc75fc88ee clutter/paint-nodes: Use texture position to draw pango layout
When painting a ClutterText, there are two different aspects that
should be taken into account:

 1. The allocated size of the actor; and
 2. The reported size of the PangoLayout, which may be smaller or
    bigger than (1)

When (2) is bigger than (1), ClutterText has to clip the text to
only draw at the visible contents over the actor surface. In
addition to that, ClutterText also tracks the cursor position,
which makes clipping a bit more complicated.

The current ClutterTextNode.draw() implementation assumes that
the (1) also represents (2), which is not true. This makes
clipping not work.

Fix that by assuming that the position to draw the PangoLayout
is passed as the second rectangle, and the actor size is the
first one.
2018-09-27 17:06:38 -03:00
2 changed files with 118 additions and 65 deletions

View File

@@ -820,8 +820,8 @@ clutter_text_node_draw (ClutterPaintNode *node)
}
cogl_pango_render_layout (tnode->layout,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[4],
op->op.texrect[5],
&tnode->color,
0);

View File

@@ -1746,7 +1746,8 @@ add_selection_rectangle_to_path (ClutterText *text,
/* Draws the selected text, its background, and the cursor */
static void
selection_paint (ClutterText *self)
paint_selection (ClutterText *self,
ClutterPaintNode *node)
{
ClutterTextPrivate *priv = self->priv;
ClutterActor *actor = CLUTTER_ACTOR (self);
@@ -1758,33 +1759,59 @@ selection_paint (ClutterText *self)
if (priv->position == priv->selection_bound)
{
g_autoptr(ClutterPaintNode) cursor_node = NULL;
/* No selection, just draw the cursor */
if (priv->cursor_color_set)
color = &priv->cursor_color;
else
color = &priv->text_color;
cogl_set_source_color4ub (color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cursor_node = clutter_color_node_new (&(ClutterColor) {
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255,
});
clutter_paint_node_set_name (cursor_node, "ClutterText.selection-background");
clutter_paint_node_add_child (node, cursor_node);
cogl_rectangle (priv->cursor_rect.origin.x,
priv->cursor_rect.origin.y,
priv->cursor_rect.origin.x + priv->cursor_rect.size.width,
priv->cursor_rect.origin.y + priv->cursor_rect.size.height);
clutter_paint_node_add_rectangle (cursor_node,
&(ClutterActorBox) {
priv->cursor_rect.origin.x,
priv->cursor_rect.origin.y,
priv->cursor_rect.origin.x + priv->cursor_rect.size.width,
priv->cursor_rect.origin.y + priv->cursor_rect.size.height
});
}
else
{
/* Paint selection background first */
g_autoptr(ClutterPaintNode) selection_background_node = NULL;
g_autoptr(ClutterPaintNode) selection_text_node = NULL;
g_autoptr(ClutterPaintNode) selection_clip_node = NULL;
PangoLayout *layout = clutter_text_get_layout (self);
ClutterActorBox alloc = { 0, };
CoglPath *selection_path = cogl_path_new ();
CoglColor cogl_color = { 0, };
CoglFramebuffer *fb;
fb = cogl_get_draw_framebuffer ();
if (G_UNLIKELY (fb == NULL))
return;
clutter_text_foreach_selection_rectangle (self,
add_selection_rectangle_to_path,
selection_path);
/* Clip against both the actor allocation - we don't want to draw
* the selection rectangle outside the actor - and the selection
* path - we don't want to render the text over the unselected
* text either.
*/
selection_clip_node = clutter_clip_node_new ();
clutter_paint_node_set_name (selection_clip_node, "ClutterText.clip-selection");
clutter_paint_node_add_child (node, selection_clip_node);
clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &alloc);
clutter_actor_box_set_origin (&alloc, 0, 0);
clutter_paint_node_add_rectangle (selection_clip_node, &alloc);
clutter_paint_node_add_path (selection_clip_node, selection_path);
/* Paint selection background */
if (priv->selection_color_set)
@@ -1794,35 +1821,40 @@ selection_paint (ClutterText *self)
else
color = &priv->text_color;
cogl_set_source_color4ub (color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
selection_background_node = clutter_color_node_new (&(ClutterColor) {
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255,
});
clutter_paint_node_set_name (selection_background_node, "ClutterText.selection-background");
clutter_paint_node_add_child (selection_clip_node, selection_background_node);
clutter_text_foreach_selection_rectangle (self,
add_selection_rectangle_to_path,
selection_path);
cogl_path_fill (selection_path);
clutter_paint_node_add_path (selection_background_node, selection_path);
/* Paint selected text */
cogl_framebuffer_push_path_clip (fb, selection_path);
cogl_object_unref (selection_path);
if (priv->selected_text_color_set)
color = &priv->selected_text_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
selection_text_node = clutter_text_node_new (layout,
&(ClutterColor) {
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255,
});
cogl_pango_render_layout (layout, priv->text_x, 0, &cogl_color, 0);
clutter_paint_node_set_name (selection_text_node, "ClutterText.selection-text");
clutter_paint_node_add_child (selection_clip_node, selection_text_node);
cogl_framebuffer_pop_clip (fb);
clutter_paint_node_add_texture_rectangle (selection_text_node,
&alloc,
priv->text_x, priv->text_y,
1, 1);
cogl_object_unref (selection_path);
}
}
@@ -2373,26 +2405,43 @@ clutter_text_compute_layout_offsets (ClutterText *self,
#define TEXT_PADDING 2
static void
clutter_text_paint (ClutterActor *self)
static inline ClutterPaintNode *
create_clip_node (ClutterPaintNode *node,
float width,
float height)
{
g_autoptr(ClutterPaintNode) clip_node = NULL;
clip_node = clutter_clip_node_new ();
clutter_paint_node_set_name (clip_node, "ClutterText.clip");
clutter_paint_node_add_child (node, clip_node);
clutter_paint_node_add_rectangle (clip_node,
&(ClutterActorBox) {
0, 0,
width, height,
});
return g_steal_pointer (&clip_node);
}
static void
clutter_text_paint_node (ClutterActor *self,
ClutterPaintNode *node)
{
g_autoptr(ClutterPaintNode) text_node = NULL;
ClutterText *text = CLUTTER_TEXT (self);
ClutterTextPrivate *priv = text->priv;
CoglFramebuffer *fb;
PangoLayout *layout;
ClutterActorBox alloc = { 0, };
CoglColor color = { 0, };
guint8 real_opacity;
gint text_x = priv->text_x;
gint text_y = priv->text_y;
gboolean clip_set = FALSE;
gboolean bg_color_set = FALSE;
guint n_chars;
float alloc_width;
float alloc_height;
fb = cogl_get_draw_framebuffer ();
/* Note that if anything in this paint method changes it needs to be
reflected in the get_paint_volume implementation which is tightly
tied to the workings of this function */
@@ -2405,6 +2454,7 @@ clutter_text_paint (ClutterActor *self)
g_object_get (self, "background-color-set", &bg_color_set, NULL);
if (bg_color_set)
{
g_autoptr(ClutterPaintNode) background_color_node = NULL;
ClutterColor bg_color;
clutter_actor_get_background_color (self, &bg_color);
@@ -2412,11 +2462,11 @@ clutter_text_paint (ClutterActor *self)
* bg_color.alpha
/ 255;
cogl_set_source_color4ub (bg_color.red,
bg_color.green,
bg_color.blue,
bg_color.alpha);
cogl_rectangle (0, 0, alloc_width, alloc_height);
background_color_node = clutter_color_node_new (&bg_color);
clutter_paint_node_set_name (background_color_node, "ClutterText.background-color");
clutter_paint_node_add_child (node, background_color_node);
clutter_paint_node_add_rectangle (background_color_node, &alloc);
}
/* don't bother painting an empty text actor, unless it's
@@ -2469,8 +2519,7 @@ clutter_text_paint (ClutterActor *self)
pango_layout_get_extents (layout, NULL, &logical_rect);
cogl_framebuffer_push_rectangle_clip (fb, 0, 0, alloc_width, alloc_height);
clip_set = TRUE;
node = create_clip_node (node, alloc_width, alloc_height);
actor_width = alloc_width - 2 * TEXT_PADDING;
text_width = logical_rect.width / PANGO_SCALE;
@@ -2513,12 +2562,8 @@ clutter_text_paint (ClutterActor *self)
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
/* don't clip if the layout managed to fit inside our allocation */
if (logical_rect.width > alloc_width ||
logical_rect.height > alloc_height)
{
cogl_framebuffer_push_rectangle_clip (fb, 0, 0, alloc_width, alloc_height);
clip_set = TRUE;
}
if (logical_rect.width > alloc_width || logical_rect.height > alloc_height)
node = create_clip_node (node, alloc_width, alloc_height);
clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
}
@@ -2541,17 +2586,25 @@ clutter_text_paint (ClutterActor *self)
CLUTTER_NOTE (PAINT, "painting text (text: '%s')",
clutter_text_buffer_get_text (get_buffer (text)));
cogl_color_init_from_4ub (&color,
priv->text_color.red,
priv->text_color.green,
priv->text_color.blue,
real_opacity);
cogl_pango_render_layout (layout, priv->text_x, priv->text_y, &color, 0);
text_node = clutter_text_node_new (layout,
&(ClutterColor) {
priv->text_color.red,
priv->text_color.green,
priv->text_color.blue,
real_opacity
});
selection_paint (text);
clutter_paint_node_set_name (text_node, "ClutterText.text");
clutter_paint_node_add_child (node, text_node);
if (clip_set)
cogl_framebuffer_pop_clip (fb);
clutter_actor_box_set_origin (&alloc, 0, 0);
clutter_paint_node_add_texture_rectangle (text_node,
&alloc,
priv->text_x,
priv->text_y,
1, 1);
paint_selection (text, node);
}
static void
@@ -3548,7 +3601,7 @@ clutter_text_class_init (ClutterTextClass *klass)
gobject_class->dispose = clutter_text_dispose;
gobject_class->finalize = clutter_text_finalize;
actor_class->paint = clutter_text_paint;
actor_class->paint_node = clutter_text_paint_node;
actor_class->get_paint_volume = clutter_text_get_paint_volume;
actor_class->get_preferred_width = clutter_text_get_preferred_width;
actor_class->get_preferred_height = clutter_text_get_preferred_height;