Bug 1172 - Disjoint paths and clip to path

* clutter/cogl/cogl-path.h:
	* clutter/cogl/common/cogl-primitives.c:
	* clutter/cogl/common/cogl-primitives.h:
	* clutter/cogl/gl/cogl-primitives.c:
	* clutter/cogl/gles/cogl-primitives.c: Changed the semantics of
	cogl_path_move_to. Previously this always started a new path but
	now it instead starts a new disjoint sub path. The path isn't
	cleared until you call either cogl_path_stroke, cogl_path_fill or
	cogl_path_new. There are also cogl_path_stroke_preserve and
	cogl_path_fill_preserve functions.

	* clutter/cogl/gl/cogl-context.c:
	* clutter/cogl/gl/cogl-context.h:
	* clutter/cogl/gles/cogl-context.c:
	* clutter/cogl/gles/cogl-context.h: Convert the path nodes array
	to a GArray.

	* clutter/cogl/gl/cogl-texture.c:
	* clutter/cogl/gles/cogl-texture.c: Call cogl_clip_ensure

	* clutter/cogl/common/cogl-clip-stack.c:
	* clutter/cogl/common/cogl-clip-stack.h: Simplified the clip
	stack code quite a bit to make it more maintainable.  Previously
	whenever you added a new clip it would go through a separate route
	to immediately intersect with the current clip and when you
	removed it again it would immediately rebuild the entire clip. Now
	when you add or remove a clip it doesn't do anything immediately
	but just sets a dirty flag instead.

	* clutter/cogl/gl/cogl.c:
	* clutter/cogl/gles/cogl.c: Taken away the code to intersect
	stencil clips when there is exactly one stencil bit. It won't work
	with path clips and I don't know of any platform that doesn't have
	eight or zero stencil bits. It needs at least three bits to
	intersect a path with an existing clip. cogl_features_init now
	just decides you don't have a stencil buffer at all if you have
	less than three bits.

	* clutter/cogl/cogl.h.in: New functions and documentation.

	* tests/interactive/test-clip.c: Replaced with a different test
	that lets you add and remove clips. The three different mouse
	buttons add clips in different shapes. This makes it easier to
	test multiple levels of clipping.

	* tests/interactive/test-cogl-primitives.c: Use
	cogl_path_stroke_preserve when using the same path again.

	* doc/reference/cogl/cogl-sections.txt: Document the new
	functions.
This commit is contained in:
Neil Roberts 2008-12-04 13:45:09 +00:00
parent be655b05ad
commit 10d7cf3273
17 changed files with 984 additions and 758 deletions

View File

