From 10d7cf3273e8963ee465d8c71cf28d02e8252cd9 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 4 Dec 2008 13:45:09 +0000 Subject: [PATCH] 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. --- cogl-path.h | 88 +++-- cogl.h.in | 43 +- common/cogl-clip-stack.c | 356 +++++++++++------ common/cogl-clip-stack.h | 15 +- common/cogl-primitives.c | 63 ++- common/cogl-primitives.h | 22 +- doc/reference/cogl/cogl-sections.txt | 6 + gl/cogl-context.c | 13 +- gl/cogl-context.h | 10 +- gl/cogl-primitives.c | 216 ++++++---- gl/cogl-texture.c | 4 + gl/cogl.c | 161 +------- gles/cogl-context.c | 14 +- gles/cogl-context.h | 10 +- gles/cogl-primitives.c | 563 ++++++++++++++++----------- gles/cogl-texture.c | 4 + gles/cogl.c | 154 +------- 17 files changed, 984 insertions(+), 758 deletions(-) diff --git a/cogl-path.h b/cogl-path.h index 01867d144..20632da37 100644 --- a/cogl-path.h +++ b/cogl-path.h @@ -82,28 +82,60 @@ 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); +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); +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,9 +343,9 @@ 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, CoglFixed width, diff --git a/cogl.h.in b/cogl.h.in index 224a1fac4..ea81c7b2c 100644 --- a/cogl.h.in +++ b/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: * diff --git a/common/cogl-clip-stack.c b/common/cogl-clip-stack.c index 66d844bed..c533b0b4c 100644 --- a/common/cogl-clip-stack.c +++ b/common/cogl-clip-stack.c @@ -27,8 +27,11 @@ #include "config.h" #endif +#include #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,58 +39,64 @@ 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; - - /* 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]; + GList *stack_top; }; -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 _CoglClipStackEntryRect { - 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 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]; +}; + +struct _CoglClipStackEntryPath +{ + 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 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; - /* Disable clip planes if the stack is empty */ - if (has_clip_planes && cogl_clip_stack_depth < 1) - _cogl_disable_clip_planes (); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Disable the stencil buffer if there isn't enough entries */ - if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1)) - _cogl_disable_stencil_buffer (); + stack = (CoglClipStack *) ctx->clip.stacks->data; + + 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 */ - 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) - { - const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data; - cogl_push_matrix (); - _cogl_set_matrix (entry->matrix); - _cogl_clip_stack_add (entry, depth); - cogl_pop_matrix (); - } + for (; node; node = node->prev) + { + gpointer entry = node->data; + + if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_PATH) + { + CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry; + + 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 -_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 (); } diff --git a/common/cogl-clip-stack.h b/common/cogl-clip-stack.h index 04be889ae..cbabc7381 100644 --- a/common/cogl-clip-stack.h +++ b/common/cogl-clip-stack.h @@ -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 */ diff --git a/common/cogl-primitives.c b/common/cogl-primitives.c index 38499e6fc..a063331f2 100644 --- a/common/cogl-primitives.c +++ b/common/cogl-primitives.c @@ -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); + + cogl_clip_ensure (); - if (ctx->path_nodes_size == 0) - return; - - _cogl_path_fill_nodes(); + if (ctx->path_nodes->len == 0) + return; + + _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; } diff --git a/common/cogl-primitives.h b/common/cogl-primitives.h index f2d3ed173..01905d611 100644 --- a/common/cogl-primitives.h +++ b/common/cogl-primitives.h @@ -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 { diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 3ff906e90..03247ff3c 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -29,9 +29,12 @@ cogl_rotatex cogl_rotate 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 cogl_enable_depth_test cogl_enable_backface_culling @@ -63,6 +66,7 @@ cogl_util_next_p2
cogl-primitives Primitives +cogl_path_new cogl_path_move_to cogl_path_close cogl_path_line_to @@ -80,7 +84,9 @@ cogl_path_ellipse 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 diff --git a/gl/cogl-context.c b/gl/cogl-context.c index a9240a102..d75086fbc 100644 --- a/gl/cogl-context.c +++ b/gl/cogl-context.c @@ -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; @@ -122,6 +121,9 @@ cogl_create_context () GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); 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) diff --git a/gl/cogl-context.h b/gl/cogl-context.h index 5ab583862..df62a53e8 100644 --- a/gl/cogl-context.h +++ b/gl/cogl-context.h @@ -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; @@ -76,6 +75,9 @@ typedef struct /* Programs */ GArray *program_handles; + + /* Clip stack */ + CoglClipStackState clip; /* Mesh */ GArray *mesh_handles; diff --git a/gl/cogl-primitives.c b/gl/cogl-primitives.c index 950a46106..ca46d85ac 100644 --- a/gl/cogl-primitives.c +++ b/gl/cogl-primitives.c @@ -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)); - - if (new_nodes == NULL) return; - ctx->path_nodes = new_nodes; - ctx->path_nodes_cap *= 2; - } - - 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++; - - if (ctx->path_nodes_size == 1) + new_node.x = COGL_FIXED_TO_FLOAT (x); + new_node.y = COGL_FIXED_TO_FLOAT (y); + new_node.path_size = 0; + + if (new_sub_path || ctx->path_nodes->len == 0) + ctx->last_path = ctx->path_nodes->len; + + 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.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)); + + 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) ); - GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); + 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) ); - GE( glEnable (GL_STENCIL_TEST) ); - GE( glStencilFunc (GL_NEVER, 0x0, 0x1) ); - GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + _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( 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) ); + _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_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); - 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; } diff --git a/gl/cogl-texture.c b/gl/cogl-texture.c index a1c5e7d75..b1b654c58 100644 --- a/gl/cogl-texture.c +++ b/gl/cogl-texture.c @@ -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 diff --git a/gl/cogl.c b/gl/cogl.c index 1ce1a3c82..9bd14d113 100644 --- a/gl/cogl.c +++ b/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) ); diff --git a/gles/cogl-context.c b/gles/cogl-context.c index f70160659..c7c768dec 100644 --- a/gles/cogl-context.c +++ b/gles/cogl-context.c @@ -55,10 +55,9 @@ 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; _context->texture_vertices = NULL; @@ -83,6 +82,8 @@ cogl_create_context () GE( glColorMask (TRUE, TRUE, TRUE, FALSE) ); 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 diff --git a/gles/cogl-context.h b/gles/cogl-context.h index 9504fa6f4..b62903efc 100644 --- a/gles/cogl-context.h +++ b/gles/cogl-context.h @@ -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; diff --git a/gles/cogl-primitives.c b/gles/cogl-primitives.c index 8be70784d..f6248ea9e 100644 --- a/gles/cogl-primitives.c +++ b/gles/cogl-primitives.c @@ -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; - - _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; + CoglPathNode new_node; - ctx->path_nodes = new_nodes; - ctx->path_nodes_cap *= 2; - } - - ctx->path_nodes [ctx->path_nodes_size].x = x; - ctx->path_nodes [ctx->path_nodes_size].y = y; - ctx->path_nodes_size++; - - if (ctx->path_nodes_size == 1) + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + new_node.x = x; + new_node.y = y; + new_node.path_size = 0; + + if (new_sub_path || ctx->path_nodes->len == 0) + ctx->last_path = ctx->path_nodes->len; + + 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.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, @@ -158,209 +168,314 @@ static gint compare_ints (gconstpointer a, 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 _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); - - 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); 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); - - /* 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; } else { - /* 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]; + guint path_start = 0; - 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 */ + while (path_start < ctx->path_nodes->len) + { + CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, + path_start); - /* clear scanline intersection lists */ - for (i=0; i < bounds_h; i++) - scanlines[i]=NULL; + _cogl_path_fill_nodes_scanlines (path, + path->path_size, + bounds_x, bounds_y, + bounds_w, bounds_h); - 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); - - /* create scanline intersection list */ - for (i=1; ipath_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); - } + path_start += path->path_size; + } } } diff --git a/gles/cogl-texture.c b/gles/cogl-texture.c index 21d1575b3..5a75eb389 100644 --- a/gles/cogl-texture.c +++ b/gles/cogl-texture.c @@ -2324,6 +2324,8 @@ cogl_texture_rectangle (CoglHandle handle, /* Check if valid texture */ if (!cogl_is_texture (handle)) return; + + cogl_clip_ensure (); tex = _cogl_texture_pointer_from_handle (handle); @@ -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 diff --git a/gles/cogl.c b/gles/cogl.c index 45bac331a..bb4a0ea76 100644 --- a/gles/cogl.c +++ b/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) );