[mutter/wip/carlosg/clip-regions: 49/54] cogl: Add support for clipping regions



commit 9e40c196a037d341617f85a42610a7cc23e037e6
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Jul 30 23:48:45 2019 +0200

    cogl: Add support for clipping regions
    
    This uses the stencil buffer to poke holes in the shape of the
    given cairo_region_t that we will draw through.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/867

 cogl/cogl/cogl-clip-stack.c              | 33 +++++++++++++
 cogl/cogl/cogl-clip-stack.h              | 18 ++++++-
 cogl/cogl/cogl-framebuffer.c             | 17 +++++++
 cogl/cogl/cogl-framebuffer.h             |  5 ++
 cogl/cogl/driver/gl/cogl-clip-stack-gl.c | 84 ++++++++++++++++++++++++++++++++
 cogl/cogl/meson.build                    |  2 +-
 6 files changed, 157 insertions(+), 2 deletions(-)
---
diff --git a/cogl/cogl/cogl-clip-stack.c b/cogl/cogl/cogl-clip-stack.c
index 092510714..cc53b3aff 100644
--- a/cogl/cogl/cogl-clip-stack.c
+++ b/cogl/cogl/cogl-clip-stack.c
@@ -295,6 +295,32 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack,
   return (CoglClipStack *) entry;
 }
 
+CoglClipStack *
+_cogl_clip_stack_push_region (CoglClipStack   *stack,
+                              CoglMatrixEntry *modelview_entry,
+                              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->matrix_entry = cogl_matrix_entry_ref (modelview_entry);
+  entry_region->region = cairo_region_reference (region);
+
+  return entry;
+}
+
 CoglClipStack *
 _cogl_clip_stack_ref (CoglClipStack *entry)
 {
@@ -336,6 +362,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..fedae3f4f 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,16 @@ struct _CoglClipStackPrimitive
   float bounds_y2;
 };
 
+struct _CoglClipStackRegion
+{
+  CoglClipStack _parent_data;
+
+  /* The matrix that was current when the clip was set */
+  CoglMatrixEntry *matrix_entry;
+
+  cairo_region_t *region;
+};
+
 CoglClipStack *
 _cogl_clip_stack_push_window_rectangle (CoglClipStack *stack,
                                         int x_offset,
@@ -189,6 +201,10 @@ _cogl_clip_stack_push_primitive (CoglClipStack *stack,
                                  CoglMatrixEntry *modelview_entry,
                                  CoglMatrixEntry *projection_entry,
                                  const float *viewport);
+CoglClipStack *
+_cogl_clip_stack_push_region (CoglClipStack   *stack,
+                              CoglMatrixEntry *modelview_entry,
+                              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 8164e5760..ae4f2bbc0 100644
--- a/cogl/cogl/cogl-framebuffer.c
+++ b/cogl/cogl/cogl-framebuffer.c
@@ -1762,6 +1762,23 @@ cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer,
       COGL_FRAMEBUFFER_STATE_CLIP;
 }
 
+void
+cogl_framebuffer_push_region_clip (CoglFramebuffer *framebuffer,
+                                   cairo_region_t  *region)
+{
+  CoglMatrixEntry *modelview_entry =
+    _cogl_framebuffer_get_modelview_entry (framebuffer);
+
+  framebuffer->clip_stack =
+    _cogl_clip_stack_push_region (framebuffer->clip_stack,
+                                  modelview_entry,
+                                  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 b582dac71..1df6731f7 100644
--- a/cogl/cogl/cogl-framebuffer.h
+++ b/cogl/cogl/cogl-framebuffer.h
@@ -54,6 +54,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
 #include <cogl/cogl-bitmap.h>
 #include <cogl/cogl-texture.h>
 #include <glib-object.h>
+#include <cairo.h>
 
 #include <graphene.h>
 
@@ -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..f8b201fc8 100644
--- a/cogl/cogl/driver/gl/cogl-clip-stack-gl.c
+++ b/cogl/cogl/driver/gl/cogl-clip-stack-gl.c
@@ -115,6 +115,74 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
   GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
 }
 
+static void
+add_stencil_clip_region (CoglFramebuffer *framebuffer,
+                         CoglMatrixEntry *modelview_entry,
+                         cairo_region_t  *region)
+{
+  CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
+  CoglMatrix matrix;
+  int num_rectangles = cairo_region_num_rectangles (region);
+  int i, width, height;
+
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+
+  /* 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);
+
+  cogl_matrix_init_identity (&matrix);
+  cogl_matrix_translate (&matrix, -1, 1, 0);
+  cogl_matrix_scale (&matrix, 2.0 / width, - 2.0 / height, 1);
+
+  GE( ctx, glEnable (GL_STENCIL_TEST) );
+
+  GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) );
+  GE( ctx, glDepthMask (FALSE) );
+
+  /* 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]);
+    }
+
+  /* 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 +450,22 @@ _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->matrix_entry, region->region);
+                  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 f6c61f8c8..b354d32aa 100644
--- a/cogl/cogl/meson.build
+++ b/cogl/cogl/meson.build
@@ -489,7 +489,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',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]