@ -82,28 +82,60 @@ void cogl_rectanglex (CoglFixed x,
/** /**
* cogl_path_fill: * cogl_path_fill:
* *
* Fills the constructed shape using the current drawing color. * Fills the constructed shape using the current drawing color. The
* current path is then cleared. To use the path again, call
* cogl_path_fill_preserve() instead.
**/ **/
void cogl_path_fill (void); void cogl_path_fill (void);
/**
* cogl_path_fill_preserve:
*
* Fills the constructed shape using the current drawing color and
* preserves the path to be used again.
*
* Since: 1.0
**/
void cogl_path_fill_preserve (void);
/** /**
* cogl_path_stroke: * cogl_path_stroke:
* *
* Strokes the constructed shape using the current drawing color * Strokes the constructed shape using the current drawing color and a
* and a width of 1 pixel (regardless of the current transformation * width of 1 pixel (regardless of the current transformation
* matrix). * matrix). To current path is then cleared. To use the path again,
* call cogl_path_stroke_preserve() instead.
**/ **/
void cogl_path_stroke (void); void cogl_path_stroke (void);
/**
* cogl_path_stroke_preserve:
*
* Strokes the constructed shape using the current drawing color and
* preserves the path to be used again.
*
* Since: 1.0
**/
void cogl_path_stroke_preserve (void);
/**
* cogl_path_new:
*
* Clears the current path and starts a new one.
*
* Since: 1.0
*/
void cogl_path_new (void);
/** /**
* cogl_path_move_to: * cogl_path_move_to:
* @x: X coordinate of the pen location to move to. * @x: X coordinate of the pen location to move to.
* @y: Y coordinate of the pen location to move to. * @y: Y coordinate of the pen location to move to.
* *
* Clears the previously constructed shape and begins a new path * Moves the pen to the given location. If there is an existing path
* contour by moving the pen to the given coordinates. * this will start a new disjoint subpath.
**/ **/
void cogl_path_move_to (CoglFixed x, void cogl_path_move_to (CoglFixed x,
CoglFixed y); CoglFixed y);
@ -113,9 +145,9 @@ void cogl_path_move_to (CoglFixed x,
* @x: X offset from the current pen location to move the pen to. * @x: X offset from the current pen location to move the pen to.
* @y: Y offset from the current pen location to move the pen to. * @y: Y offset from the current pen location to move the pen to.
* *
* Clears the previously constructed shape and begins a new path * Moves the pen to the given offset relative to the current pen
* contour by moving the pen to the given coordinates relative * location. If there is an existing path this will start a new
* to the current pen location. * disjoint subpath.
**/ **/
void cogl_path_rel_move_to (CoglFixed x, void cogl_path_rel_move_to (CoglFixed x,
CoglFixed y); CoglFixed y);
@ -222,8 +254,9 @@ void cogl_path_close (void);
* @x2: X coordinate of the end line vertex * @x2: X coordinate of the end line vertex
* @y2: Y coordinate of the end line vertex * @y2: Y coordinate of the end line vertex
* *
* Clears the previously constructed shape and constructs a straight * Constructs a straight line shape starting and ending at the given
* line shape start and ending at the given coordinates. * coordinates. If there is an existing path this will start a new
* disjoint sub-path.
**/ **/
void cogl_path_line (CoglFixed x1, void cogl_path_line (CoglFixed x1,
CoglFixed y1, CoglFixed y1,
@ -236,10 +269,11 @@ void cogl_path_line (CoglFixed x1,
* values that specify the vertex coordinates. * values that specify the vertex coordinates.
* @num_points: The total number of vertices. * @num_points: The total number of vertices.
* *
* Clears the previously constructed shape and constructs a series of straight * Constructs a series of straight line segments, starting from the
* line segments, starting from the first given vertex coordinate. Each * first given vertex coordinate. If there is an existing path this
* subsequent segment stars where the previous one ended and ends at the next * will start a new disjoint sub-path. Each subsequent segment starts
* given vertex coordinate. * where the previous one ended and ends at the next given vertex
* coordinate.
* *
* The coords array must contain 2 * num_points values. The first value * The coords array must contain 2 * num_points values. The first value
* represents the X coordinate of the first vertex, the second value * represents the X coordinate of the first vertex, the second value
@ -257,8 +291,8 @@ void cogl_path_polyline (CoglFixed *coords,
* values that specify the vertex coordinates. * values that specify the vertex coordinates.
* @num_points: The total number of vertices. * @num_points: The total number of vertices.
* *
* Clears the previously constructed shape and constructs a polygonal * Constructs a polygonal shape of the given number of vertices. If
* shape of the given number of vertices. * there is an existing path this will start a new disjoint sub-path.
* *
* The coords array must contain 2 * num_points values. The first value * The coords array must contain 2 * num_points values. The first value
* represents the X coordinate of the first vertex, the second value * represents the X coordinate of the first vertex, the second value
@ -276,8 +310,8 @@ void cogl_path_polygon (CoglFixed *coords,
* @width: Rectangle width. * @width: Rectangle width.
* @height: Rectangle height. * @height: Rectangle height.
* *
* Clears the previously constructed shape and constructs a rectangular * Constructs a rectangular shape at the given coordinates. If there
* shape at the given coordinates. * is an existing path this will start a new disjoint sub-path.
**/ **/
void cogl_path_rectangle (CoglFixed x, void cogl_path_rectangle (CoglFixed x,
CoglFixed y, CoglFixed y,
@ -291,8 +325,8 @@ void cogl_path_rectangle (CoglFixed x,
* @radius_x: X radius of the ellipse * @radius_x: X radius of the ellipse
* @radius_y: Y radius of the ellipse * @radius_y: Y radius of the ellipse
* *
* Clears the previously constructed shape and constructs an ellipse * Constructs an ellipse shape. If there is an existing path this will
* shape. * start a new disjoint sub-path.
**/ **/
void cogl_path_ellipse (CoglFixed center_x, void cogl_path_ellipse (CoglFixed center_x,
CoglFixed center_y, CoglFixed center_y,
@ -309,9 +343,9 @@ void cogl_path_ellipse (CoglFixed center_x,
* @arc_step: Angle increment resolution for subdivision of * @arc_step: Angle increment resolution for subdivision of
* the corner arcs. * the corner arcs.
* *
* Clears the previously constructed shape and constructs a rectangular * Constructs a rectangular shape with rounded corners. If there is an
* shape with rounded corners. * existing path this will start a new disjoint sub-path.
**/ **/
void cogl_path_round_rectangle (CoglFixed x, void cogl_path_round_rectangle (CoglFixed x,
CoglFixed y, CoglFixed y,
CoglFixed width, CoglFixed width,

View File

@ -332,20 +332,59 @@ void cogl_get_viewport (CoglFixed v[4]);
* will be clipped so that only the portion inside the rectangle will * will be clipped so that only the portion inside the rectangle will
* be displayed. The rectangle dimensions are transformed by the * be displayed. The rectangle dimensions are transformed by the
* current model-view matrix. * current model-view matrix.
*
* The rectangle is intersected with the current clip region. To undo
* the effect of this function, call cogl_clip_unset().
*/ */
void cogl_clip_set (CoglFixed x_offset, void cogl_clip_set (CoglFixed x_offset,
CoglFixed y_offset, CoglFixed y_offset,
CoglFixed width, CoglFixed width,
CoglFixed height); CoglFixed height);
/**
* cogl_clip_set_from_path:
*
* Sets a new clipping area using the current path. The current path
* is then cleared. The clipping area is intersected with the previous
* clipping area. To restore the previous clipping area, call
* cogl_clip_unset().
*
* Since: 1.0
*/
void cogl_clip_set_from_path (void);
/**
* cogl_clip_set_from_path_preserve:
*
* Sets a new clipping area using the current path. The current path
* is then cleared. The clipping area is intersected with the previous
* clipping area. To restore the previous clipping area, call
* cogl_clip_unset().
*
* Since: 1.0
*/
void cogl_clip_set_from_path_preserve (void);
/** /**
* cogl_clip_unset: * cogl_clip_unset:
* *
* Removes the current clipping rectangle so that all drawing * Reverts the clipping region to the state before the last call to
* operations extend to full size of the viewport again. * cogl_clip_set().
*/ */
void cogl_clip_unset (void); void cogl_clip_unset (void);
/**
* cogl_clip_ensure:
*
* Ensures that the current clipping region has been set in GL. This
* will automatically be called before any Cogl primitives but it
* maybe be neccessary to call if you are using raw GL calls with
* clipping.
*
* Since: 1.0
*/
void cogl_clip_ensure (void);
/** /**
* cogl_clip_stack_save: * cogl_clip_stack_save:
* *

View File

@ -27,8 +27,11 @@
#include "config.h" #include "config.h"
#endif #endif
#include <string.h>
#include "cogl.h" #include "cogl.h"
#include "cogl-clip-stack.h" #include "cogl-clip-stack.h"
#include "cogl-primitives.h"
#include "cogl-context.h"
/* These are defined in the particular backend (float in GL vs fixed /* These are defined in the particular backend (float in GL vs fixed
in GL ES) */ in GL ES) */
@ -36,58 +39,64 @@ void _cogl_set_clip_planes (CoglFixed x,
CoglFixed y, CoglFixed y,
CoglFixed width, CoglFixed width,
CoglFixed height); CoglFixed height);
void _cogl_init_stencil_buffer (void);
void _cogl_add_stencil_clip (CoglFixed x, void _cogl_add_stencil_clip (CoglFixed x,
CoglFixed y, CoglFixed y,
CoglFixed width, CoglFixed width,
CoglFixed height, CoglFixed height,
gboolean first); gboolean first);
void _cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min,
CoglFixedVec2 nodes_max,
guint path_size,
CoglPathNode *path,
gboolean merge);
void _cogl_enable_clip_planes (void);
void _cogl_disable_clip_planes (void); void _cogl_disable_clip_planes (void);
void _cogl_disable_stencil_buffer (void); void _cogl_disable_stencil_buffer (void);
void _cogl_set_matrix (const CoglFixed *matrix); void _cogl_set_matrix (const CoglFixed *matrix);
typedef struct _CoglClipStackEntry CoglClipStackEntry; typedef struct _CoglClipStack CoglClipStack;
struct _CoglClipStackEntry typedef struct _CoglClipStackEntryRect CoglClipStackEntryRect;
typedef struct _CoglClipStackEntryPath CoglClipStackEntryPath;
typedef enum
{
COGL_CLIP_STACK_RECT,
COGL_CLIP_STACK_PATH
} CoglClipStackEntryType;
struct _CoglClipStack
{ {
/* If this is set then this entry clears the clip stack. This is GList *stack_top;
used to clear the stack when drawing an FBO put to keep the
entries so they can be restored when the FBO drawing is
completed */
gboolean clear;
/* The rectangle for this clip */
CoglFixed x_offset;
CoglFixed y_offset;
CoglFixed width;
CoglFixed height;
/* The matrix that was current when the clip was set */
CoglFixed matrix[16];
}; };
static GList *cogl_clip_stack_top = NULL; struct _CoglClipStackEntryRect
static int cogl_clip_stack_depth = 0;
static void
_cogl_clip_stack_add (const CoglClipStackEntry *entry, int depth)
{ {
int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); CoglClipStackEntryType type;
/* If this is the first entry and we support clip planes then use /* The rectangle for this clip */
that instead */ CoglFixed x_offset;
if (depth == 1 && has_clip_planes) CoglFixed y_offset;
_cogl_set_clip_planes (entry->x_offset, CoglFixed width;
entry->y_offset, CoglFixed height;
entry->width,
entry->height); /* The matrix that was current when the clip was set */
else CoglFixed matrix[16];
_cogl_add_stencil_clip (entry->x_offset, };
entry->y_offset,
entry->width, struct _CoglClipStackEntryPath
entry->height, {
depth == (has_clip_planes ? 2 : 1)); CoglClipStackEntryType type;
}
/* The matrix that was current when the clip was set */
CoglFixed matrix[16];
CoglFixedVec2 path_nodes_min;
CoglFixedVec2 path_nodes_max;
guint path_size;
CoglPathNode path[1];
};
void void
cogl_clip_set (CoglFixed x_offset, cogl_clip_set (CoglFixed x_offset,
@ -95,10 +104,17 @@ cogl_clip_set (CoglFixed x_offset,
CoglFixed width, CoglFixed width,
CoglFixed height) CoglFixed height)
{ {
CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry); CoglClipStackEntryRect *entry;
CoglClipStack *stack;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
stack = (CoglClipStack *) ctx->clip.stacks->data;
entry = g_slice_new (CoglClipStackEntryRect);
/* Make a new entry */ /* Make a new entry */
entry->clear = FALSE; entry->type = COGL_CLIP_STACK_RECT;
entry->x_offset = x_offset; entry->x_offset = x_offset;
entry->y_offset = y_offset; entry->y_offset = y_offset;
entry->width = width; entry->width = width;
@ -106,127 +122,231 @@ cogl_clip_set (CoglFixed x_offset,
cogl_get_modelview_matrix (entry->matrix); cogl_get_modelview_matrix (entry->matrix);
/* Add the entry to the current clip */ /* Store it in the stack */
_cogl_clip_stack_add (entry, ++cogl_clip_stack_depth); stack->stack_top = g_list_prepend (stack->stack_top, entry);
ctx->clip.stack_dirty = TRUE;
}
void
cogl_clip_set_from_path_preserve (void)
{
CoglClipStackEntryPath *entry;
CoglClipStack *stack;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
stack = (CoglClipStack *) ctx->clip.stacks->data;
entry = g_malloc (sizeof (CoglClipStackEntryPath)
+ sizeof (CoglPathNode) * (ctx->path_nodes->len - 1));
entry->type = COGL_CLIP_STACK_PATH;
entry->path_nodes_min = ctx->path_nodes_min;
entry->path_nodes_max = ctx->path_nodes_max;
entry->path_size = ctx->path_nodes->len;
memcpy (entry->path, ctx->path_nodes->data,
sizeof (CoglPathNode) * ctx->path_nodes->len);
cogl_get_modelview_matrix (entry->matrix);
/* Store it in the stack */ /* Store it in the stack */
cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry); stack->stack_top = g_list_prepend (stack->stack_top, entry);
ctx->clip.stack_dirty = TRUE;
}
void
cogl_clip_set_from_path (void)
{
cogl_clip_set_from_path_preserve ();
cogl_path_new ();
} }
void void
cogl_clip_unset (void) cogl_clip_unset (void)
{ {
g_return_if_fail (cogl_clip_stack_top != NULL); gpointer entry;
CoglClipStack *stack;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
stack = (CoglClipStack *) ctx->clip.stacks->data;
g_return_if_fail (stack->stack_top != NULL);
entry = stack->stack_top->data;
/* Remove the top entry from the stack */ /* Remove the top entry from the stack */
g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data); if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_RECT)
cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top, g_slice_free (CoglClipStackEntryRect, entry);
cogl_clip_stack_top); else
cogl_clip_stack_depth--; g_free (entry);
/* Rebuild the clip */ stack->stack_top = g_list_delete_link (stack->stack_top,
_cogl_clip_stack_rebuild (FALSE); stack->stack_top);
ctx->clip.stack_dirty = TRUE;
} }
void void
_cogl_clip_stack_rebuild (gboolean just_stencil) _cogl_clip_stack_rebuild (void)
{ {
int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
gboolean using_clip_planes = FALSE;
gboolean using_stencil_buffer = FALSE;
GList *node; GList *node;
int depth = 0; CoglClipStack *stack;
/* Disable clip planes if the stack is empty */ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (has_clip_planes && cogl_clip_stack_depth < 1)
_cogl_disable_clip_planes ();
/* Disable the stencil buffer if there isn't enough entries */ stack = (CoglClipStack *) ctx->clip.stacks->data;
if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1))
_cogl_disable_stencil_buffer (); ctx->clip.stack_dirty = FALSE;
ctx->clip.stencil_used = FALSE;
_cogl_disable_clip_planes ();
_cogl_disable_stencil_buffer ();
/* If the stack is empty then there's nothing else to do */
if (stack->stack_top == NULL)
return;
/* Find the bottom of the stack */ /* Find the bottom of the stack */
for (node = cogl_clip_stack_top; depth < cogl_clip_stack_depth - 1; for (node = stack->stack_top; node->next; node = node->next);
node = node->next)
depth++;
/* Re-add every entry from the bottom of the stack up */ /* Re-add every entry from the bottom of the stack up */
depth = 1; for (; node; node = node->prev)
for (; depth <= cogl_clip_stack_depth; node = node->prev, depth++) {
if (!just_stencil || !has_clip_planes || depth > 1) gpointer entry = node->data;
{
const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data; if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_PATH)
cogl_push_matrix (); {
_cogl_set_matrix (entry->matrix); CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry;
_cogl_clip_stack_add (entry, depth);
cogl_pop_matrix (); cogl_push_matrix ();
} _cogl_set_matrix (path->matrix);
_cogl_add_path_to_stencil_buffer (path->path_nodes_min,
path->path_nodes_max,
path->path_size,
path->path,
using_stencil_buffer);
cogl_pop_matrix ();
using_stencil_buffer = TRUE;
/* We can't use clip planes any more */
has_clip_planes = FALSE;
}
else
{
CoglClipStackEntryRect *rect = (CoglClipStackEntryRect *) entry;
cogl_push_matrix ();
_cogl_set_matrix (rect->matrix);
/* If this is the first entry and we support clip planes then use
that instead */
if (has_clip_planes)
{
_cogl_set_clip_planes (rect->x_offset,
rect->y_offset,
rect->width,
rect->height);
using_clip_planes = TRUE;
/* We can't use clip planes a second time */
has_clip_planes = FALSE;
}
else
{
_cogl_add_stencil_clip (rect->x_offset,
rect->y_offset,
rect->width,
rect->height,
!using_stencil_buffer);
using_stencil_buffer = TRUE;
}
cogl_pop_matrix ();
}
}
/* Enabling clip planes is delayed to now so that they won't affect
setting up the stencil buffer */
if (using_clip_planes)
_cogl_enable_clip_planes ();
ctx->clip.stencil_used = using_stencil_buffer;
} }
void void
_cogl_clip_stack_merge (void) cogl_clip_ensure (void)
{ {
GList *node = cogl_clip_stack_top; _COGL_GET_CONTEXT (ctx, NO_RETVAL);
int i;
/* Merge the current clip stack on top of whatever is in the stencil if (ctx->clip.stack_dirty)
buffer */ _cogl_clip_stack_rebuild ();
if (cogl_clip_stack_depth)
{
for (i = 0; i < cogl_clip_stack_depth - 1; i++)
node = node->next;
/* Skip the first entry if we have clipping planes */
if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
node = node->prev;
while (node)
{
const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data;
cogl_push_matrix ();
_cogl_set_matrix (entry->matrix);
_cogl_clip_stack_add (entry, 3);
cogl_pop_matrix ();
node = node->prev;
}
}
} }
void void
cogl_clip_stack_save (void) cogl_clip_stack_save (void)
{ {
CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry); CoglClipStack *stack;
/* Push an entry into the stack to mark that it should be cleared */ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
entry->clear = TRUE;
cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
/* Reset the depth to zero */ stack = g_slice_new (CoglClipStack);
cogl_clip_stack_depth = 0; stack->stack_top = NULL;
/* Rebuilding the stack will now disabling all clipping */ ctx->clip.stacks = g_slist_prepend (ctx->clip.stacks, stack);
_cogl_clip_stack_rebuild (FALSE);
ctx->clip.stack_dirty = TRUE;
} }
void void
cogl_clip_stack_restore (void) cogl_clip_stack_restore (void)
{ {
GList *node; CoglClipStack *stack;
/* The top of the stack should be a clear marker */ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
g_assert (cogl_clip_stack_top);
g_assert (((CoglClipStackEntry *) cogl_clip_stack_top->data)->clear);
/* Remove the top entry */ g_return_if_fail (ctx->clip.stacks != NULL);
g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data);
cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top,
cogl_clip_stack_top);
/* Recalculate the depth of the stack */ stack = (CoglClipStack *) ctx->clip.stacks->data;
cogl_clip_stack_depth = 0;
for (node = cogl_clip_stack_top;
node && !((CoglClipStackEntry *) node->data)->clear;
node = node->next)
cogl_clip_stack_depth++;
_cogl_clip_stack_rebuild (FALSE); /* Empty the current stack */
while (stack->stack_top)
cogl_clip_unset ();
/* Revert to an old stack */
g_slice_free (CoglClipStack, stack);
ctx->clip.stacks = g_slist_delete_link (ctx->clip.stacks,
ctx->clip.stacks);
ctx->clip.stack_dirty = TRUE;
}
void
_cogl_clip_stack_state_init (void)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
ctx->clip.stacks = NULL;
ctx->clip.stack_dirty = TRUE;
/* Add an intial stack */
cogl_clip_stack_save ();
}
void
_cogl_clip_stack_state_destroy (void)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* Destroy all of the stacks */
while (ctx->clip.stacks)
cogl_clip_stack_restore ();
} }

