diff --git a/cogl/cogl/cogl-clip-stack.c b/cogl/cogl/cogl-clip-stack.c index 092510714..96eb105a0 100644 --- a/cogl/cogl/cogl-clip-stack.c +++ b/cogl/cogl/cogl-clip-stack.c @@ -295,6 +295,30 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack, return (CoglClipStack *) entry; } +CoglClipStack * +cogl_clip_stack_push_region (CoglClipStack *stack, + cairo_region_t *region) +{ + CoglClipStack *entry; + CoglClipStackRegion *entry_region; + cairo_rectangle_int_t bounds; + + entry_region = _cogl_clip_stack_push_entry (stack, + sizeof (CoglClipStackRegion), + COGL_CLIP_STACK_REGION); + entry = (CoglClipStack *) entry_region; + + cairo_region_get_extents (region, &bounds); + entry->bounds_x0 = bounds.x; + entry->bounds_x1 = bounds.x + bounds.width; + entry->bounds_y0 = bounds.y; + entry->bounds_y1 = bounds.y + bounds.height; + + entry_region->region = cairo_region_reference (region); + + return entry; +} + CoglClipStack * _cogl_clip_stack_ref (CoglClipStack *entry) { @@ -336,6 +360,13 @@ _cogl_clip_stack_unref (CoglClipStack *entry) g_slice_free1 (sizeof (CoglClipStackPrimitive), entry); break; } + case COGL_CLIP_STACK_REGION: + { + CoglClipStackRegion *region = (CoglClipStackRegion *) entry; + cairo_region_destroy (region->region); + g_slice_free1 (sizeof (CoglClipStackRegion), entry); + break; + } default: g_assert_not_reached (); } diff --git a/cogl/cogl/cogl-clip-stack.h b/cogl/cogl/cogl-clip-stack.h index eb2c43282..372e7347a 100644 --- a/cogl/cogl/cogl-clip-stack.h +++ b/cogl/cogl/cogl-clip-stack.h @@ -48,12 +48,14 @@ typedef struct _CoglClipStack CoglClipStack; typedef struct _CoglClipStackRect CoglClipStackRect; typedef struct _CoglClipStackWindowRect CoglClipStackWindowRect; typedef struct _CoglClipStackPrimitive CoglClipStackPrimitive; +typedef struct _CoglClipStackRegion CoglClipStackRegion; typedef enum { COGL_CLIP_STACK_RECT, COGL_CLIP_STACK_WINDOW_RECT, - COGL_CLIP_STACK_PRIMITIVE + COGL_CLIP_STACK_PRIMITIVE, + COGL_CLIP_STACK_REGION, } CoglClipStackType; /* A clip stack consists a list of entries. Each entry has a reference @@ -162,6 +164,13 @@ struct _CoglClipStackPrimitive float bounds_y2; }; +struct _CoglClipStackRegion +{ + CoglClipStack _parent_data; + + cairo_region_t *region; +}; + CoglClipStack * _cogl_clip_stack_push_window_rectangle (CoglClipStack *stack, int x_offset, @@ -189,6 +198,9 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport); +CoglClipStack * +cogl_clip_stack_push_region (CoglClipStack *stack, + cairo_region_t *region); CoglClipStack * _cogl_clip_stack_pop (CoglClipStack *stack); diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c index 602eb0bc0..2bdac109b 100644 --- a/cogl/cogl/cogl-framebuffer.c +++ b/cogl/cogl/cogl-framebuffer.c @@ -1761,6 +1761,19 @@ cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, COGL_FRAMEBUFFER_STATE_CLIP; } +void +cogl_framebuffer_push_region_clip (CoglFramebuffer *framebuffer, + cairo_region_t *region) +{ + framebuffer->clip_stack = + cogl_clip_stack_push_region (framebuffer->clip_stack, + region); + + if (framebuffer->context->current_draw_buffer == framebuffer) + framebuffer->context->current_draw_buffer_changes |= + COGL_FRAMEBUFFER_STATE_CLIP; +} + void cogl_framebuffer_pop_clip (CoglFramebuffer *framebuffer) { diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h index d78bd0079..9979e9082 100644 --- a/cogl/cogl/cogl-framebuffer.h +++ b/cogl/cogl/cogl-framebuffer.h @@ -54,6 +54,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer; #include #include #include +#include #include @@ -624,6 +625,10 @@ cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, float bounds_x2, float bounds_y2); +void +cogl_framebuffer_push_region_clip (CoglFramebuffer *framebuffer, + cairo_region_t *region); + /** * cogl_framebuffer_pop_clip: * @framebuffer: A #CoglFramebuffer pointer diff --git a/cogl/cogl/driver/gl/cogl-clip-stack-gl.c b/cogl/cogl/driver/gl/cogl-clip-stack-gl.c index 34f809a86..31f078f93 100644 --- a/cogl/cogl/driver/gl/cogl-clip-stack-gl.c +++ b/cogl/cogl/driver/gl/cogl-clip-stack-gl.c @@ -115,6 +115,102 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer, GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); } +static void +add_stencil_clip_region (CoglFramebuffer *framebuffer, + cairo_region_t *region, + gboolean merge) +{ + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + CoglMatrix matrix; + int num_rectangles = cairo_region_num_rectangles (region); + int i; + + /* NB: This can be called while flushing the journal so we need + * to be very conservative with what state we change. + */ + _cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry); + _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); + + /* The coordinates in the region are meant to be window coordinates, + * make a matrix that translates those across the viewport, and into + * the default [-1, -1, 1, 1] range. + */ + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, -1, 1, 0); + cogl_matrix_scale (&matrix, + 2.0 / framebuffer->viewport_width, + - 2.0 / framebuffer->viewport_height, + 1); + cogl_matrix_translate (&matrix, + - framebuffer->viewport_x, + - framebuffer->viewport_y, + 0); + + GE( ctx, glEnable (GL_STENCIL_TEST) ); + + GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) ); + GE( ctx, glDepthMask (FALSE) ); + + if (merge) + { + GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x3) ); + GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_INCR) ); + } + else + { + /* Initially disallow everything */ + GE( ctx, glClearStencil (0) ); + GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) ); + + /* Punch out holes to allow the rectangles */ + GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x1) ); + GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE) ); + } + + for (i = 0; i < num_rectangles; i++) + { + cairo_rectangle_int_t rect; + float tl[4], br[4]; + + cairo_region_get_rectangle (region, i, &rect); + + tl[0] = rect.x; + tl[1] = rect.y; + tl[2] = 0.; + tl[3] = 1.; + + br[0] = rect.x + rect.width; + br[1] = rect.y + rect.height; + br[2] = 0.; + br[3] = 1.; + + cogl_matrix_transform_point (&matrix, &tl[0], &tl[1], &tl[2], &tl[3]); + cogl_matrix_transform_point (&matrix, &br[0], &br[1], &br[2], &br[3]); + + _cogl_rectangle_immediate (framebuffer, + ctx->stencil_pipeline, + tl[0], tl[1], br[0], br[1]); + } + + if (merge) + { + /* Subtract one from all pixels in the stencil buffer so that + * only pixels where both the original stencil buffer and the + * region are set will be valid + */ + GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_DECR) ); + _cogl_rectangle_immediate (framebuffer, + ctx->stencil_pipeline, + -1.0, -1.0, 1.0, 1.0); + } + + /* Restore the stencil mode */ + GE (ctx, glDepthMask (TRUE)); + GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE)); + GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); +} + typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, void *user_data); @@ -382,6 +478,23 @@ _cogl_clip_stack_gl_flush (CoglClipStack *stack, } break; } + case COGL_CLIP_STACK_REGION: + { + CoglClipStackRegion *region = (CoglClipStackRegion *) entry; + + /* If nrectangles <= 1, it can be fully represented with the + * scissor clip. + */ + if (cairo_region_num_rectangles (region->region) > 1) + { + COGL_NOTE (CLIPPING, "Adding stencil clip for region"); + + add_stencil_clip_region (framebuffer, region->region, + using_stencil_buffer); + using_stencil_buffer = TRUE; + } + break; + } case COGL_CLIP_STACK_WINDOW_RECT: break; /* We don't need to do anything for window space rectangles because diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build index 8041e786e..f6f8c0163 100644 --- a/cogl/cogl/meson.build +++ b/cogl/cogl/meson.build @@ -478,7 +478,7 @@ if have_introspection sources: cogl_introspected_headers, nsversion: libmutter_api_version, namespace: 'Cogl', - includes: ['GL-1.0', 'GObject-2.0', 'Graphene-1.0'], + includes: ['cairo-1.0', 'GL-1.0', 'GObject-2.0', 'Graphene-1.0'], dependencies: [cogl_deps], extra_args: introspection_args + [ '-UCOGL_COMPILATION',