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 006506ac9..8a12e9572 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 = g_array_new (FALSE, FALSE, @@ -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 ffec47464..fd6e1b918 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; @@ -70,6 +69,7 @@ typedef struct can be flushed */ GLuint texture_current; GLenum texture_target; + GLenum texture_wrap_mode; /* Framebuffer objects */ GArray *fbo_handles; @@ -80,6 +80,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 471bf4776..8f1dc6ded 100644 --- a/gl/cogl-texture.c +++ b/gl/cogl-texture.c @@ -763,6 +763,29 @@ _cogl_texture_size_supported (GLenum gl_target, } } +static void +_cogl_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + /* Only set the wrap mode if it's different from the current + value to avoid too many GL calls */ + if (tex->wrap_mode != wrap_mode) + { + int i; + + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GLuint texnum = g_array_index (tex->slice_gl_handles, GLuint, i); + + GE( glBindTexture (tex->gl_target, texnum) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); + } + + tex->wrap_mode = wrap_mode; + } +} + static gboolean _cogl_texture_slices_create (CoglTexture *tex) { @@ -890,15 +913,10 @@ _cogl_texture_slices_create (CoglTexture *tex) n_slices); g_array_set_size (tex->slice_gl_handles, n_slices); - - - /* Hardware repeated tiling if supported, else tile in software*/ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) - && n_slices == 1) - tex->wrap_mode = GL_REPEAT; - else - tex->wrap_mode = GL_CLAMP_TO_EDGE; - + + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + /* Generate a "working set" of GL texture objects * (some implementations might supported faster * re-binding between textures inside a set) */ @@ -930,11 +948,6 @@ _cogl_texture_slices_create (CoglTexture *tex) GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, - tex->wrap_mode) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, - tex->wrap_mode) ); - if (tex->auto_mipmap) GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); @@ -1414,16 +1427,22 @@ cogl_texture_new_from_foreign (GLuint gl_handle, CoglTexture *tex; CoglTexSliceSpan x_span; CoglTexSliceSpan y_span; - - /* Allow 2-dimensional textures only */ - if (gl_target != GL_TEXTURE_2D) + + /* GL_ARB_texture_rectangle textures are supported if they are + created from foreign because some chipsets have trouble with + GL_ARB_texture_non_power_of_two. There is no Cogl call to create + them directly to emphasize the fact that they don't work fully + (for example, no mipmapping and complicated shader support) */ + + /* Allow 2-dimensional or rectangle textures only */ + if (gl_target != GL_TEXTURE_2D && gl_target != CGL_TEXTURE_RECTANGLE_ARB) return COGL_INVALID_HANDLE; - + /* Make sure it is a valid GL texture object */ gl_istexture = glIsTexture (gl_handle); if (gl_istexture == GL_FALSE) return COGL_INVALID_HANDLE; - + /* Make sure binding succeeds */ gl_error = glGetError (); glBindTexture (gl_target, gl_handle); @@ -1507,7 +1526,10 @@ cogl_texture_new_from_foreign (GLuint gl_handle, tex->min_filter = gl_min_filter; tex->mag_filter = gl_mag_filter; tex->max_waste = 0; - + + /* Wrap mode not yet set */ + tex->wrap_mode = GL_FALSE; + /* Create slice arrays */ tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, @@ -1533,24 +1555,7 @@ cogl_texture_new_from_foreign (GLuint gl_handle, g_array_append_val (tex->slice_y_spans, y_span); g_array_append_val (tex->slice_gl_handles, gl_handle); - - /* Force appropriate wrap parameter */ - if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && - gl_target == GL_TEXTURE_2D) - { - /* Hardware repeated tiling */ - tex->wrap_mode = GL_REPEAT; - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_REPEAT) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_REPEAT) ); - } - else - { - /* Any tiling will be done in software */ - tex->wrap_mode = GL_CLAMP_TO_EDGE; - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); - } - + return _cogl_texture_handle_new (tex); } @@ -1957,6 +1962,14 @@ _cogl_texture_quad_sw (CoglTexture *tex, printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); #endif + /* We can't use hardware repeat so we need to set clamp to edge + otherwise it might pull in edge pixels from the other side */ + if (ctx->texture_vertices->len > 0 + && ctx->texture_wrap_mode != GL_CLAMP_TO_EDGE) + _cogl_texture_flush_vertices (); + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_EDGE); + ctx->texture_wrap_mode = GL_CLAMP_TO_EDGE; + /* If the texture coordinates are backwards then swap both the geometry and texture coordinates so that the texture will be flipped but we can still use the same algorithm to iterate the @@ -2019,17 +2032,19 @@ _cogl_texture_quad_sw (CoglTexture *tex, slice_qy2 = first_qy + COGL_FIXED_MUL (iter_y.intersect_end - first_ty, tqy); - + /* Localize slice texture coordinates */ slice_ty1 = iter_y.intersect_start - iter_y.pos; slice_ty2 = iter_y.intersect_end - iter_y.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ - slice_ty1 /= iter_y.span->size; - slice_ty2 /= iter_y.span->size; - - + if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB) + { + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + } + /* Iterate until whole quad width covered */ for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, first_tx, tx1, tx2) ; @@ -2049,12 +2064,15 @@ _cogl_texture_quad_sw (CoglTexture *tex, /* Localize slice texture coordinates */ slice_tx1 = iter_x.intersect_start - iter_x.pos; slice_tx2 = iter_x.intersect_end - iter_x.pos; - + /* Normalize texture coordinates to current slice (rectangle texture targets take denormalized) */ - slice_tx1 /= iter_x.span->size; - slice_tx2 /= iter_x.span->size; - + if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB) + { + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + } + #if COGL_DEBUG printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); printf("qx1: %f\n", COGL_FIXED_TO_FLOAT (slice_qx1)); @@ -2128,6 +2146,7 @@ _cogl_texture_quad_hw (CoglTexture *tex, CoglTexSliceSpan *x_span; CoglTexSliceSpan *y_span; CoglTextureGLVertex *p; + GLenum wrap_mode; #if COGL_DEBUG printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); @@ -2135,15 +2154,31 @@ _cogl_texture_quad_hw (CoglTexture *tex, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* If the texture coords are all in the range [0,1] then we want to + clamp the coords to the edge otherwise it can pull in edge pixels + from the wrong side when scaled */ + if (tx1 >= 0 && tx1 <= COGL_FIXED_1 + && tx2 >= 0 && tx2 <= COGL_FIXED_1 + && ty1 >= 0 && ty1 <= COGL_FIXED_1 + && ty2 >= 0 && ty2 <= COGL_FIXED_1) + wrap_mode = GL_CLAMP_TO_EDGE; + else + wrap_mode = GL_REPEAT; + /* Pick and bind opengl texture object */ gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); /* If we're using a different texture from the one already queued then flush the vertices */ - if (ctx->texture_vertices->len > 0 && gl_handle != ctx->texture_current) + if (ctx->texture_vertices->len > 0 + && (gl_handle != ctx->texture_current + || ctx->texture_wrap_mode != wrap_mode)) _cogl_texture_flush_vertices (); ctx->texture_target = tex->gl_target; ctx->texture_current = gl_handle; + ctx->texture_wrap_mode = wrap_mode; + + _cogl_texture_set_wrap_mode_parameter (tex, wrap_mode); /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); @@ -2160,6 +2195,15 @@ _cogl_texture_quad_hw (CoglTexture *tex, p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex, ctx->texture_vertices->len - 6); + /* Denormalize texture coordinates for rectangle textures */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx1 *= x_span->size; + tx2 *= x_span->size; + ty1 *= y_span->size; + ty2 *= y_span->size; + } + #define CFX_F(x) COGL_FIXED_TO_FLOAT(x) p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y2); @@ -2191,8 +2235,7 @@ cogl_texture_multiple_rectangles (CoglHandle handle, guint n_rects) { CoglTexture *tex; - gulong enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY); _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -2201,6 +2244,8 @@ cogl_texture_multiple_rectangles (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 */ @@ -2211,6 +2256,11 @@ cogl_texture_multiple_rectangles (CoglHandle handle, return; /* Prepare GL state */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + if (ctx->color_alpha < 255 || tex->bitmap.format & COGL_A_BIT) enable_flags |= COGL_ENABLE_BLEND; @@ -2230,7 +2280,8 @@ cogl_texture_multiple_rectangles (CoglHandle handle, NPOT (no waste) or all of the coordinates are in the range [0,1] then we can use hardware tiling */ if (tex->slice_gl_handles->len == 1 - && (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && ((cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->gl_target == GL_TEXTURE_2D) || (verts[4] >= 0 && verts[4] <= COGL_FIXED_1 && verts[6] >= 0 && verts[6] <= COGL_FIXED_1 && verts[5] >= 0 && verts[5] <= COGL_FIXED_1 @@ -2292,6 +2343,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 @@ -2321,11 +2374,14 @@ cogl_texture_polygon (CoglHandle handle, p = (CoglTextureGLVertex *) ctx->texture_vertices->data; /* Prepare GL state */ - enable_flags = (COGL_ENABLE_TEXTURE_2D - | COGL_ENABLE_VERTEX_ARRAY + enable_flags = (COGL_ENABLE_VERTEX_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY | COGL_ENABLE_BLEND); + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; if (ctx->enable_backface_culling) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; @@ -2344,15 +2400,7 @@ cogl_texture_polygon (CoglHandle handle, /* Temporarily change the wrapping mode on all of the slices to use a transparent border */ - for (i = 0; i < tex->slice_gl_handles->len; i++) - { - GE( glBindTexture (tex->gl_target, - g_array_index (tex->slice_gl_handles, GLuint, i)) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, - GL_CLAMP_TO_BORDER) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, - GL_CLAMP_TO_BORDER) ); - } + _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_BORDER); tex_num = 0; @@ -2375,19 +2423,31 @@ cogl_texture_polygon (CoglHandle handle, OpenGL */ for (i = 0; i < n_vertices; i++, p++) { + CoglFixed tx, ty; + #define CFX_F COGL_FIXED_TO_FLOAT + tx = ((vertices[i].tx + - (COGL_FIXED_FROM_INT (x_span->start) + / tex->bitmap.width)) + * tex->bitmap.width / x_span->size); + ty = ((vertices[i].ty + - (COGL_FIXED_FROM_INT (y_span->start) + / tex->bitmap.height)) + * tex->bitmap.height / y_span->size); + + /* Scale the coordinates up for rectangle textures */ + if (tex->gl_target == CGL_TEXTURE_RECTANGLE_ARB) + { + tx *= x_span->size; + ty *= y_span->size; + } + p->v[0] = CFX_F(vertices[i].x); p->v[1] = CFX_F(vertices[i].y); p->v[2] = CFX_F(vertices[i].z); - p->t[0] = CFX_F((vertices[i].tx - - (COGL_FIXED_FROM_INT (x_span->start) - / tex->bitmap.width)) - * tex->bitmap.width / x_span->size); - p->t[1] = CFX_F((vertices[i].ty - - (COGL_FIXED_FROM_INT (y_span->start) - / tex->bitmap.height)) - * tex->bitmap.height / y_span->size); + p->t[0] = CFX_F(tx); + p->t[1] = CFX_F(ty); p->c[0] = cogl_color_get_red_byte(&vertices[i].color); p->c[1] = cogl_color_get_green_byte(&vertices[i].color); p->c[2] = cogl_color_get_blue_byte(&vertices[i].color); @@ -2401,13 +2461,4 @@ cogl_texture_polygon (CoglHandle handle, GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); } } - - /* Restore the wrapping mode */ - for (i = 0; i < tex->slice_gl_handles->len; i++) - { - GE( glBindTexture (tex->gl_target, - g_array_index (tex->slice_gl_handles, GLuint, i)) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, tex->wrap_mode) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, tex->wrap_mode) ); - } } diff --git a/gl/cogl.c b/gl/cogl.c index 1ce1a3c82..64bd6faf3 100644 --- a/gl/cogl.c +++ b/gl/cogl.c @@ -318,7 +318,13 @@ cogl_enable (gulong flags) cogl_toggle_flag (ctx, flags, COGL_ENABLE_BACKFACE_CULLING, GL_CULL_FACE); - + +#ifdef GL_TEXTURE_RECTANGLE_ARB + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_RECT, + GL_TEXTURE_RECTANGLE_ARB); +#endif + cogl_toggle_client_flag (ctx, flags, COGL_ENABLE_VERTEX_ARRAY, GL_VERTEX_ARRAY); @@ -457,8 +463,6 @@ set_clip_plane (GLint plane_num, GE( glClipPlane (plane_num, plane) ); GE( glPopMatrix () ); - - GE( glEnable (plane_num) ); } void @@ -513,15 +517,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 +524,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 +542,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 +567,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 +592,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 +817,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 +1087,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) );