View File

@ -26,7 +26,20 @@
#ifndef __COGL_CLIP_STACK_H #ifndef __COGL_CLIP_STACK_H
#define __COGL_CLIP_STACK_H #define __COGL_CLIP_STACK_H
void _cogl_clip_stack_rebuild (gboolean just_stencil); typedef struct _CoglClipStackState CoglClipStackState;
struct _CoglClipStackState
{
/* Stack of stacks */
GSList *stacks;
gboolean stack_dirty;
gboolean stencil_used;
};
void _cogl_clip_stack_state_init (void);
void _cogl_clip_stack_state_destroy (void);
void _cogl_clip_stack_rebuild (void);
void _cogl_clip_stack_merge (void); void _cogl_clip_stack_merge (void);
#endif /* __COGL_CLIP_STACK_H */ #endif /* __COGL_CLIP_STACK_H */

View File

@ -37,8 +37,8 @@
#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 #define _COGL_MAX_BEZ_RECURSE_DEPTH 16
/* these are defined in the particular backend(float in gl vs fixed in gles)*/ /* these are defined in the particular backend(float in gl vs fixed in gles)*/
void _cogl_path_clear_nodes (); void _cogl_path_add_node (gboolean new_sub_path,
void _cogl_path_add_node (CoglFixed x, CoglFixed x,
CoglFixed y); CoglFixed y);
void _cogl_path_fill_nodes (); void _cogl_path_fill_nodes ();
void _cogl_path_stroke_nodes (); void _cogl_path_stroke_nodes ();
@ -56,6 +56,8 @@ cogl_rectangle (gint x,
guint width, guint width,
guint height) guint height)
{ {
cogl_clip_ensure ();
_cogl_rectangle (x, y, width, height); _cogl_rectangle (x, y, width, height);
} }
@ -65,27 +67,49 @@ cogl_rectanglex (CoglFixed x,
CoglFixed width, CoglFixed width,
CoglFixed height) CoglFixed height)
{ {
cogl_clip_ensure ();
_cogl_rectanglex (x, y, width, height); _cogl_rectanglex (x, y, width, height);
} }
void void
cogl_path_fill (void) cogl_path_fill (void)
{
cogl_path_fill_preserve ();
cogl_path_new ();
}
void
cogl_path_fill_preserve (void)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_clip_ensure ();
if (ctx->path_nodes_size == 0) if (ctx->path_nodes->len == 0)
return; return;
_cogl_path_fill_nodes(); _cogl_path_fill_nodes ();
} }
void void
cogl_path_stroke (void) cogl_path_stroke (void)
{
cogl_path_stroke_preserve ();
cogl_path_new ();
}
void
cogl_path_stroke_preserve (void)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes_size == 0) cogl_clip_ensure ();
if (ctx->path_nodes->len == 0)
return; return;
_cogl_path_stroke_nodes(); _cogl_path_stroke_nodes();
@ -99,11 +123,7 @@ cogl_path_move_to (CoglFixed x,
/* FIXME: handle multiple contours maybe? */ /* FIXME: handle multiple contours maybe? */
/* at the moment, a move_to is an implicit instruction to create _cogl_path_add_node (TRUE, x, y);
* a new path.
*/
_cogl_path_clear_nodes ();
_cogl_path_add_node (x, y);
ctx->path_start.x = x; ctx->path_start.x = x;
ctx->path_start.y = y; ctx->path_start.y = y;
@ -127,7 +147,7 @@ cogl_path_line_to (CoglFixed x,
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_add_node (x, y); _cogl_path_add_node (FALSE, x, y);
ctx->path_pen.x = x; ctx->path_pen.x = x;
ctx->path_pen.y = y; ctx->path_pen.y = y;
@ -148,10 +168,17 @@ cogl_path_close (void)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_add_node (ctx->path_start.x, ctx->path_start.y); _cogl_path_add_node (FALSE, ctx->path_start.x, ctx->path_start.y);
ctx->path_pen = ctx->path_start; ctx->path_pen = ctx->path_start;
} }
void
cogl_path_new (void)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
g_array_set_size (ctx->path_nodes, 0);
}
void void
cogl_path_line (CoglFixed x1, cogl_path_line (CoglFixed x1,
@ -427,7 +454,7 @@ _cogl_path_bezier3_sub (CoglBezCubic *cubic)
if (cindex == 0) if (cindex == 0)
return; return;
_cogl_path_add_node (c->p4.x, c->p4.y); _cogl_path_add_node (FALSE, c->p4.x, c->p4.y);
--cindex; --cindex;
@ -492,7 +519,7 @@ cogl_path_curve_to (CoglFixed x1,
_cogl_path_bezier3_sub (&cubic); _cogl_path_bezier3_sub (&cubic);
/* Add last point */ /* Add last point */
_cogl_path_add_node (cubic.p4.x, cubic.p4.y); _cogl_path_add_node (FALSE, cubic.p4.x, cubic.p4.y);
ctx->path_pen = cubic.p4; ctx->path_pen = cubic.p4;
} }
@ -559,7 +586,7 @@ _cogl_path_bezier2_sub (CoglBezQuad *quad)
{ {
/* Add subdivision point (skip last) */ /* Add subdivision point (skip last) */
if (qindex == 0) return; if (qindex == 0) return;
_cogl_path_add_node (q->p3.x, q->p3.y); _cogl_path_add_node (FALSE, q->p3.x, q->p3.y);
--qindex; continue; --qindex; continue;
} }
@ -607,7 +634,7 @@ cogl_path_curve2_to (CoglFixed x1,
_cogl_path_bezier2_sub (&quad); _cogl_path_bezier2_sub (&quad);
/* Add last point */ /* Add last point */
_cogl_path_add_node (quad.p3.x, quad.p3.y); _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y);
ctx->path_pen = quad.p3; ctx->path_pen = quad.p3;
} }

