clip: Adds cogl_clip_push_primitive API

This adds a new experimental function, cogl_clip_push_primitive, that
allows you to push a CoglPrimitive onto the clip stack. The primitive
should describe a flat 2D shape and the geometry shouldn't include any
self intersections. When pushing a primitive you also need to tell
Cogl what the bounding box of that shape is (in shape local coordinates)
so that Cogl is able to efficiently update the required region of the
stencil buffer.

Reviewed-by: Neil Roberts <neil@linux.intel.com>
This commit is contained in:
Robert Bragg 2011-10-03 14:39:05 +01:00
parent 7a834cf681
commit f6daed62e3
4 changed files with 292 additions and 70 deletions

View File

@ -43,6 +43,8 @@
#include "cogl-primitives-private.h"
#include "cogl-private.h"
#include "cogl-pipeline-opengl-private.h"
#include "cogl-attribute-private.h"
#include "cogl-primitive-private.h"
#ifndef GL_CLIP_PLANE0
#define GL_CLIP_PLANE0 0x3000
@ -274,13 +276,19 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
cogl_pop_source ();
}
typedef void (*SilhouettePaintCallback) (void *user_data);
static void
add_stencil_clip_path (CoglFramebuffer *framebuffer,
CoglPath *path,
gboolean merge,
gboolean need_clear)
add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
SilhouettePaintCallback silhouette_callback,
float bounds_x1,
float bounds_y1,
float bounds_x2,
float bounds_y2,
gboolean merge,
gboolean need_clear,
void *user_data)
{
CoglPathData *data = path->data;
CoglMatrixStack *modelview_stack =
_cogl_framebuffer_get_modelview_stack (framebuffer);
CoglMatrixStack *projection_stack =
@ -330,10 +338,8 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer,
/* Just clear the bounding box */
GE( ctx, glStencilMask (~(GLuint) 0) );
GE( ctx, glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
_cogl_rectangle_immediate (data->path_nodes_min.x,
data->path_nodes_min.y,
data->path_nodes_max.x,
data->path_nodes_max.y);
_cogl_rectangle_immediate (bounds_x1, bounds_y1,
bounds_x2, bounds_y2);
}
GE (ctx, glStencilMask (1));
GE (ctx, glStencilFunc (GL_LEQUAL, 0x1, 0x3));
@ -341,11 +347,7 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer,
GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT));
if (path->data->path_nodes->len >= 3)
_cogl_path_fill_nodes (path,
COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
silhouette_callback (user_data);
if (merge)
{
@ -385,6 +387,65 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer,
cogl_pop_source ();
}
static void
paint_path_silhouette (void *user_data)
{
CoglPath *path = user_data;
if (path->data->path_nodes->len >= 3)
_cogl_path_fill_nodes (path,
COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
}
static void
add_stencil_clip_path (CoglFramebuffer *framebuffer,
CoglPath *path,
gboolean merge,
gboolean need_clear)
{
CoglPathData *data = path->data;
add_stencil_clip_silhouette (framebuffer,
paint_path_silhouette,
data->path_nodes_min.x,
data->path_nodes_min.y,
data->path_nodes_max.x,
data->path_nodes_max.y,
merge,
need_clear,
path);
}
static void
paint_primitive_silhouette (void *user_data)
{
_cogl_primitive_draw (user_data,
COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
}
void
add_stencil_clip_primitive (CoglFramebuffer *framebuffer,
CoglPrimitive *primitive,
float bounds_x1,
float bounds_y1,
float bounds_x2,
float bounds_y2,
gboolean merge,
gboolean need_clear)
{
add_stencil_clip_silhouette (framebuffer,
paint_primitive_silhouette,
bounds_x1,
bounds_y1,
bounds_x2,
bounds_y2,
merge,
need_clear,
primitive);
}
static void
disable_stencil_buffer (void)
{
@ -604,6 +665,39 @@ _cogl_clip_stack_push_from_path (CoglClipStack *stack,
}
}
CoglClipStack *
_cogl_clip_stack_push_primitive (CoglClipStack *stack,
CoglPrimitive *primitive,
float bounds_x1,
float bounds_y1,
float bounds_x2,
float bounds_y2,
const CoglMatrix *modelview_matrix)
{
CoglClipStackPrimitive *entry;
entry = _cogl_clip_stack_push_entry (stack,
sizeof (CoglClipStackPrimitive),
COGL_CLIP_STACK_PRIMITIVE);
entry->primitive = cogl_object_ref (primitive);
entry->matrix = *modelview_matrix;
entry->bounds_x1 = bounds_x1;
entry->bounds_y1 = bounds_y1;
entry->bounds_x2 = bounds_x2;
entry->bounds_y2 = bounds_y2;
/* NB: this is referring to the bounds in window coordinates as opposed
* to the bounds above in primitive local coordinates. */
_cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry,
bounds_x1, bounds_y1, bounds_x2, bounds_y2,
modelview_matrix);
return (CoglClipStack *) entry;
}
CoglClipStack *
_cogl_clip_stack_ref (CoglClipStack *entry)
{
@ -639,6 +733,11 @@ _cogl_clip_stack_unref (CoglClipStack *entry)
g_slice_free1 (sizeof (CoglClipStackPath), entry);
break;
case COGL_CLIP_STACK_PRIMITIVE:
cogl_object_unref (((CoglClipStackPrimitive *) entry)->primitive);
g_slice_free1 (sizeof (CoglClipStackPrimitive), entry);
break;
default:
g_assert_not_reached ();
}
@ -792,69 +891,101 @@ _cogl_clip_stack_flush (CoglClipStack *stack,
order */
for (entry = stack; entry; entry = entry->parent)
{
if (entry->type == COGL_CLIP_STACK_PATH)
switch (entry->type)
{
CoglClipStackPath *path_entry = (CoglClipStackPath *) entry;
COGL_NOTE (CLIPPING, "Adding stencil clip for path");
_cogl_matrix_stack_push (modelview_stack);
_cogl_matrix_stack_set (modelview_stack, &path_entry->matrix);
add_stencil_clip_path (framebuffer,
path_entry->path,
using_stencil_buffer,
TRUE);
_cogl_matrix_stack_pop (modelview_stack);
using_stencil_buffer = TRUE;
}
else if (entry->type == COGL_CLIP_STACK_RECT)
{
CoglClipStackRect *rect = (CoglClipStackRect *) entry;
/* We don't need to do anything extra if the clip for this
rectangle was entirely described by its scissor bounds */
if (!rect->can_be_scissor)
case COGL_CLIP_STACK_PATH:
{
CoglClipStackPath *path_entry = (CoglClipStackPath *) entry;
COGL_NOTE (CLIPPING, "Adding stencil clip for path");
_cogl_matrix_stack_push (modelview_stack);
_cogl_matrix_stack_set (modelview_stack, &rect->matrix);
_cogl_matrix_stack_set (modelview_stack, &path_entry->matrix);
/* If we support clip planes and we haven't already used
them then use that instead */
if (has_clip_planes)
{
COGL_NOTE (CLIPPING, "Adding clip planes clip for rectangle");
set_clip_planes (framebuffer,
rect->x0,
rect->y0,
rect->x1,
rect->y1);
using_clip_planes = TRUE;
/* We can't use clip planes a second time */
has_clip_planes = FALSE;
}
else
{
COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle");
add_stencil_clip_rectangle (framebuffer,
rect->x0,
rect->y0,
rect->x1,
rect->y1,
!using_stencil_buffer);
using_stencil_buffer = TRUE;
}
add_stencil_clip_path (framebuffer,
path_entry->path,
using_stencil_buffer,
TRUE);
_cogl_matrix_stack_pop (modelview_stack);
using_stencil_buffer = TRUE;
break;
}
case COGL_CLIP_STACK_PRIMITIVE:
{
CoglClipStackPrimitive *primitive_entry =
(CoglClipStackPrimitive *) entry;
COGL_NOTE (CLIPPING, "Adding stencil clip for primitive");
_cogl_matrix_stack_push (modelview_stack);
_cogl_matrix_stack_set (modelview_stack, &primitive_entry->matrix);
add_stencil_clip_primitive (framebuffer,
primitive_entry->primitive,
primitive_entry->bounds_x1,
primitive_entry->bounds_y1,
primitive_entry->bounds_x2,
primitive_entry->bounds_y2,
using_stencil_buffer,
TRUE);
_cogl_matrix_stack_pop (modelview_stack);
using_stencil_buffer = TRUE;
break;
}
case COGL_CLIP_STACK_RECT:
{
CoglClipStackRect *rect = (CoglClipStackRect *) entry;
/* We don't need to do anything extra if the clip for this
rectangle was entirely described by its scissor bounds */
if (!rect->can_be_scissor)
{
_cogl_matrix_stack_push (modelview_stack);
_cogl_matrix_stack_set (modelview_stack, &rect->matrix);
/* If we support clip planes and we haven't already used
them then use that instead */
if (has_clip_planes)
{
COGL_NOTE (CLIPPING,
"Adding clip planes clip for rectangle");
set_clip_planes (framebuffer,
rect->x0,
rect->y0,
rect->x1,
rect->y1);
using_clip_planes = TRUE;
/* We can't use clip planes a second time */
has_clip_planes = FALSE;
}
else
{
COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle");
add_stencil_clip_rectangle (framebuffer,
rect->x0,
rect->y0,
rect->x1,
rect->y1,
!using_stencil_buffer);
using_stencil_buffer = TRUE;
}
_cogl_matrix_stack_pop (modelview_stack);
}
break;
}
case COGL_CLIP_STACK_WINDOW_RECT:
break;
/* We don't need to do anything for window space rectangles because
* their functionality is entirely implemented by the entry bounding
* box */
}
/* We don't need to do anything for window space rectangles
because their functionality is entirely implemented by the
entry bounding box */
}
/* Enabling clip planes is delayed to now so that they won't affect

View File

@ -40,12 +40,14 @@ typedef struct _CoglClipStack CoglClipStack;
typedef struct _CoglClipStackRect CoglClipStackRect;
typedef struct _CoglClipStackWindowRect CoglClipStackWindowRect;
typedef struct _CoglClipStackPath CoglClipStackPath;
typedef struct _CoglClipStackPrimitive CoglClipStackPrimitive;
typedef enum
{
COGL_CLIP_STACK_RECT,
COGL_CLIP_STACK_WINDOW_RECT,
COGL_CLIP_STACK_PATH
COGL_CLIP_STACK_PATH,
COGL_CLIP_STACK_PRIMITIVE
} CoglClipStackType;
/* A clip stack consists a list of entries. Each entry has a reference
@ -149,6 +151,21 @@ struct _CoglClipStackPath
CoglPath *path;
};
struct _CoglClipStackPrimitive
{
CoglClipStack _parent_data;
/* The matrix that was current when the clip was set */
CoglMatrix matrix;
CoglPrimitive *primitive;
float bounds_x1;
float bounds_y1;
float bounds_x2;
float bounds_y2;
};
CoglClipStack *
_cogl_clip_stack_push_window_rectangle (CoglClipStack *stack,
int x_offset,
@ -168,6 +185,16 @@ CoglClipStack *
_cogl_clip_stack_push_from_path (CoglClipStack *stack,
CoglPath *path,
const CoglMatrix *modelview_matrix);
CoglClipStack *
_cogl_clip_stack_push_primitive (CoglClipStack *stack,
CoglPrimitive *primitive,
float bounds_x1,
float bounds_y1,
float bounds_x2,
float bounds_y2,
const CoglMatrix *modelview_matrix);
CoglClipStack *
_cogl_clip_stack_pop (CoglClipStack *stack);

View File

@ -125,6 +125,32 @@ cogl_clip_push_from_path (void)
ctx->current_path = cogl2_path_new ();
}
void
cogl_clip_push_primitive (CoglPrimitive *primitive,
float bounds_x1,
float bounds_y1,
float bounds_x2,
float bounds_y2)
{
CoglFramebuffer *framebuffer;
CoglClipState *clip_state;
CoglMatrix modelview_matrix;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
framebuffer = cogl_get_draw_framebuffer ();
clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
cogl_get_modelview_matrix (&modelview_matrix);
clip_state->stacks->data =
_cogl_clip_stack_push_primitive (clip_state->stacks->data,
primitive,
bounds_x1, bounds_y1,
bounds_x2, bounds_y2,
&modelview_matrix);
}
static void
_cogl_clip_pop_real (CoglClipState *clip_state)
{

View File

@ -844,6 +844,44 @@ cogl_clip_push_rectangle (float x0,
void
cogl_clip_push_from_path_preserve (void);
#ifdef COGL_ENABLE_EXPERIMENTAL_2_0_API
#define cogl_clip_push_primitive cogl_clip_push_primitive_EXP
/**
* cogl_clip_push_primitive:
* @primitive: A #CoglPrimitive describing a flat 2D shape
* @bounds_x1: x coordinate for the top-left corner of the primitives
* bounds
* @bounds_y1: y coordinate for the top-left corner of the primitives
* bounds
* @bounds_x2: x coordinate for the top-left corner of the primitives
* bounds
* @bounds_y2: x coordinate for the bottom-right corner of the
* primitives bounds.
* @bounds_x1: y coordinate for the bottom-right corner of the
* primitives bounds.
*
* Sets a new clipping area using a 2D shaped described with a
* #CoglPrimitive. The shape must not contain self overlapping
* geometry and must lie on a single 2D plane. A bounding box of the
* 2D shape in local coordinates (the same coordinates used to
* describe the shape) must be given. It is acceptable for the bounds
* to be larger than the true bounds but behaviour is undefined if the
* bounds are smaller than the true bounds.
*
* The clipping area is intersected with the previous clipping area.
* To restore the previous clipping area, call cogl_clip_pop().
*
* Since: 1.10
* Stability: unstable
*/
void
cogl_clip_push_primitive (CoglPrimitive *primitive,
float bounds_x1,
float bounds_y1,
float bounds_x2,
float bounds_y2);
#endif
/**
* cogl_clip_pop:
*