snapshot: Implement gtk_snapshot_clips_rect()

And use this to cull widgets and gadgets that are completely outside the
clip region.

A potential optimization is to apply this clip region to cairo contexts
created with gtk_snapshot_append_cairo_node(), but for that we'd need to
apply the inverse matrix to the clip region, and that causes rounding
errors.

Plus, I hope that cairo drawing becomes exceedingly rare so it won't be
used for the whole widget factory like today (which might also explain
why no culling happens in the widget factory outside the header bar.
This commit is contained in:
Benjamin Otte 2016-11-17 03:31:15 +01:00
parent 28b32d336f
commit ab60cbd86a
3 changed files with 52 additions and 29 deletions

View File

@ -55,6 +55,26 @@ gtk_snapshot_state_set_transform (GtkSnapshotState *state,
const graphene_matrix_t *transform)
{
graphene_matrix_init_from_matrix (&state->transform, transform);
state->world_is_valid = FALSE;
}
static const graphene_matrix_t *
gtk_snapshot_state_get_world_transform (GtkSnapshotState *state)
{
if (!state->world_is_valid)
{
if (state->parent)
graphene_matrix_multiply (gtk_snapshot_state_get_world_transform (state->parent),
&state->transform,
&state->world_transform);
else
graphene_matrix_init_from_matrix (&state->world_transform, &state->transform);
state->world_is_valid = TRUE;
}
return &state->world_transform;
}
void
@ -140,34 +160,6 @@ gtk_snapshot_get_renderer (const GtkSnapshot *state)
return state->renderer;
}
#if 0
GskRenderNode *
gtk_snapshot_create_render_node (const GtkSnapshot *state,
const char *name,
...)
{
GskRenderNode *node;
node = gsk_renderer_create_render_node (state->renderer);
if (name)
{
va_list args;
char *str;
va_start (args, name);
str = g_strdup_vprintf (name, args);
va_end (args);
gsk_render_node_set_name (node, str);
g_free (str);
}
return node;
}
#endif
void
gtk_snapshot_set_transform (GtkSnapshot *snapshot,
const graphene_matrix_t *transform)
@ -325,11 +317,38 @@ gtk_snapshot_push_cairo_node (GtkSnapshot *state,
return gsk_render_node_get_draw_context (node, state->renderer);
}
static void
rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
const graphene_rect_t *graphene)
{
cairo->x = floorf (graphene->origin.x);
cairo->y = floorf (graphene->origin.y);
cairo->width = ceilf (graphene->origin.x + graphene->size.width) - cairo->x;
cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
}
gboolean
gtk_snapshot_clips_rect (GtkSnapshot *snapshot,
const graphene_rect_t *bounds)
{
return FALSE;
cairo_rectangle_int_t rect;
if (snapshot->state)
{
const graphene_matrix_t *world;
graphene_rect_t transformed;
world = gtk_snapshot_state_get_world_transform (snapshot->state);
graphene_matrix_transform_bounds (world, bounds, &transformed);
rectangle_init_from_graphene (&rect, &transformed);
}
else
{
rectangle_init_from_graphene (&rect, bounds);
}
return cairo_region_contains_rectangle (snapshot->clip_region, &rect) == CAIRO_REGION_OVERLAP_OUT;
}
void

View File

@ -30,6 +30,8 @@ struct _GtkSnapshotState {
GskRenderNode *node;
graphene_matrix_t transform;
graphene_matrix_t world_transform;
guint world_is_valid : 1;
};
struct _GtkSnapshot {

View File

@ -15657,6 +15657,8 @@ gtk_widget_snapshot (GtkWidget *widget,
gtk_widget_get_clip (widget, &clip);
_gtk_widget_get_allocation (widget, &alloc);
graphene_rect_init (&bounds, alloc.x - clip.x, alloc.y - clip.y, clip.width, clip.height);
if (gtk_snapshot_clips_rect (snapshot, &bounds))
return;
/* Compatibility mode: if the widget does not have a render node, we draw
* using gtk_widget_draw() on a temporary node