View File

@ -29,6 +29,7 @@
typedef struct _CoglFixedVec2 CoglFixedVec2; typedef struct _CoglFixedVec2 CoglFixedVec2;
typedef struct _CoglBezQuad CoglBezQuad; typedef struct _CoglBezQuad CoglBezQuad;
typedef struct _CoglBezCubic CoglBezCubic; typedef struct _CoglBezCubic CoglBezCubic;
typedef struct _CoglPathNode CoglPathNode;
struct _CoglFixedVec2 struct _CoglFixedVec2
{ {
@ -37,13 +38,32 @@ struct _CoglFixedVec2
}; };
#ifdef CLUTTER_COGL_HAS_GL #ifdef CLUTTER_COGL_HAS_GL
typedef struct _CoglFloatVec2 CoglFloatVec2; typedef struct _CoglFloatVec2 CoglFloatVec2;
struct _CoglFloatVec2 struct _CoglFloatVec2
{ {
GLfloat x; GLfloat x;
GLfloat y; GLfloat y;
}; };
#endif
struct _CoglPathNode
{
GLfloat x;
GLfloat y;
guint path_size;
};
#else /* CLUTTER_COGL_HAS_GL */
struct _CoglPathNode
{
GLfixed x;
GLfixed y;
guint path_size;
};
#endif /* CLUTTER_COGL_HAS_GL */
struct _CoglBezQuad struct _CoglBezQuad
{ {

View File

@ -29,9 +29,12 @@ cogl_rotatex
cogl_rotate cogl_rotate
<SUBSECTION> <SUBSECTION>
cogl_clip_set cogl_clip_set
cogl_clip_set_from_path
cogl_clip_set_from_path_preserve
cogl_clip_unset cogl_clip_unset
cogl_clip_stack_save cogl_clip_stack_save
cogl_clip_stack_restore cogl_clip_stack_restore
cogl_clip_ensure
<SUBSECTION> <SUBSECTION>
cogl_enable_depth_test cogl_enable_depth_test
cogl_enable_backface_culling cogl_enable_backface_culling
@ -63,6 +66,7 @@ cogl_util_next_p2
<SECTION> <SECTION>
<FILE>cogl-primitives</FILE> <FILE>cogl-primitives</FILE>
<TITLE>Primitives</TITLE> <TITLE>Primitives</TITLE>
cogl_path_new
cogl_path_move_to cogl_path_move_to
cogl_path_close cogl_path_close
cogl_path_line_to cogl_path_line_to
@ -80,7 +84,9 @@ cogl_path_ellipse
<SUBSECTION> <SUBSECTION>
cogl_path_fill cogl_path_fill
cogl_path_fill_preserve
cogl_path_stroke cogl_path_stroke
cogl_path_stroke_preserve
cogl_set_source_color cogl_set_source_color
cogl_set_source_color4ub cogl_set_source_color4ub
cogl_set_source_color4x cogl_set_source_color4x

View File

@ -52,9 +52,8 @@ cogl_create_context ()
_context->enable_flags = 0; _context->enable_flags = 0;
_context->color_alpha = 255; _context->color_alpha = 255;
_context->path_nodes = NULL; _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
_context->path_nodes_cap = 0; _context->last_path = 0;
_context->path_nodes_size = 0;
_context->texture_handles = NULL; _context->texture_handles = NULL;
_context->texture_vertices_size = 0; _context->texture_vertices_size = 0;
@ -122,6 +121,9 @@ cogl_create_context ()
GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); GE( glColorMask (TRUE, TRUE, TRUE, FALSE) );
GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) );
cogl_enable (0); cogl_enable (0);
/* Initialise the clip stack */
_cogl_clip_stack_state_init ();
return TRUE; return TRUE;
} }
@ -132,6 +134,11 @@ cogl_destroy_context ()
if (_context == NULL) if (_context == NULL)
return; return;
_cogl_clip_stack_state_destroy ();
if (_context->path_nodes)
g_array_free (_context->path_nodes, TRUE);
if (_context->texture_handles) if (_context->texture_handles)
g_array_free (_context->texture_handles, TRUE); g_array_free (_context->texture_handles, TRUE);
if (_context->fbo_handles) if (_context->fbo_handles)

View File

@ -27,6 +27,7 @@
#define __COGL_CONTEXT_H #define __COGL_CONTEXT_H
#include "cogl-primitives.h" #include "cogl-primitives.h"
#include "cogl-clip-stack.h"
typedef struct typedef struct
{ {
@ -40,7 +41,6 @@ typedef struct
/* Features cache */ /* Features cache */
CoglFeatureFlags feature_flags; CoglFeatureFlags feature_flags;
gboolean features_cached; gboolean features_cached;
GLint num_stencil_bits;
/* Enable cache */ /* Enable cache */
gulong enable_flags; gulong enable_flags;
@ -53,9 +53,8 @@ typedef struct
/* Primitives */ /* Primitives */
CoglFixedVec2 path_start; CoglFixedVec2 path_start;
CoglFixedVec2 path_pen; CoglFixedVec2 path_pen;
CoglFloatVec2 *path_nodes; GArray *path_nodes;
guint path_nodes_cap; guint last_path;
guint path_nodes_size;
CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_min;
CoglFixedVec2 path_nodes_max; CoglFixedVec2 path_nodes_max;
@ -76,6 +75,9 @@ typedef struct
/* Programs */ /* Programs */
GArray *program_handles; GArray *program_handles;
/* Clip stack */
CoglClipStackState clip;
/* Mesh */ /* Mesh */
GArray *mesh_handles; GArray *mesh_handles;

View File

@ -70,43 +70,26 @@ _cogl_rectanglex (CoglFixed x,
} }
void void
_cogl_path_clear_nodes () _cogl_path_add_node (gboolean new_sub_path,
{ CoglFixed x,
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes)
g_free(ctx->path_nodes);
ctx->path_nodes = (CoglFloatVec2*) g_malloc (2 * sizeof(CoglFloatVec2));
ctx->path_nodes_size = 0;
ctx->path_nodes_cap = 2;
}
void
_cogl_path_add_node (CoglFixed x,
CoglFixed y) CoglFixed y)
{ {
CoglFloatVec2 *new_nodes = NULL; CoglPathNode new_node;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes_size == ctx->path_nodes_cap)
{
new_nodes = g_realloc (ctx->path_nodes,
2 * ctx->path_nodes_cap
* sizeof (CoglFloatVec2));
if (new_nodes == NULL) return;
ctx->path_nodes = new_nodes; new_node.x = COGL_FIXED_TO_FLOAT (x);
ctx->path_nodes_cap *= 2; new_node.y = COGL_FIXED_TO_FLOAT (y);
} new_node.path_size = 0;
ctx->path_nodes [ctx->path_nodes_size] .x = COGL_FIXED_TO_FLOAT (x); if (new_sub_path || ctx->path_nodes->len == 0)
ctx->path_nodes [ctx->path_nodes_size] .y = COGL_FIXED_TO_FLOAT (y); ctx->last_path = ctx->path_nodes->len;
ctx->path_nodes_size++;
g_array_append_val (ctx->path_nodes, new_node);
if (ctx->path_nodes_size == 1)
g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++;
if (ctx->path_nodes->len == 1)
{ {
ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; ctx->path_nodes_min.x = ctx->path_nodes_max.x = x;
ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
@ -123,55 +106,158 @@ _cogl_path_add_node (CoglFixed x,
void void
_cogl_path_stroke_nodes () _cogl_path_stroke_nodes ()
{ {
guint path_start = 0;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_enable (COGL_ENABLE_VERTEX_ARRAY cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 | (ctx->color_alpha < 255
? COGL_ENABLE_BLEND : 0)); ? COGL_ENABLE_BLEND : 0));
while (path_start < ctx->path_nodes->len)
{
CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode,
path_start);
GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode),
(guchar *) path
+ G_STRUCT_OFFSET (CoglPathNode, x)) );
GE( glDrawArrays (GL_LINE_STRIP, 0, path->path_size) );
path_start += path->path_size;
}
}
static void
_cogl_path_get_bounds (CoglFixedVec2 nodes_min,
CoglFixedVec2 nodes_max,
gint *bounds_x,
gint *bounds_y,
guint *bounds_w,
guint *bounds_h)
{
*bounds_x = COGL_FIXED_FLOOR (nodes_min.x);
*bounds_y = COGL_FIXED_FLOOR (nodes_min.y);
*bounds_w = COGL_FIXED_CEIL (nodes_max.x
- COGL_FIXED_FROM_INT (*bounds_x));
*bounds_h = COGL_FIXED_CEIL (nodes_max.y
- COGL_FIXED_FROM_INT (*bounds_y));
}
void
_cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min,
CoglFixedVec2 nodes_max,
guint path_size,
CoglPathNode *path,
gboolean merge)
{
guint path_start = 0;
guint sub_path_num = 0;
gint bounds_x;
gint bounds_y;
guint bounds_w;
guint bounds_h;
_cogl_path_get_bounds (nodes_min, nodes_max,
&bounds_x, &bounds_y, &bounds_w, &bounds_h);
if (merge)
{
GE( glStencilMask (2) );
GE( glStencilFunc (GL_LEQUAL, 0x2, 0x6) );
}
else
{
GE( glClear (GL_STENCIL_BUFFER_BIT) );
GE( glStencilMask (1) );
GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) );
}
GE( glEnable (GL_STENCIL_TEST) );
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
GE( glColorMask (FALSE, FALSE, FALSE, FALSE) );
GE( glDepthMask (FALSE) );
GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); while (path_start < path_size)
GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); {
cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode),
(guchar *) path
+ G_STRUCT_OFFSET (CoglPathNode, x)) );
GE( glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size) );
if (sub_path_num > 0)
{
/* Union the two stencil buffers bits into the least
significant bit */
GE( glStencilMask (merge ? 6 : 3) );
GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) );
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
}
GE( glStencilMask (merge ? 4 : 2) );
path_start += path->path_size;
path += path->path_size;
sub_path_num++;
}
if (merge)
{
/* Now we have the new stencil buffer in bit 1 and the old
stencil buffer in bit 0 so we need to intersect them */
GE( glStencilMask (3) );
GE( glStencilFunc (GL_NEVER, 0x2, 0x3) );
GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
/* Decrement all of the bits twice so that only pixels where the
value is 3 will remain */
GE( glPushMatrix () );
GE( glLoadIdentity () );
GE( glMatrixMode (GL_PROJECTION) );
GE( glPushMatrix () );
GE( glLoadIdentity () );
GE( glRecti (-1, 1, 1, -1) );
GE( glRecti (-1, 1, 1, -1) );
GE( glPopMatrix () );
GE( glMatrixMode (GL_MODELVIEW) );
GE( glPopMatrix () );
}
GE( glStencilMask (~(GLuint) 0) );
GE( glDepthMask (TRUE) );
GE( glColorMask (TRUE, TRUE, TRUE, TRUE) );
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
} }
void void
_cogl_path_fill_nodes () _cogl_path_fill_nodes ()
{ {
guint bounds_x; gint bounds_x;
guint bounds_y; gint bounds_y;
guint bounds_w; guint bounds_w;
guint bounds_h; guint bounds_h;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
GE( glClear (GL_STENCIL_BUFFER_BIT) );
GE( glEnable (GL_STENCIL_TEST) ); _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min,
GE( glStencilFunc (GL_NEVER, 0x0, 0x1) ); ctx->path_nodes_max,
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); ctx->path_nodes->len,
&g_array_index (ctx->path_nodes,
CoglPathNode, 0),
ctx->clip.stencil_used);
GE( glStencilMask (1) ); _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max,
&bounds_x, &bounds_y, &bounds_w, &bounds_h);
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) );
GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) );
GE( glStencilMask (~(GLuint) 0) );
/* Merge the stencil buffer with any clipping rectangles */
_cogl_clip_stack_merge ();
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
bounds_x = COGL_FIXED_FLOOR (ctx->path_nodes_min.x);
bounds_y = COGL_FIXED_FLOOR (ctx->path_nodes_min.y);
bounds_w = COGL_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x);
bounds_h = COGL_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y);
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
/* Rebuild the stencil clip */ /* The stencil buffer now contains garbage so the clip area needs to
_cogl_clip_stack_rebuild (TRUE); be rebuilt */
ctx->clip.stack_dirty = TRUE;
} }

