mirror of
https://github.com/brl/mutter.git
synced 2024-11-22 16:10:41 -05:00
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:
parent
be655b05ad
commit
10d7cf3273
80
cogl-path.h
80
cogl-path.h
@ -82,27 +82,59 @@ void cogl_rectanglex (CoglFixed x,
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* Strokes the constructed shape using the current drawing color
|
||||
* and a width of 1 pixel (regardless of the current transformation
|
||||
* matrix).
|
||||
* Strokes the constructed shape using the current drawing color and a
|
||||
* width of 1 pixel (regardless of the current transformation
|
||||
* matrix). To current path is then cleared. To use the path again,
|
||||
* call cogl_path_stroke_preserve() instead.
|
||||
**/
|
||||
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:
|
||||
* @x: X 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
|
||||
* contour by moving the pen to the given coordinates.
|
||||
* Moves the pen to the given location. If there is an existing path
|
||||
* this will start a new disjoint subpath.
|
||||
**/
|
||||
void cogl_path_move_to (CoglFixed x,
|
||||
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.
|
||||
* @y: Y offset from the current pen location to move the pen to.
|
||||
*
|
||||
* Clears the previously constructed shape and begins a new path
|
||||
* contour by moving the pen to the given coordinates relative
|
||||
* to the current pen location.
|
||||
* Moves the pen to the given offset relative to the current pen
|
||||
* location. If there is an existing path this will start a new
|
||||
* disjoint subpath.
|
||||
**/
|
||||
void cogl_path_rel_move_to (CoglFixed x,
|
||||
CoglFixed y);
|
||||
@ -222,8 +254,9 @@ void cogl_path_close (void);
|
||||
* @x2: X coordinate of the end line vertex
|
||||
* @y2: Y coordinate of the end line vertex
|
||||
*
|
||||
* Clears the previously constructed shape and constructs a straight
|
||||
* line shape start and ending at the given coordinates.
|
||||
* Constructs a straight line shape starting and ending at the given
|
||||
* coordinates. If there is an existing path this will start a new
|
||||
* disjoint sub-path.
|
||||
**/
|
||||
void cogl_path_line (CoglFixed x1,
|
||||
CoglFixed y1,
|
||||
@ -236,10 +269,11 @@ void cogl_path_line (CoglFixed x1,
|
||||
* values that specify the vertex coordinates.
|
||||
* @num_points: The total number of vertices.
|
||||
*
|
||||
* Clears the previously constructed shape and constructs a series of straight
|
||||
* line segments, starting from the first given vertex coordinate. Each
|
||||
* subsequent segment stars where the previous one ended and ends at the next
|
||||
* given vertex coordinate.
|
||||
* Constructs a series of straight line segments, starting from the
|
||||
* first given vertex coordinate. If there is an existing path this
|
||||
* will start a new disjoint sub-path. Each subsequent segment starts
|
||||
* 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
|
||||
* 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.
|
||||
* @num_points: The total number of vertices.
|
||||
*
|
||||
* Clears the previously constructed shape and constructs a polygonal
|
||||
* shape of the given number of vertices.
|
||||
* Constructs a polygonal shape of the given number of vertices. If
|
||||
* 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
|
||||
* represents the X coordinate of the first vertex, the second value
|
||||
@ -276,8 +310,8 @@ void cogl_path_polygon (CoglFixed *coords,
|
||||
* @width: Rectangle width.
|
||||
* @height: Rectangle height.
|
||||
*
|
||||
* Clears the previously constructed shape and constructs a rectangular
|
||||
* shape at the given coordinates.
|
||||
* Constructs a rectangular shape at the given coordinates. If there
|
||||
* is an existing path this will start a new disjoint sub-path.
|
||||
**/
|
||||
void cogl_path_rectangle (CoglFixed x,
|
||||
CoglFixed y,
|
||||
@ -291,8 +325,8 @@ void cogl_path_rectangle (CoglFixed x,
|
||||
* @radius_x: X radius of the ellipse
|
||||
* @radius_y: Y radius of the ellipse
|
||||
*
|
||||
* Clears the previously constructed shape and constructs an ellipse
|
||||
* shape.
|
||||
* Constructs an ellipse shape. If there is an existing path this will
|
||||
* start a new disjoint sub-path.
|
||||
**/
|
||||
void cogl_path_ellipse (CoglFixed center_x,
|
||||
CoglFixed center_y,
|
||||
@ -309,8 +343,8 @@ void cogl_path_ellipse (CoglFixed center_x,
|
||||
* @arc_step: Angle increment resolution for subdivision of
|
||||
* the corner arcs.
|
||||
*
|
||||
* Clears the previously constructed shape and constructs a rectangular
|
||||
* shape with rounded corners.
|
||||
* Constructs a rectangular shape with rounded corners. If there is an
|
||||
* existing path this will start a new disjoint sub-path.
|
||||
**/
|
||||
void cogl_path_round_rectangle (CoglFixed x,
|
||||
CoglFixed y,
|
||||
|
43
cogl.h.in
43
cogl.h.in
@ -332,20 +332,59 @@ void cogl_get_viewport (CoglFixed v[4]);
|
||||
* will be clipped so that only the portion inside the rectangle will
|
||||
* be displayed. The rectangle dimensions are transformed by the
|
||||
* 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,
|
||||
CoglFixed y_offset,
|
||||
CoglFixed width,
|
||||
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:
|
||||
*
|
||||
* Removes the current clipping rectangle so that all drawing
|
||||
* operations extend to full size of the viewport again.
|
||||
* Reverts the clipping region to the state before the last call to
|
||||
* cogl_clip_set().
|
||||
*/
|
||||
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:
|
||||
*
|
||||
|
@ -27,8 +27,11 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "cogl.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
|
||||
in GL ES) */
|
||||
@ -36,25 +39,40 @@ void _cogl_set_clip_planes (CoglFixed x,
|
||||
CoglFixed y,
|
||||
CoglFixed width,
|
||||
CoglFixed height);
|
||||
void _cogl_init_stencil_buffer (void);
|
||||
void _cogl_add_stencil_clip (CoglFixed x,
|
||||
CoglFixed y,
|
||||
CoglFixed width,
|
||||
CoglFixed height,
|
||||
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_stencil_buffer (void);
|
||||
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
|
||||
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;
|
||||
GList *stack_top;
|
||||
};
|
||||
|
||||
struct _CoglClipStackEntryRect
|
||||
{
|
||||
CoglClipStackEntryType type;
|
||||
|
||||
/* The rectangle for this clip */
|
||||
CoglFixed x_offset;
|
||||
@ -66,28 +84,19 @@ struct _CoglClipStackEntry
|
||||
CoglFixed matrix[16];
|
||||
};
|
||||
|
||||
static GList *cogl_clip_stack_top = NULL;
|
||||
static int cogl_clip_stack_depth = 0;
|
||||
|
||||
static void
|
||||
_cogl_clip_stack_add (const CoglClipStackEntry *entry, int depth)
|
||||
struct _CoglClipStackEntryPath
|
||||
{
|
||||
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
|
||||
that instead */
|
||||
if (depth == 1 && has_clip_planes)
|
||||
_cogl_set_clip_planes (entry->x_offset,
|
||||
entry->y_offset,
|
||||
entry->width,
|
||||
entry->height);
|
||||
else
|
||||
_cogl_add_stencil_clip (entry->x_offset,
|
||||
entry->y_offset,
|
||||
entry->width,
|
||||
entry->height,
|
||||
depth == (has_clip_planes ? 2 : 1));
|
||||
}
|
||||
/* 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
|
||||
cogl_clip_set (CoglFixed x_offset,
|
||||
@ -95,10 +104,17 @@ cogl_clip_set (CoglFixed x_offset,
|
||||
CoglFixed width,
|
||||
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 */
|
||||
entry->clear = FALSE;
|
||||
entry->type = COGL_CLIP_STACK_RECT;
|
||||
entry->x_offset = x_offset;
|
||||
entry->y_offset = y_offset;
|
||||
entry->width = width;
|
||||
@ -106,127 +122,231 @@ cogl_clip_set (CoglFixed x_offset,
|
||||
|
||||
cogl_get_modelview_matrix (entry->matrix);
|
||||
|
||||
/* Add the entry to the current clip */
|
||||
_cogl_clip_stack_add (entry, ++cogl_clip_stack_depth);
|
||||
/* Store it in the stack */
|
||||
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 */
|
||||
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
|
||||
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 */
|
||||
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);
|
||||
cogl_clip_stack_depth--;
|
||||
if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_RECT)
|
||||
g_slice_free (CoglClipStackEntryRect, entry);
|
||||
else
|
||||
g_free (entry);
|
||||
|
||||
/* Rebuild the clip */
|
||||
_cogl_clip_stack_rebuild (FALSE);
|
||||
stack->stack_top = g_list_delete_link (stack->stack_top,
|
||||
stack->stack_top);
|
||||
|
||||
ctx->clip.stack_dirty = TRUE;
|
||||
}
|
||||
|
||||
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);
|
||||
gboolean using_clip_planes = FALSE;
|
||||
gboolean using_stencil_buffer = FALSE;
|
||||
GList *node;
|
||||
int depth = 0;
|
||||
CoglClipStack *stack;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
stack = (CoglClipStack *) ctx->clip.stacks->data;
|
||||
|
||||
ctx->clip.stack_dirty = FALSE;
|
||||
ctx->clip.stencil_used = FALSE;
|
||||
|
||||
/* Disable clip planes if the stack is empty */
|
||||
if (has_clip_planes && cogl_clip_stack_depth < 1)
|
||||
_cogl_disable_clip_planes ();
|
||||
|
||||
/* Disable the stencil buffer if there isn't enough entries */
|
||||
if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1))
|
||||
_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 */
|
||||
for (node = cogl_clip_stack_top; depth < cogl_clip_stack_depth - 1;
|
||||
node = node->next)
|
||||
depth++;
|
||||
for (node = stack->stack_top; node->next; node = node->next);
|
||||
|
||||
/* Re-add every entry from the bottom of the stack up */
|
||||
depth = 1;
|
||||
for (; depth <= cogl_clip_stack_depth; node = node->prev, depth++)
|
||||
if (!just_stencil || !has_clip_planes || depth > 1)
|
||||
for (; node; node = node->prev)
|
||||
{
|
||||
const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data;
|
||||
gpointer entry = node->data;
|
||||
|
||||
if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_PATH)
|
||||
{
|
||||
CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry;
|
||||
|
||||
cogl_push_matrix ();
|
||||
_cogl_set_matrix (entry->matrix);
|
||||
_cogl_clip_stack_add (entry, depth);
|
||||
_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
|
||||
_cogl_clip_stack_merge (void)
|
||||
cogl_clip_ensure (void)
|
||||
{
|
||||
GList *node = cogl_clip_stack_top;
|
||||
int i;
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
/* Merge the current clip stack on top of whatever is in the stencil
|
||||
buffer */
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (ctx->clip.stack_dirty)
|
||||
_cogl_clip_stack_rebuild ();
|
||||
}
|
||||
|
||||
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 */
|
||||
entry->clear = TRUE;
|
||||
cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
/* Reset the depth to zero */
|
||||
cogl_clip_stack_depth = 0;
|
||||
stack = g_slice_new (CoglClipStack);
|
||||
stack->stack_top = NULL;
|
||||
|
||||
/* Rebuilding the stack will now disabling all clipping */
|
||||
_cogl_clip_stack_rebuild (FALSE);
|
||||
ctx->clip.stacks = g_slist_prepend (ctx->clip.stacks, stack);
|
||||
|
||||
ctx->clip.stack_dirty = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
cogl_clip_stack_restore (void)
|
||||
{
|
||||
GList *node;
|
||||
CoglClipStack *stack;
|
||||
|
||||
/* The top of the stack should be a clear marker */
|
||||
g_assert (cogl_clip_stack_top);
|
||||
g_assert (((CoglClipStackEntry *) cogl_clip_stack_top->data)->clear);
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
/* Remove the top entry */
|
||||
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);
|
||||
g_return_if_fail (ctx->clip.stacks != NULL);
|
||||
|
||||
/* Recalculate the depth of the stack */
|
||||
cogl_clip_stack_depth = 0;
|
||||
for (node = cogl_clip_stack_top;
|
||||
node && !((CoglClipStackEntry *) node->data)->clear;
|
||||
node = node->next)
|
||||
cogl_clip_stack_depth++;
|
||||
stack = (CoglClipStack *) ctx->clip.stacks->data;
|
||||
|
||||
_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 ();
|
||||
}
|
||||
|
@ -26,7 +26,20 @@
|
||||
#ifndef __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);
|
||||
|
||||
#endif /* __COGL_CLIP_STACK_H */
|
||||
|
@ -37,8 +37,8 @@
|
||||
#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
|
||||
|
||||
/* these are defined in the particular backend(float in gl vs fixed in gles)*/
|
||||
void _cogl_path_clear_nodes ();
|
||||
void _cogl_path_add_node (CoglFixed x,
|
||||
void _cogl_path_add_node (gboolean new_sub_path,
|
||||
CoglFixed x,
|
||||
CoglFixed y);
|
||||
void _cogl_path_fill_nodes ();
|
||||
void _cogl_path_stroke_nodes ();
|
||||
@ -56,6 +56,8 @@ cogl_rectangle (gint x,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
cogl_clip_ensure ();
|
||||
|
||||
_cogl_rectangle (x, y, width, height);
|
||||
}
|
||||
|
||||
@ -65,27 +67,49 @@ cogl_rectanglex (CoglFixed x,
|
||||
CoglFixed width,
|
||||
CoglFixed height)
|
||||
{
|
||||
cogl_clip_ensure ();
|
||||
|
||||
_cogl_rectanglex (x, y, width, height);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cogl_path_fill (void)
|
||||
{
|
||||
cogl_path_fill_preserve ();
|
||||
|
||||
cogl_path_new ();
|
||||
}
|
||||
|
||||
void
|
||||
cogl_path_fill_preserve (void)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (ctx->path_nodes_size == 0)
|
||||
cogl_clip_ensure ();
|
||||
|
||||
if (ctx->path_nodes->len == 0)
|
||||
return;
|
||||
|
||||
_cogl_path_fill_nodes();
|
||||
_cogl_path_fill_nodes ();
|
||||
}
|
||||
|
||||
void
|
||||
cogl_path_stroke (void)
|
||||
{
|
||||
cogl_path_stroke_preserve ();
|
||||
|
||||
cogl_path_new ();
|
||||
}
|
||||
|
||||
void
|
||||
cogl_path_stroke_preserve (void)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (ctx->path_nodes_size == 0)
|
||||
cogl_clip_ensure ();
|
||||
|
||||
if (ctx->path_nodes->len == 0)
|
||||
return;
|
||||
|
||||
_cogl_path_stroke_nodes();
|
||||
@ -99,11 +123,7 @@ cogl_path_move_to (CoglFixed x,
|
||||
|
||||
/* FIXME: handle multiple contours maybe? */
|
||||
|
||||
/* at the moment, a move_to is an implicit instruction to create
|
||||
* a new path.
|
||||
*/
|
||||
_cogl_path_clear_nodes ();
|
||||
_cogl_path_add_node (x, y);
|
||||
_cogl_path_add_node (TRUE, x, y);
|
||||
|
||||
ctx->path_start.x = x;
|
||||
ctx->path_start.y = y;
|
||||
@ -127,7 +147,7 @@ cogl_path_line_to (CoglFixed x,
|
||||
{
|
||||
_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.y = y;
|
||||
@ -148,10 +168,17 @@ cogl_path_close (void)
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
void
|
||||
cogl_path_new (void)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
g_array_set_size (ctx->path_nodes, 0);
|
||||
}
|
||||
|
||||
void
|
||||
cogl_path_line (CoglFixed x1,
|
||||
@ -427,7 +454,7 @@ _cogl_path_bezier3_sub (CoglBezCubic *cubic)
|
||||
if (cindex == 0)
|
||||
return;
|
||||
|
||||
_cogl_path_add_node (c->p4.x, c->p4.y);
|
||||
_cogl_path_add_node (FALSE, c->p4.x, c->p4.y);
|
||||
|
||||
--cindex;
|
||||
|
||||
@ -492,7 +519,7 @@ cogl_path_curve_to (CoglFixed x1,
|
||||
_cogl_path_bezier3_sub (&cubic);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -559,7 +586,7 @@ _cogl_path_bezier2_sub (CoglBezQuad *quad)
|
||||
{
|
||||
/* Add subdivision point (skip last) */
|
||||
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;
|
||||
}
|
||||
|
||||
@ -607,7 +634,7 @@ cogl_path_curve2_to (CoglFixed x1,
|
||||
_cogl_path_bezier2_sub (&quad);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
typedef struct _CoglFixedVec2 CoglFixedVec2;
|
||||
typedef struct _CoglBezQuad CoglBezQuad;
|
||||
typedef struct _CoglBezCubic CoglBezCubic;
|
||||
typedef struct _CoglPathNode CoglPathNode;
|
||||
|
||||
struct _CoglFixedVec2
|
||||
{
|
||||
@ -37,13 +38,32 @@ struct _CoglFixedVec2
|
||||
};
|
||||
|
||||
#ifdef CLUTTER_COGL_HAS_GL
|
||||
|
||||
typedef struct _CoglFloatVec2 CoglFloatVec2;
|
||||
|
||||
struct _CoglFloatVec2
|
||||
{
|
||||
GLfloat x;
|
||||
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
|
||||
{
|
||||
|
@ -29,9 +29,12 @@ cogl_rotatex
|
||||
cogl_rotate
|
||||
<SUBSECTION>
|
||||
cogl_clip_set
|
||||
cogl_clip_set_from_path
|
||||
cogl_clip_set_from_path_preserve
|
||||
cogl_clip_unset
|
||||
cogl_clip_stack_save
|
||||
cogl_clip_stack_restore
|
||||
cogl_clip_ensure
|
||||
<SUBSECTION>
|
||||
cogl_enable_depth_test
|
||||
cogl_enable_backface_culling
|
||||
@ -63,6 +66,7 @@ cogl_util_next_p2
|
||||
<SECTION>
|
||||
<FILE>cogl-primitives</FILE>
|
||||
<TITLE>Primitives</TITLE>
|
||||
cogl_path_new
|
||||
cogl_path_move_to
|
||||
cogl_path_close
|
||||
cogl_path_line_to
|
||||
@ -80,7 +84,9 @@ cogl_path_ellipse
|
||||
|
||||
<SUBSECTION>
|
||||
cogl_path_fill
|
||||
cogl_path_fill_preserve
|
||||
cogl_path_stroke
|
||||
cogl_path_stroke_preserve
|
||||
cogl_set_source_color
|
||||
cogl_set_source_color4ub
|
||||
cogl_set_source_color4x
|
||||
|
@ -52,9 +52,8 @@ cogl_create_context ()
|
||||
_context->enable_flags = 0;
|
||||
_context->color_alpha = 255;
|
||||
|
||||
_context->path_nodes = NULL;
|
||||
_context->path_nodes_cap = 0;
|
||||
_context->path_nodes_size = 0;
|
||||
_context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
|
||||
_context->last_path = 0;
|
||||
|
||||
_context->texture_handles = NULL;
|
||||
_context->texture_vertices_size = 0;
|
||||
@ -123,6 +122,9 @@ cogl_create_context ()
|
||||
GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) );
|
||||
cogl_enable (0);
|
||||
|
||||
/* Initialise the clip stack */
|
||||
_cogl_clip_stack_state_init ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -132,6 +134,11 @@ cogl_destroy_context ()
|
||||
if (_context == NULL)
|
||||
return;
|
||||
|
||||
_cogl_clip_stack_state_destroy ();
|
||||
|
||||
if (_context->path_nodes)
|
||||
g_array_free (_context->path_nodes, TRUE);
|
||||
|
||||
if (_context->texture_handles)
|
||||
g_array_free (_context->texture_handles, TRUE);
|
||||
if (_context->fbo_handles)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define __COGL_CONTEXT_H
|
||||
|
||||
#include "cogl-primitives.h"
|
||||
#include "cogl-clip-stack.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -40,7 +41,6 @@ typedef struct
|
||||
/* Features cache */
|
||||
CoglFeatureFlags feature_flags;
|
||||
gboolean features_cached;
|
||||
GLint num_stencil_bits;
|
||||
|
||||
/* Enable cache */
|
||||
gulong enable_flags;
|
||||
@ -53,9 +53,8 @@ typedef struct
|
||||
/* Primitives */
|
||||
CoglFixedVec2 path_start;
|
||||
CoglFixedVec2 path_pen;
|
||||
CoglFloatVec2 *path_nodes;
|
||||
guint path_nodes_cap;
|
||||
guint path_nodes_size;
|
||||
GArray *path_nodes;
|
||||
guint last_path;
|
||||
CoglFixedVec2 path_nodes_min;
|
||||
CoglFixedVec2 path_nodes_max;
|
||||
|
||||
@ -77,6 +76,9 @@ typedef struct
|
||||
/* Programs */
|
||||
GArray *program_handles;
|
||||
|
||||
/* Clip stack */
|
||||
CoglClipStackState clip;
|
||||
|
||||
/* Mesh */
|
||||
GArray *mesh_handles;
|
||||
|
||||
|
@ -70,43 +70,26 @@ _cogl_rectanglex (CoglFixed x,
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_path_clear_nodes ()
|
||||
{
|
||||
_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,
|
||||
_cogl_path_add_node (gboolean new_sub_path,
|
||||
CoglFixed x,
|
||||
CoglFixed y)
|
||||
{
|
||||
CoglFloatVec2 *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 (CoglFloatVec2));
|
||||
new_node.x = COGL_FIXED_TO_FLOAT (x);
|
||||
new_node.y = COGL_FIXED_TO_FLOAT (y);
|
||||
new_node.path_size = 0;
|
||||
|
||||
if (new_nodes == NULL) return;
|
||||
if (new_sub_path || ctx->path_nodes->len == 0)
|
||||
ctx->last_path = ctx->path_nodes->len;
|
||||
|
||||
ctx->path_nodes = new_nodes;
|
||||
ctx->path_nodes_cap *= 2;
|
||||
}
|
||||
g_array_append_val (ctx->path_nodes, new_node);
|
||||
|
||||
ctx->path_nodes [ctx->path_nodes_size] .x = COGL_FIXED_TO_FLOAT (x);
|
||||
ctx->path_nodes [ctx->path_nodes_size] .y = COGL_FIXED_TO_FLOAT (y);
|
||||
ctx->path_nodes_size++;
|
||||
g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++;
|
||||
|
||||
if (ctx->path_nodes_size == 1)
|
||||
if (ctx->path_nodes->len == 1)
|
||||
{
|
||||
ctx->path_nodes_min.x = ctx->path_nodes_max.x = x;
|
||||
ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
|
||||
@ -123,55 +106,158 @@ _cogl_path_add_node (CoglFixed x,
|
||||
void
|
||||
_cogl_path_stroke_nodes ()
|
||||
{
|
||||
guint path_start = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
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_LINE_STRIP, 0, ctx->path_nodes_size) );
|
||||
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) );
|
||||
|
||||
while (path_start < path_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
|
||||
_cogl_path_fill_nodes ()
|
||||
{
|
||||
guint bounds_x;
|
||||
guint bounds_y;
|
||||
gint bounds_x;
|
||||
gint bounds_y;
|
||||
guint bounds_w;
|
||||
guint bounds_h;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
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( 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( 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_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max,
|
||||
&bounds_x, &bounds_y, &bounds_w, &bounds_h);
|
||||
|
||||
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
|
||||
|
||||
/* Rebuild the stencil clip */
|
||||
_cogl_clip_stack_rebuild (TRUE);
|
||||
/* The stencil buffer now contains garbage so the clip area needs to
|
||||
be rebuilt */
|
||||
ctx->clip.stack_dirty = TRUE;
|
||||
}
|
||||
|
@ -2197,6 +2197,8 @@ cogl_texture_rectangle (CoglHandle handle,
|
||||
if (!cogl_is_texture (handle))
|
||||
return;
|
||||
|
||||
cogl_clip_ensure ();
|
||||
|
||||
tex = _cogl_texture_pointer_from_handle (handle);
|
||||
|
||||
/* Make sure we got stuff to draw */
|
||||
@ -2242,6 +2244,8 @@ cogl_texture_polygon (CoglHandle handle,
|
||||
if (!cogl_is_texture (handle))
|
||||
return;
|
||||
|
||||
cogl_clip_ensure ();
|
||||
|
||||
tex = _cogl_texture_pointer_from_handle (handle);
|
||||
|
||||
/* The polygon will have artifacts where the slices join if the wrap
|
||||
|
161
gl/cogl.c
161
gl/cogl.c
@ -457,8 +457,6 @@ set_clip_plane (GLint plane_num,
|
||||
GE( glClipPlane (plane_num, plane) );
|
||||
|
||||
GE( glPopMatrix () );
|
||||
|
||||
GE( glEnable (plane_num) );
|
||||
}
|
||||
|
||||
void
|
||||
@ -513,15 +511,6 @@ _cogl_set_clip_planes (CoglFixed x_offset,
|
||||
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
|
||||
_cogl_add_stencil_clip (CoglFixed x_offset,
|
||||
CoglFixed y_offset,
|
||||
@ -529,19 +518,8 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
|
||||
CoglFixed height,
|
||||
gboolean first)
|
||||
{
|
||||
gboolean has_clip_planes
|
||||
= cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
|
||||
|
||||
_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)
|
||||
{
|
||||
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 (y_offset + height)) );
|
||||
}
|
||||
else if (ctx->num_stencil_bits > 1)
|
||||
else
|
||||
{
|
||||
/* Add one to every pixel of the stencil buffer in the
|
||||
rectangle */
|
||||
@ -583,127 +561,6 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
|
||||
GE( glMatrixMode (GL_MODELVIEW) );
|
||||
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 */
|
||||
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
|
||||
@ -729,6 +586,15 @@ _cogl_disable_stencil_buffer (void)
|
||||
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
|
||||
_cogl_disable_clip_planes (void)
|
||||
{
|
||||
@ -945,6 +811,7 @@ _cogl_features_init ()
|
||||
CoglFeatureFlags flags = 0;
|
||||
const gchar *gl_extensions;
|
||||
GLint max_clip_planes = 0;
|
||||
GLint num_stencil_bits = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
@ -1214,9 +1081,9 @@ _cogl_features_init ()
|
||||
flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE;
|
||||
}
|
||||
|
||||
ctx->num_stencil_bits = 0;
|
||||
GE( glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) );
|
||||
if (ctx->num_stencil_bits > 0)
|
||||
GE( glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) );
|
||||
/* We need at least three stencil bits to combine clips */
|
||||
if (num_stencil_bits > 2)
|
||||
flags |= COGL_FEATURE_STENCIL_BUFFER;
|
||||
|
||||
GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );
|
||||
|
@ -55,9 +55,8 @@ cogl_create_context ()
|
||||
_context->enable_flags = 0;
|
||||
_context->color_alpha = 255;
|
||||
|
||||
_context->path_nodes = NULL;
|
||||
_context->path_nodes_cap = 0;
|
||||
_context->path_nodes_size = 0;
|
||||
_context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
|
||||
_context->last_path = 0;
|
||||
|
||||
_context->texture_handles = NULL;
|
||||
_context->texture_vertices_size = 0;
|
||||
@ -84,6 +83,8 @@ cogl_create_context ()
|
||||
GE( glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) );
|
||||
cogl_enable (0);
|
||||
|
||||
_cogl_clip_stack_state_init ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -93,6 +94,11 @@ cogl_destroy_context ()
|
||||
if (_context == NULL)
|
||||
return;
|
||||
|
||||
_cogl_clip_stack_state_destroy ();
|
||||
|
||||
if (_context->path_nodes)
|
||||
g_array_free (_context->path_nodes, TRUE);
|
||||
|
||||
#ifdef HAVE_COGL_GLES2
|
||||
cogl_gles2_wrapper_deinit (&_context->gles2);
|
||||
#endif
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define __COGL_CONTEXT_H
|
||||
|
||||
#include "cogl-primitives.h"
|
||||
#include "cogl-clip-stack.h"
|
||||
|
||||
#include "cogl-gles2-wrapper.h"
|
||||
|
||||
@ -42,7 +43,6 @@ typedef struct
|
||||
/* Features cache */
|
||||
CoglFeatureFlags feature_flags;
|
||||
gboolean features_cached;
|
||||
GLint num_stencil_bits;
|
||||
|
||||
/* Enable cache */
|
||||
gulong enable_flags;
|
||||
@ -55,9 +55,8 @@ typedef struct
|
||||
/* Primitives */
|
||||
CoglFixedVec2 path_start;
|
||||
CoglFixedVec2 path_pen;
|
||||
CoglFixedVec2 *path_nodes;
|
||||
guint path_nodes_cap;
|
||||
guint path_nodes_size;
|
||||
GArray *path_nodes;
|
||||
guint last_path;
|
||||
CoglFixedVec2 path_nodes_min;
|
||||
CoglFixedVec2 path_nodes_max;
|
||||
|
||||
@ -80,6 +79,9 @@ typedef struct
|
||||
/* Mesh */
|
||||
GArray *mesh_handles;
|
||||
|
||||
/* Clip stack */
|
||||
CoglClipStackState clip;
|
||||
|
||||
#ifdef HAVE_COGL_GLES2
|
||||
CoglGles2Wrapper gles2;
|
||||
|
||||
|
@ -87,45 +87,27 @@ _cogl_rectanglex (CoglFixed x,
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cogl_path_clear_nodes ()
|
||||
{
|
||||
_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,
|
||||
_cogl_path_add_node (gboolean new_sub_path,
|
||||
CoglFixed x,
|
||||
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));
|
||||
new_node.x = x;
|
||||
new_node.y = y;
|
||||
new_node.path_size = 0;
|
||||
|
||||
if (new_nodes == NULL) return;
|
||||
if (new_sub_path || ctx->path_nodes->len == 0)
|
||||
ctx->last_path = ctx->path_nodes->len;
|
||||
|
||||
ctx->path_nodes = new_nodes;
|
||||
ctx->path_nodes_cap *= 2;
|
||||
}
|
||||
g_array_append_val (ctx->path_nodes, new_node);
|
||||
|
||||
ctx->path_nodes [ctx->path_nodes_size].x = x;
|
||||
ctx->path_nodes [ctx->path_nodes_size].y = y;
|
||||
ctx->path_nodes_size++;
|
||||
g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++;
|
||||
|
||||
if (ctx->path_nodes_size == 1)
|
||||
if (ctx->path_nodes->len == 1)
|
||||
{
|
||||
ctx->path_nodes_min.x = ctx->path_nodes_max.x = x;
|
||||
ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
|
||||
@ -142,14 +124,42 @@ _cogl_path_add_node (CoglFixed x,
|
||||
void
|
||||
_cogl_path_stroke_nodes ()
|
||||
{
|
||||
guint path_start = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
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_LINE_STRIP, 0, ctx->path_nodes_size) );
|
||||
while (path_start < ctx->path_nodes->len)
|
||||
{
|
||||
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,
|
||||
@ -159,51 +169,108 @@ static gint compare_ints (gconstpointer a,
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_path_fill_nodes ()
|
||||
_cogl_add_path_to_stencil_buffer (CoglFixedVec2 nodes_min,
|
||||
CoglFixedVec2 nodes_max,
|
||||
guint path_size,
|
||||
CoglPathNode *path,
|
||||
gboolean merge)
|
||||
{
|
||||
guint bounds_x;
|
||||
guint bounds_y;
|
||||
guint path_start = 0;
|
||||
guint sub_path_num = 0;
|
||||
gint bounds_x;
|
||||
gint bounds_y;
|
||||
guint bounds_w;
|
||||
guint bounds_h;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
_cogl_path_get_bounds (nodes_min, nodes_max,
|
||||
&bounds_x, &bounds_y, &bounds_w, &bounds_h);
|
||||
|
||||
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);
|
||||
|
||||
if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
|
||||
if (merge)
|
||||
{
|
||||
GE( glClear (GL_STENCIL_BUFFER_BIT) );
|
||||
|
||||
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);
|
||||
|
||||
/* Rebuild the stencil clip */
|
||||
_cogl_clip_stack_rebuild (TRUE);
|
||||
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
|
||||
@ -219,18 +286,20 @@ _cogl_path_fill_nodes ()
|
||||
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 (ctx->path_nodes[0].x);
|
||||
first_y = prev_y = COGL_FIXED_TO_INT (ctx->path_nodes[0].y);
|
||||
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<ctx->path_nodes_size; i++)
|
||||
for (i=1; i < path_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 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;
|
||||
@ -281,7 +350,7 @@ _cogl_path_fill_nodes ()
|
||||
|
||||
/* if we're on the last knot, fake the first vertex being a
|
||||
next one */
|
||||
if (ctx->path_nodes_size == i+1)
|
||||
if (path_size == i+1)
|
||||
{
|
||||
dest_x = first_x;
|
||||
dest_y = first_y;
|
||||
@ -361,6 +430,52 @@ _cogl_path_fill_nodes ()
|
||||
GE ( cogl_wrap_glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3));
|
||||
g_free (coords);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_path_fill_nodes ()
|
||||
{
|
||||
gint bounds_x;
|
||||
gint bounds_y;
|
||||
guint bounds_w;
|
||||
guint bounds_h;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
_cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max,
|
||||
&bounds_x, &bounds_y, &bounds_w, &bounds_h);
|
||||
|
||||
if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
|
||||
{
|
||||
_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);
|
||||
|
||||
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
|
||||
|
||||
/* The stencil buffer now contains garbage so the clip area needs to
|
||||
be rebuilt */
|
||||
ctx->clip.stack_dirty = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
guint path_start = 0;
|
||||
|
||||
while (path_start < ctx->path_nodes->len)
|
||||
{
|
||||
CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode,
|
||||
path_start);
|
||||
|
||||
_cogl_path_fill_nodes_scanlines (path,
|
||||
path->path_size,
|
||||
bounds_x, bounds_y,
|
||||
bounds_w, bounds_h);
|
||||
|
||||
path_start += path->path_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2325,6 +2325,8 @@ cogl_texture_rectangle (CoglHandle handle,
|
||||
if (!cogl_is_texture (handle))
|
||||
return;
|
||||
|
||||
cogl_clip_ensure ();
|
||||
|
||||
tex = _cogl_texture_pointer_from_handle (handle);
|
||||
|
||||
/* Make sure we got stuff to draw */
|
||||
@ -2379,6 +2381,8 @@ cogl_texture_polygon (CoglHandle handle,
|
||||
if (!cogl_is_texture (handle))
|
||||
return;
|
||||
|
||||
cogl_clip_ensure ();
|
||||
|
||||
tex = _cogl_texture_pointer_from_handle (handle);
|
||||
|
||||
/* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to
|
||||
|
154
gles/cogl.c
154
gles/cogl.c
@ -389,8 +389,6 @@ set_clip_plane (GLint plane_num,
|
||||
GE( cogl_wrap_glClipPlanex (plane_num, plane) );
|
||||
|
||||
GE( cogl_wrap_glPopMatrix () );
|
||||
|
||||
GE( cogl_wrap_glEnable (plane_num) );
|
||||
}
|
||||
|
||||
void
|
||||
@ -438,15 +436,6 @@ _cogl_set_clip_planes (CoglFixed x_offset,
|
||||
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
|
||||
_cogl_add_stencil_clip (CoglFixed x_offset,
|
||||
CoglFixed y_offset,
|
||||
@ -454,19 +443,8 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
|
||||
CoglFixed height,
|
||||
gboolean first)
|
||||
{
|
||||
gboolean has_clip_planes
|
||||
= cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
|
||||
|
||||
_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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if (ctx->num_stencil_bits > 1)
|
||||
else
|
||||
{
|
||||
/* Add one to every pixel of the stencil buffer in the
|
||||
rectangle */
|
||||
@ -505,120 +483,6 @@ _cogl_add_stencil_clip (CoglFixed x_offset,
|
||||
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
|
||||
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 */
|
||||
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
|
||||
@ -638,6 +502,15 @@ _cogl_disable_stencil_buffer (void)
|
||||
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
|
||||
_cogl_disable_clip_planes (void)
|
||||
{
|
||||
@ -818,12 +691,13 @@ _cogl_features_init ()
|
||||
{
|
||||
CoglFeatureFlags flags = 0;
|
||||
int max_clip_planes = 0;
|
||||
GLint num_stencil_bits = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
ctx->num_stencil_bits = 0;
|
||||
GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) );
|
||||
if (ctx->num_stencil_bits > 0)
|
||||
GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) );
|
||||
/* We need at least three stencil bits to combine clips */
|
||||
if (num_stencil_bits > 2)
|
||||
flags |= COGL_FEATURE_STENCIL_BUFFER;
|
||||
|
||||
GE( cogl_wrap_glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );
|
||||
|
Loading…
Reference in New Issue
Block a user