View File

@ -2197,6 +2197,8 @@ cogl_texture_rectangle (CoglHandle handle,
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* Make sure we got stuff to draw */ /* Make sure we got stuff to draw */
@ -2242,6 +2244,8 @@ cogl_texture_polygon (CoglHandle handle,
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* The polygon will have artifacts where the slices join if the wrap /* The polygon will have artifacts where the slices join if the wrap

161
gl/cogl.c
View File

@ -457,8 +457,6 @@ set_clip_plane (GLint plane_num,
GE( glClipPlane (plane_num, plane) ); GE( glClipPlane (plane_num, plane) );
GE( glPopMatrix () ); GE( glPopMatrix () );
GE( glEnable (plane_num) );
} }
void void
@ -513,15 +511,6 @@ _cogl_set_clip_planes (CoglFixed x_offset,
set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl); set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl);
} }
static int
compare_y_coordinate (const void *a, const void *b)
{
GLfloat ay = ((const GLfloat *) a)[1];
GLfloat by = ((const GLfloat *) b)[1];
return ay < by ? -1 : ay > by ? 1 : 0;
}
void void
_cogl_add_stencil_clip (CoglFixed x_offset, _cogl_add_stencil_clip (CoglFixed x_offset,
CoglFixed y_offset, CoglFixed y_offset,
@ -529,19 +518,8 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
CoglFixed height, CoglFixed height,
gboolean first) gboolean first)
{ {
gboolean has_clip_planes
= cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (has_clip_planes)
{
GE( glDisable (GL_CLIP_PLANE3) );
GE( glDisable (GL_CLIP_PLANE2) );
GE( glDisable (GL_CLIP_PLANE1) );
GE( glDisable (GL_CLIP_PLANE0) );
}
if (first) if (first)
{ {
GE( glEnable (GL_STENCIL_TEST) ); GE( glEnable (GL_STENCIL_TEST) );
@ -558,7 +536,7 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
COGL_FIXED_TO_FLOAT (x_offset + width), COGL_FIXED_TO_FLOAT (x_offset + width),
COGL_FIXED_TO_FLOAT (y_offset + height)) ); COGL_FIXED_TO_FLOAT (y_offset + height)) );
} }
else if (ctx->num_stencil_bits > 1) else
{ {
/* Add one to every pixel of the stencil buffer in the /* Add one to every pixel of the stencil buffer in the
rectangle */ rectangle */
@ -583,127 +561,6 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
GE( glMatrixMode (GL_MODELVIEW) ); GE( glMatrixMode (GL_MODELVIEW) );
GE( glPopMatrix () ); GE( glPopMatrix () );
} }
else
{
/* Slower fallback if there is exactly one stencil bit. This
tries to draw enough triangles to tessalate around the
rectangle so that it can subtract from the stencil buffer for
every pixel in the screen except those in the rectangle */
GLfloat modelview[16], projection[16];
GLfloat temp_point[4];
GLfloat left_edge, right_edge, bottom_edge, top_edge;
int i;
GLfloat points[16] =
{
COGL_FIXED_TO_FLOAT (x_offset),
COGL_FIXED_TO_FLOAT (y_offset),
0, 1,
COGL_FIXED_TO_FLOAT (x_offset + width),
COGL_FIXED_TO_FLOAT (y_offset),
0, 1,
COGL_FIXED_TO_FLOAT (x_offset),
COGL_FIXED_TO_FLOAT (y_offset + height),
0, 1,
COGL_FIXED_TO_FLOAT (x_offset + width),
COGL_FIXED_TO_FLOAT (y_offset + height),
0, 1
};
GE( glGetFloatv (GL_MODELVIEW_MATRIX, modelview) );
GE( glGetFloatv (GL_PROJECTION_MATRIX, projection) );
/* Project all of the vertices into screen coordinates */
for (i = 0; i < 4; i++)
project_vertex (modelview, projection, points + i * 4);
/* Sort the points by y coordinate */
qsort (points, 4, sizeof (GLfloat) * 4, compare_y_coordinate);
/* Put the bottom two pairs and the top two pairs in
left-right order */
if (points[0] > points[4])
{
memcpy (temp_point, points, sizeof (GLfloat) * 4);
memcpy (points, points + 4, sizeof (GLfloat) * 4);
memcpy (points + 4, temp_point, sizeof (GLfloat) * 4);
}
if (points[8] > points[12])
{
memcpy (temp_point, points + 8, sizeof (GLfloat) * 4);
memcpy (points + 8, points + 12, sizeof (GLfloat) * 4);
memcpy (points + 12, temp_point, sizeof (GLfloat) * 4);
}
/* If the clip rect goes outside of the screen then use the
extents of the rect instead */
left_edge = MIN (-1.0f, MIN (points[0], points[8]));
right_edge = MAX ( 1.0f, MAX (points[4], points[12]));
bottom_edge = MIN (-1.0f, MIN (points[1], points[5]));
top_edge = MAX ( 1.0f, MAX (points[9], points[13]));
/* Using the identity matrix for the projection and
modelview matrix, draw the triangles around the inner
rectangle */
GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
GE( glPushMatrix () );
GE( glLoadIdentity () );
GE( glMatrixMode (GL_PROJECTION) );
GE( glPushMatrix () );
GE( glLoadIdentity () );
/* Clear the left side */
glBegin (GL_TRIANGLE_STRIP);
glVertex2f (left_edge, bottom_edge);
glVertex2fv (points);
glVertex2f (left_edge, points[1]);
glVertex2fv (points + 8);
glVertex2f (left_edge, points[9]);
glVertex2f (left_edge, top_edge);
glEnd ();
/* Clear the right side */
glBegin (GL_TRIANGLE_STRIP);
glVertex2f (right_edge, top_edge);
glVertex2fv (points + 12);
glVertex2f (right_edge, points[13]);
glVertex2fv (points + 4);
glVertex2f (right_edge, points[5]);
glVertex2f (right_edge, bottom_edge);
glEnd ();
/* Clear the top side */
glBegin (GL_TRIANGLE_STRIP);
glVertex2f (left_edge, top_edge);
glVertex2fv (points + 8);
glVertex2f (points[8], top_edge);
glVertex2fv (points + 12);
glVertex2f (points[12], top_edge);
glVertex2f (right_edge, top_edge);
glEnd ();
/* Clear the bottom side */
glBegin (GL_TRIANGLE_STRIP);
glVertex2f (left_edge, bottom_edge);
glVertex2fv (points);
glVertex2f (points[0], bottom_edge);
glVertex2fv (points + 4);
glVertex2f (points[4], bottom_edge);
glVertex2f (right_edge, bottom_edge);
glEnd ();
GE( glPopMatrix () );
GE( glMatrixMode (GL_MODELVIEW) );
GE( glPopMatrix () );
}
if (has_clip_planes)
{
GE( glEnable (GL_CLIP_PLANE0) );
GE( glEnable (GL_CLIP_PLANE1) );
GE( glEnable (GL_CLIP_PLANE2) );
GE( glEnable (GL_CLIP_PLANE3) );
}
/* Restore the stencil mode */ /* Restore the stencil mode */
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
@ -729,6 +586,15 @@ _cogl_disable_stencil_buffer (void)
GE( glDisable (GL_STENCIL_TEST) ); GE( glDisable (GL_STENCIL_TEST) );
} }
void
_cogl_enable_clip_planes (void)
{
GE( glEnable (GL_CLIP_PLANE0) );
GE( glEnable (GL_CLIP_PLANE1) );
GE( glEnable (GL_CLIP_PLANE2) );
GE( glEnable (GL_CLIP_PLANE3) );
}
void void
_cogl_disable_clip_planes (void) _cogl_disable_clip_planes (void)
{ {
@ -945,6 +811,7 @@ _cogl_features_init ()
CoglFeatureFlags flags = 0; CoglFeatureFlags flags = 0;
const gchar *gl_extensions; const gchar *gl_extensions;
GLint max_clip_planes = 0; GLint max_clip_planes = 0;
GLint num_stencil_bits = 0;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -1214,9 +1081,9 @@ _cogl_features_init ()
flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE;
} }
ctx->num_stencil_bits = 0; GE( glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) );
GE( glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) ); /* We need at least three stencil bits to combine clips */
if (ctx->num_stencil_bits > 0) if (num_stencil_bits > 2)
flags |= COGL_FEATURE_STENCIL_BUFFER; flags |= COGL_FEATURE_STENCIL_BUFFER;
GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );

View File

@ -55,10 +55,9 @@ cogl_create_context ()
_context->enable_flags = 0; _context->enable_flags = 0;
_context->color_alpha = 255; _context->color_alpha = 255;
_context->path_nodes = NULL; _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
_context->path_nodes_cap = 0; _context->last_path = 0;
_context->path_nodes_size = 0;
_context->texture_handles = NULL; _context->texture_handles = NULL;
_context->texture_vertices_size = 0; _context->texture_vertices_size = 0;
_context->texture_vertices = NULL; _context->texture_vertices = NULL;
@ -83,6 +82,8 @@ cogl_create_context ()
GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); GE( glColorMask (TRUE, TRUE, TRUE, FALSE) );
GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) );
cogl_enable (0); cogl_enable (0);
_cogl_clip_stack_state_init ();
return TRUE; return TRUE;
} }
@ -93,6 +94,11 @@ cogl_destroy_context ()
if (_context == NULL) if (_context == NULL)
return; return;
_cogl_clip_stack_state_destroy ();
if (_context->path_nodes)
g_array_free (_context->path_nodes, TRUE);
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
cogl_gles2_wrapper_deinit (&_context->gles2); cogl_gles2_wrapper_deinit (&_context->gles2);
#endif #endif

View File

@ -27,6 +27,7 @@
#define __COGL_CONTEXT_H #define __COGL_CONTEXT_H
#include "cogl-primitives.h" #include "cogl-primitives.h"
#include "cogl-clip-stack.h"
#include "cogl-gles2-wrapper.h" #include "cogl-gles2-wrapper.h"
@ -42,7 +43,6 @@ typedef struct
/* Features cache */ /* Features cache */
CoglFeatureFlags feature_flags; CoglFeatureFlags feature_flags;
gboolean features_cached; gboolean features_cached;
GLint num_stencil_bits;
/* Enable cache */ /* Enable cache */
gulong enable_flags; gulong enable_flags;
@ -55,9 +55,8 @@ typedef struct
/* Primitives */ /* Primitives */
CoglFixedVec2 path_start; CoglFixedVec2 path_start;
CoglFixedVec2 path_pen; CoglFixedVec2 path_pen;
CoglFixedVec2 *path_nodes; GArray *path_nodes;
guint path_nodes_cap; guint last_path;
guint path_nodes_size;
CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_min;
CoglFixedVec2 path_nodes_max; CoglFixedVec2 path_nodes_max;
@ -80,6 +79,9 @@ typedef struct
/* Mesh */ /* Mesh */
GArray *mesh_handles; GArray *mesh_handles;
/* Clip stack */
CoglClipStackState clip;
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
CoglGles2Wrapper gles2; CoglGles2Wrapper gles2;

View File

@ -87,45 +87,27 @@ _cogl_rectanglex (CoglFixed x,
} }
void void
_cogl_path_clear_nodes () _cogl_path_add_node (gboolean new_sub_path,
{ CoglFixed x,
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes)
g_free(ctx->path_nodes);
ctx->path_nodes = (CoglFixedVec2*) g_malloc (2 * sizeof(CoglFixedVec2));
ctx->path_nodes_size = 0;
ctx->path_nodes_cap = 2;
}
void
_cogl_path_add_node (CoglFixed x,
CoglFixed y) CoglFixed y)
{ {
CoglFixedVec2 *new_nodes = NULL; CoglPathNode new_node;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes_size == ctx->path_nodes_cap)
{
new_nodes = g_realloc (ctx->path_nodes,
2 * ctx->path_nodes_cap
* sizeof (CoglFixedVec2));
if (new_nodes == NULL) return;
ctx->path_nodes = new_nodes; _COGL_GET_CONTEXT (ctx, NO_RETVAL);
ctx->path_nodes_cap *= 2;
} new_node.x = x;
new_node.y = y;
ctx->path_nodes [ctx->path_nodes_size].x = x; new_node.path_size = 0;
ctx->path_nodes [ctx->path_nodes_size].y = y;
ctx->path_nodes_size++; if (new_sub_path || ctx->path_nodes->len == 0)
ctx->last_path = ctx->path_nodes->len;
if (ctx->path_nodes_size == 1)
g_array_append_val (ctx->path_nodes, new_node);
g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++;
if (ctx->path_nodes->len == 1)
{ {
ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; ctx->path_nodes_min.x = ctx->path_nodes_max.x = x;
ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
@ -142,14 +124,42 @@ _cogl_path_add_node (CoglFixed x,
void void
_cogl_path_stroke_nodes () _cogl_path_stroke_nodes ()
{ {
guint path_start = 0;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_enable (COGL_ENABLE_VERTEX_ARRAY cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 | (ctx->color_alpha < 255
? COGL_ENABLE_BLEND : 0)); ? COGL_ENABLE_BLEND : 0));
GE( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); while (path_start < ctx->path_nodes->len)
GE( cogl_wrap_glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); {
CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode,
path_start);
GE( cogl_wrap_glVertexPointer (2, GL_FIXED, sizeof (CoglPathNode),
(guchar *) path
+ G_STRUCT_OFFSET (CoglPathNode, x)) );
GE( cogl_wrap_glDrawArrays (GL_LINE_STRIP, 0, path->path_size) );
path_start += path->path_size;
}
}
static void
_cogl_path_get_bounds (CoglFixedVec2 nodes_min,
CoglFixedVec2 nodes_max,
gint *bounds_x,
gint *bounds_y,
guint *bounds_w,
guint *bounds_h)
{
*bounds_x = COGL_FIXED_FLOOR (nodes_min.x);
*bounds_y = COGL_FIXED_FLOOR (nodes_min.y);
*bounds_w = COGL_FIXED_CEIL (nodes_max.x
- COGL_FIXED_FROM_INT (*bounds_x));
*bounds_h = COGL_FIXED_CEIL (nodes_max.y
- COGL_FIXED_FROM_INT (*bounds_y));
} }
static gint compare_ints (gconstpointer a, static gint compare_ints (gconstpointer a,
@ -158,209 +168,314 @@ static gint compare_ints (gconstpointer a,
return GPOINTER_TO_INT(a)-GPOINTER_TO_INT(b); return GPOINTER_TO_INT(a)-GPOINTER_TO_INT(b);
} }
void
_cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min,
CoglFixedVec2 nodes_max,
guint path_size,
CoglPathNode *path,
gboolean merge)
{
guint path_start = 0;
guint sub_path_num = 0;
gint bounds_x;
gint bounds_y;
guint bounds_w;
guint bounds_h;
_cogl_path_get_bounds (nodes_min, nodes_max,
&bounds_x, &bounds_y, &bounds_w, &bounds_h);
if (merge)
{
GE( glStencilMask (2) );
GE( glStencilFunc (GL_LEQUAL, 0x2, 0x6) );
}
else
{
GE( glClear (GL_STENCIL_BUFFER_BIT) );
GE( glStencilMask (1) );
GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) );
}
GE( cogl_wrap_glEnable (GL_STENCIL_TEST) );
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
GE( glColorMask (FALSE, FALSE, FALSE, FALSE) );
GE( glDepthMask (FALSE) );
while (path_start < path_size)
{
cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
GE( cogl_wrap_glVertexPointer (2, GL_FIXED, sizeof (CoglPathNode),
(guchar *) path
+ G_STRUCT_OFFSET (CoglPathNode, x)) );
GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size) );
if (sub_path_num > 0)
{
/* Union the two stencil buffers bits into the least
significant bit */
GE( glStencilMask (merge ? 6 : 3) );
GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) );
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
}
GE( glStencilMask (merge ? 4 : 2) );
path_start += path->path_size;
path += path->path_size;
sub_path_num++;
}
if (merge)
{
/* Now we have the new stencil buffer in bit 1 and the old
stencil buffer in bit 0 so we need to intersect them */
GE( glStencilMask (3) );
GE( glStencilFunc (GL_NEVER, 0x2, 0x3) );
GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
/* Decrement all of the bits twice so that only pixels where the
value is 3 will remain */
GE( cogl_wrap_glPushMatrix () );
GE( cogl_wrap_glLoadIdentity () );
GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
GE( cogl_wrap_glPushMatrix () );
GE( cogl_wrap_glLoadIdentity () );
cogl_rectanglex (-COGL_FIXED_1, -COGL_FIXED_1,
COGL_FIXED_FROM_INT (2),
COGL_FIXED_FROM_INT (2));
cogl_rectanglex (-COGL_FIXED_1, -COGL_FIXED_1,
COGL_FIXED_FROM_INT (2),
COGL_FIXED_FROM_INT (2));
GE( cogl_wrap_glPopMatrix () );
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
GE( cogl_wrap_glPopMatrix () );
}
GE( glStencilMask (~(GLuint) 0) );
GE( glDepthMask (TRUE) );
GE( glColorMask (TRUE, TRUE, TRUE, TRUE) );
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
}
static void
_cogl_path_fill_nodes_scanlines (CoglPathNode *path,
guint path_size,
gint bounds_x,
gint bounds_y,
guint bounds_w,
guint bounds_h)
{
/* This is our edge list it stores intersections between our
* curve and scanlines, it should probably be implemented with a
* data structure that has smaller overhead for inserting the
* curve/scanline intersections.
*/
GSList *scanlines[bounds_h];
gint i;
gint prev_x;
gint prev_y;
gint first_x;
gint first_y;
gint lastdir=-2; /* last direction we vere moving */
gint lastline=-1; /* the previous scanline we added to */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* clear scanline intersection lists */
for (i=0; i < bounds_h; i++)
scanlines[i]=NULL;
first_x = prev_x = COGL_FIXED_TO_INT (path->x);
first_y = prev_y = COGL_FIXED_TO_INT (path->y);
/* create scanline intersection list */
for (i=1; i < path_size; i++)
{
gint dest_x = COGL_FIXED_TO_INT (path[i].x);
gint dest_y = COGL_FIXED_TO_INT (path[i].y);
gint ydir;
gint dx;
gint dy;
gint y;
fill_close:
dx = dest_x - prev_x;
dy = dest_y - prev_y;
if (dy < 0)
ydir = -1;
else if (dy > 0)
ydir = 1;
else
ydir = 0;
/* do linear interpolation between vertexes */
for (y=prev_y; y!= dest_y; y += ydir)
{
/* only add a point if the scanline has changed and we're
* within bounds.
*/
if (y-bounds_y >= 0 &&
y-bounds_y < bounds_h &&
lastline != y)
{
gint x = prev_x + (dx * (y-prev_y)) / dy;
scanlines[ y - bounds_y ]=
g_slist_insert_sorted (scanlines[ y - bounds_y],
GINT_TO_POINTER(x),
compare_ints);
if (ydir != lastdir && /* add a double entry when changing */
lastdir!=-2) /* vertical direction */
scanlines[ y - bounds_y ]=
g_slist_insert_sorted (scanlines[ y - bounds_y],
GINT_TO_POINTER(x),
compare_ints);
lastdir = ydir;
lastline = y;
}
}
prev_x = dest_x;
prev_y = dest_y;
/* if we're on the last knot, fake the first vertex being a
next one */
if (path_size == i+1)
{
dest_x = first_x;
dest_y = first_y;
i++; /* to make the loop finally end */
goto fill_close;
}
}
{
gint spans = 0;
gint span_no;
GLfixed *coords;
/* count number of spans */
for (i=0; i < bounds_h; i++)
{
GSList *iter = scanlines[i];
while (iter)
{
GSList *next = iter->next;
if (!next)
{
break;
}
/* draw the segments that should be visible */
spans ++;
iter = next->next;
}
}
coords = g_malloc0 (spans * sizeof (GLfixed) * 3 * 2 * 2);
span_no = 0;
/* build list of triangles */
for (i=0; i < bounds_h; i++)
{
GSList *iter = scanlines[i];
while (iter)
{
GSList *next = iter->next;
GLfixed x0, x1;
GLfixed y0, y1;
if (!next)
break;
x0 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (iter->data));
x1 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (next->data));
y0 = COGL_FIXED_FROM_INT (bounds_y + i);
y1 = COGL_FIXED_FROM_INT (bounds_y + i + 1) + 2048;
/* render scanlines 1.0625 high to avoid gaps when
transformed */
coords[span_no * 12 + 0] = x0;
coords[span_no * 12 + 1] = y0;
coords[span_no * 12 + 2] = x1;
coords[span_no * 12 + 3] = y0;
coords[span_no * 12 + 4] = x1;
coords[span_no * 12 + 5] = y1;
coords[span_no * 12 + 6] = x0;
coords[span_no * 12 + 7] = y0;
coords[span_no * 12 + 8] = x0;
coords[span_no * 12 + 9] = y1;
coords[span_no * 12 + 10] = x1;
coords[span_no * 12 + 11] = y1;
span_no ++;
iter = next->next;
}
}
for (i=0; i < bounds_h; i++)
{
g_slist_free (scanlines[i]);
}
/* render triangles */
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
GE ( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, coords ) );
GE ( cogl_wrap_glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3));
g_free (coords);
}
}
void void
_cogl_path_fill_nodes () _cogl_path_fill_nodes ()
{ {
guint bounds_x; gint bounds_x;
guint bounds_y; gint bounds_y;
guint bounds_w; guint bounds_w;
guint bounds_h; guint bounds_h;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
bounds_x = COGL_FIXED_FLOOR (ctx->path_nodes_min.x); _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max,
bounds_y = COGL_FIXED_FLOOR (ctx->path_nodes_min.y); &bounds_x, &bounds_y, &bounds_w, &bounds_h);
bounds_w = COGL_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x);
bounds_h = COGL_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y);
if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
{ {
GE( glClear (GL_STENCIL_BUFFER_BIT) ); _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min,
ctx->path_nodes_max,
ctx->path_nodes->len,
&g_array_index (ctx->path_nodes,
CoglPathNode, 0),
ctx->clip.stencil_used);
GE( cogl_wrap_glEnable (GL_STENCIL_TEST) );
GE( glStencilFunc (GL_NEVER, 0x0, 0x1) );
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
GE( glStencilMask (1) );
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
GE( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) );
GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) );
GE( glStencilMask (~(GLuint) 0) );
/* Merge the stencil buffer with any clipping rectangles */
_cogl_clip_stack_merge ();
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
/* Rebuild the stencil clip */ /* The stencil buffer now contains garbage so the clip area needs to
_cogl_clip_stack_rebuild (TRUE); be rebuilt */
ctx->clip.stack_dirty = TRUE;
} }
else else
{ {
/* This is our edge list it stores intersections between our guint path_start = 0;
* curve and scanlines, it should probably be implemented with a
* data structure that has smaller overhead for inserting the
* curve/scanline intersections.
*/
GSList *scanlines[bounds_h];
gint i; while (path_start < ctx->path_nodes->len)
gint prev_x; {
gint prev_y; CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode,
gint first_x; path_start);
gint first_y;
gint lastdir=-2; /* last direction we vere moving */
gint lastline=-1; /* the previous scanline we added to */
/* clear scanline intersection lists */ _cogl_path_fill_nodes_scanlines (path,
for (i=0; i < bounds_h; i++) path->path_size,
scanlines[i]=NULL; bounds_x, bounds_y,
bounds_w, bounds_h);
first_x = prev_x = COGL_FIXED_TO_INT (ctx->path_nodes[0].x); path_start += path->path_size;
first_y = prev_y = COGL_FIXED_TO_INT (ctx->path_nodes[0].y); }
/* create scanline intersection list */
for (i=1; i<ctx->path_nodes_size; i++)
{
gint dest_x = COGL_FIXED_TO_INT (ctx->path_nodes[i].x);
gint dest_y = COGL_FIXED_TO_INT (ctx->path_nodes[i].y);
gint ydir;
gint dx;
gint dy;
gint y;
fill_close:
dx = dest_x - prev_x;
dy = dest_y - prev_y;
if (dy < 0)
ydir = -1;
else if (dy > 0)
ydir = 1;
else
ydir = 0;
/* do linear interpolation between vertexes */
for (y=prev_y; y!= dest_y; y += ydir)
{
/* only add a point if the scanline has changed and we're
* within bounds.
*/
if (y-bounds_y >= 0 &&
y-bounds_y < bounds_h &&
lastline != y)
{
gint x = prev_x + (dx * (y-prev_y)) / dy;
scanlines[ y - bounds_y ]=
g_slist_insert_sorted (scanlines[ y - bounds_y],
GINT_TO_POINTER(x),
compare_ints);
if (ydir != lastdir && /* add a double entry when changing */
lastdir!=-2) /* vertical direction */
scanlines[ y - bounds_y ]=
g_slist_insert_sorted (scanlines[ y - bounds_y],
GINT_TO_POINTER(x),
compare_ints);
lastdir = ydir;
lastline = y;
}
}
prev_x = dest_x;
prev_y = dest_y;
/* if we're on the last knot, fake the first vertex being a
next one */
if (ctx->path_nodes_size == i+1)
{
dest_x = first_x;
dest_y = first_y;
i++; /* to make the loop finally end */
goto fill_close;
}
}
{
gint spans = 0;
gint span_no;
GLfixed *coords;
/* count number of spans */
for (i=0; i < bounds_h; i++)
{
GSList *iter = scanlines[i];
while (iter)
{
GSList *next = iter->next;
if (!next)
{
break;
}
/* draw the segments that should be visible */
spans ++;
iter = next->next;
}
}
coords = g_malloc0 (spans * sizeof (GLfixed) * 3 * 2 * 2);
span_no = 0;
/* build list of triangles */
for (i=0; i < bounds_h; i++)
{
GSList *iter = scanlines[i];
while (iter)
{
GSList *next = iter->next;
GLfixed x0, x1;
GLfixed y0, y1;
if (!next)
break;
x0 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (iter->data));
x1 = COGL_FIXED_FROM_INT (GPOINTER_TO_INT (next->data));
y0 = COGL_FIXED_FROM_INT (bounds_y + i);
y1 = COGL_FIXED_FROM_INT (bounds_y + i + 1) + 2048;
/* render scanlines 1.0625 high to avoid gaps when
transformed */
coords[span_no * 12 + 0] = x0;
coords[span_no * 12 + 1] = y0;
coords[span_no * 12 + 2] = x1;
coords[span_no * 12 + 3] = y0;
coords[span_no * 12 + 4] = x1;
coords[span_no * 12 + 5] = y1;
coords[span_no * 12 + 6] = x0;
coords[span_no * 12 + 7] = y0;
coords[span_no * 12 + 8] = x0;
coords[span_no * 12 + 9] = y1;
coords[span_no * 12 + 10] = x1;
coords[span_no * 12 + 11] = y1;
span_no ++;
iter = next->next;
}
}
for (i=0; i < bounds_h; i++)
{
g_slist_free (scanlines[i]);
}
/* render triangles */
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
GE ( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, coords ) );
GE ( cogl_wrap_glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3));
g_free (coords);
}
} }
} }

View File

@ -2324,6 +2324,8 @@ cogl_texture_rectangle (CoglHandle handle,
/* Check if valid texture */ /* Check if valid texture */
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
@ -2379,6 +2381,8 @@ cogl_texture_polygon (CoglHandle handle,
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to

View File

@ -389,8 +389,6 @@ set_clip_plane (GLint plane_num,
GE( cogl_wrap_glClipPlanex (plane_num, plane) ); GE( cogl_wrap_glClipPlanex (plane_num, plane) );
GE( cogl_wrap_glPopMatrix () ); GE( cogl_wrap_glPopMatrix () );
GE( cogl_wrap_glEnable (plane_num) );
} }
void void
@ -438,15 +436,6 @@ _cogl_set_clip_planes (CoglFixed x_offset,
set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl); set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl);
} }
static int
compare_y_coordinate (const void *a, const void *b)
{
GLfixed ay = ((const GLfixed *) a)[1];
GLfixed by = ((const GLfixed *) b)[1];
return ay < by ? -1 : ay > by ? 1 : 0;
}
void void
_cogl_add_stencil_clip (CoglFixed x_offset, _cogl_add_stencil_clip (CoglFixed x_offset,
CoglFixed y_offset, CoglFixed y_offset,
@ -454,19 +443,8 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
CoglFixed height, CoglFixed height,
gboolean first) gboolean first)
{ {
gboolean has_clip_planes
= cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (has_clip_planes)
{
GE( cogl_wrap_glDisable (GL_CLIP_PLANE3) );
GE( cogl_wrap_glDisable (GL_CLIP_PLANE2) );
GE( cogl_wrap_glDisable (GL_CLIP_PLANE1) );
GE( cogl_wrap_glDisable (GL_CLIP_PLANE0) );
}
if (first) if (first)
{ {
GE( cogl_wrap_glEnable (GL_STENCIL_TEST) ); GE( cogl_wrap_glEnable (GL_STENCIL_TEST) );
@ -481,7 +459,7 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
cogl_rectanglex (x_offset, y_offset, width, height); cogl_rectanglex (x_offset, y_offset, width, height);
} }
else if (ctx->num_stencil_bits > 1) else
{ {
/* Add one to every pixel of the stencil buffer in the /* Add one to every pixel of the stencil buffer in the
rectangle */ rectangle */
@ -505,120 +483,6 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
GE( cogl_wrap_glPopMatrix () ); GE( cogl_wrap_glPopMatrix () );
} }
else
{
/* Slower fallback if there is exactly one stencil bit. This
tries to draw enough triangles to tessalate around the
rectangle so that it can subtract from the stencil buffer for
every pixel in the screen except those in the rectangle */
GLfixed modelview[16], projection[16];
GLfixed temp_point[4];
GLfixed left_edge, right_edge, bottom_edge, top_edge;
int i;
GLfixed points[16] =
{
x_offset, y_offset, 0, COGL_FIXED_1,
x_offset + width, y_offset, 0, COGL_FIXED_1,
x_offset, y_offset + height, 0, COGL_FIXED_1,
x_offset + width, y_offset + height, 0, COGL_FIXED_1
};
GLfixed draw_points[12];
GE( cogl_wrap_glGetFixedv (GL_MODELVIEW_MATRIX, modelview) );
GE( cogl_wrap_glGetFixedv (GL_PROJECTION_MATRIX, projection) );
/* Project all of the vertices into screen coordinates */
for (i = 0; i < 4; i++)
project_vertex (modelview, projection, points + i * 4);
/* Sort the points by y coordinate */
qsort (points, 4, sizeof (GLfixed) * 4, compare_y_coordinate);
/* Put the bottom two pairs and the top two pairs in
left-right order */
if (points[0] > points[4])
{
memcpy (temp_point, points, sizeof (GLfixed) * 4);
memcpy (points, points + 4, sizeof (GLfixed) * 4);
memcpy (points + 4, temp_point, sizeof (GLfixed) * 4);
}
if (points[8] > points[12])
{
memcpy (temp_point, points + 8, sizeof (GLfixed) * 4);
memcpy (points + 8, points + 12, sizeof (GLfixed) * 4);
memcpy (points + 12, temp_point, sizeof (GLfixed) * 4);
}
/* If the clip rect goes outside of the screen then use the
extents of the rect instead */
left_edge = MIN (-COGL_FIXED_1, MIN (points[0], points[8]));
right_edge = MAX ( COGL_FIXED_1, MAX (points[4], points[12]));
bottom_edge = MIN (-COGL_FIXED_1, MIN (points[1], points[5]));
top_edge = MAX ( COGL_FIXED_1, MAX (points[9], points[13]));
/* Using the identity matrix for the projection and
modelview matrix, draw the triangles around the inner
rectangle */
GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
GE( cogl_wrap_glPushMatrix () );
GE( cogl_wrap_glLoadIdentity () );
GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
GE( cogl_wrap_glPushMatrix () );
GE( cogl_wrap_glLoadIdentity () );
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
GE( cogl_wrap_glVertexPointer (2, GL_FIXED, 0, draw_points) );
/* Clear the left side */
draw_points[0] = left_edge; draw_points[1] = bottom_edge;
draw_points[2] = points[0]; draw_points[3] = points[1];
draw_points[4] = left_edge; draw_points[5] = points[1];
draw_points[6] = points[8]; draw_points[7] = points[9];
draw_points[8] = left_edge; draw_points[9] = points[9];
draw_points[10] = left_edge; draw_points[11] = top_edge;
GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) );
/* Clear the right side */
draw_points[0] = right_edge; draw_points[1] = top_edge;
draw_points[2] = points[12]; draw_points[3] = points[13];
draw_points[4] = right_edge; draw_points[5] = points[13];
draw_points[6] = points[4]; draw_points[7] = points[5];
draw_points[8] = right_edge; draw_points[9] = points[5];
draw_points[10] = right_edge; draw_points[11] = bottom_edge;
GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) );
/* Clear the top side */
draw_points[0] = left_edge; draw_points[1] = top_edge;
draw_points[2] = points[8]; draw_points[3] = points[9];
draw_points[4] = points[8]; draw_points[5] = top_edge;
draw_points[6] = points[12]; draw_points[7] = points[13];
draw_points[8] = points[12]; draw_points[9] = top_edge;
draw_points[10] = right_edge; draw_points[11] = top_edge;
GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) );
/* Clear the bottom side */
draw_points[0] = left_edge; draw_points[1] = bottom_edge;
draw_points[2] = points[0]; draw_points[3] = points[1];
draw_points[4] = points[0]; draw_points[5] = bottom_edge;
draw_points[6] = points[4]; draw_points[7] = points[5];
draw_points[8] = points[4]; draw_points[9] = bottom_edge;
draw_points[10] = right_edge; draw_points[11] = bottom_edge;
GE( cogl_wrap_glDrawArrays (GL_TRIANGLE_STRIP, 0, 6) );
GE( cogl_wrap_glPopMatrix () );
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
GE( cogl_wrap_glPopMatrix () );
}
if (has_clip_planes)
{
GE( cogl_wrap_glEnable (GL_CLIP_PLANE0) );
GE( cogl_wrap_glEnable (GL_CLIP_PLANE1) );
GE( cogl_wrap_glEnable (GL_CLIP_PLANE2) );
GE( cogl_wrap_glEnable (GL_CLIP_PLANE3) );
}
/* Restore the stencil mode */ /* Restore the stencil mode */
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
@ -638,6 +502,15 @@ _cogl_disable_stencil_buffer (void)
GE( cogl_wrap_glDisable (GL_STENCIL_TEST) ); GE( cogl_wrap_glDisable (GL_STENCIL_TEST) );
} }
void
_cogl_enable_clip_planes (void)
{
GE( cogl_wrap_glEnable (GL_CLIP_PLANE0) );
GE( cogl_wrap_glEnable (GL_CLIP_PLANE1) );
GE( cogl_wrap_glEnable (GL_CLIP_PLANE2) );
GE( cogl_wrap_glEnable (GL_CLIP_PLANE3) );
}
void void
_cogl_disable_clip_planes (void) _cogl_disable_clip_planes (void)
{ {
@ -818,12 +691,13 @@ _cogl_features_init ()
{ {
CoglFeatureFlags flags = 0; CoglFeatureFlags flags = 0;
int max_clip_planes = 0; int max_clip_planes = 0;
GLint num_stencil_bits = 0;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
ctx->num_stencil_bits = 0; GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) );
GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) ); /* We need at least three stencil bits to combine clips */
if (ctx->num_stencil_bits > 0) if (num_stencil_bits > 2)
flags |= COGL_FEATURE_STENCIL_BUFFER; flags |= COGL_FEATURE_STENCIL_BUFFER;
GE( cogl_wrap_glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); GE( cogl_wrap_glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );