Merge commit 'git-svn' into multiple-texture-rectangle

Conflicts:

	clutter/cogl/gl/cogl-texture.c
This commit is contained in:
Neil Roberts 2008-12-04 18:08:40 +00:00
commit bafcfca797
27 changed files with 1966 additions and 950 deletions

121
ChangeLog
View File

@ -1,3 +1,124 @@
2008-12-04 Neil Roberts <neil@linux.intel.com>
Bug 1297 - Bring back support for GL_ARB_texture_rectangle
* clutter/cogl/gl/cogl-texture.c (cogl_texture_new_from_foreign,
(_cogl_texture_quad_hw, cogl_texture_polygon),
(_cogl_texture_quad_sw): Support GL_ARB_texture_rectangle textures
* clutter/glx/clutter-glx-texture-pixmap.c: Use rectangle textures
when NPOTs are not available or it is forced by the
CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable.
* clutter/cogl/gl/cogl.c (cogl_enable): Allow enabling
GL_TEXTURE_RECTANGLE_ARB.
2008-12-04 Neil Roberts <neil@linux.intel.com>
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.
2008-12-03 Robert Bragg <robert@linux.intel.com>
Bug 1303 - clutter_glx_texture_pixmap_using_extension doesn't check if
fallbacks are being used
* glx/clutter-glx-texture-pixmap.c:
clutter_glx_texture_pixmap_using_extension now checks to see if
priv->use_fallback is TRUE not just that the tfp extension is
available.
2008-12-01 Neil Roberts <neil@linux.intel.com>
Bug 1305 - NPOT textures unaligned to a pixel sometimes have
border artifacts
* clutter/cogl/gl/cogl-texture.c: Set the wrap mode of a texture
on demand
Instead of setting the wrap mode once per texture at creation, it
is now changed whenever the texture is drawn. The previous value
is cached so that it isn't changed if the value is the same.
This is used in _cogl_texture_quad_hw to only enable GL_REPEAT
mode when the coordinates are not in the range [0,1]. Otherwise it
can pull in pixels from the other edge when the texture is
rendered off-pixel.
2008-11-28 Neil Roberts <neil@linux.intel.com>
* tests/conform/test-backface-culling.c (TEXTURE_SIZE): Don't set
to a funny size on GLES because it will break cogl_texture_polygon
2008-11-28 Neil Roberts <neil@linux.intel.com>
* tests/conform/test-backface-culling.c: New test for backface
culling
* tests/conform/test-conform-main.c (main): Add
/texture/test_backface_culing
* tests/conform/Makefile.am (test_conformance_SOURCES): Add
test-backface-culling.c
2008-11-28 Neil Roberts <neil@linux.intel.com>
* tests/conform/test-mesh-mutability.c:
* tests/conform/test-mesh-interleved.c:
* tests/conform/test-mesh-contiguous.c: Remove the idle source
after the test is complete so that it won't interfere with other
tests.
2008-11-26 Emmanuele Bassi <ebassi@linux.intel.com> 2008-11-26 Emmanuele Bassi <ebassi@linux.intel.com>
* clutter/clutter-group.c: * clutter/clutter-group.c:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
#define __COGL_CONTEXT_H #define __COGL_CONTEXT_H
#include "cogl-primitives.h" #include "cogl-primitives.h"
#include "cogl-clip-stack.h"
typedef struct typedef struct
{ {
@ -40,7 +41,6 @@ typedef struct
/* Features cache */ /* Features cache */
CoglFeatureFlags feature_flags; CoglFeatureFlags feature_flags;
gboolean features_cached; gboolean features_cached;
GLint num_stencil_bits;
/* Enable cache */ /* Enable cache */
gulong enable_flags; gulong enable_flags;
@ -53,9 +53,8 @@ typedef struct
/* Primitives */ /* Primitives */
CoglFixedVec2 path_start; CoglFixedVec2 path_start;
CoglFixedVec2 path_pen; CoglFixedVec2 path_pen;
CoglFloatVec2 *path_nodes; GArray *path_nodes;
guint path_nodes_cap; guint last_path;
guint path_nodes_size;
CoglFixedVec2 path_nodes_min; CoglFixedVec2 path_nodes_min;
CoglFixedVec2 path_nodes_max; CoglFixedVec2 path_nodes_max;
@ -70,6 +69,7 @@ typedef struct
can be flushed */ can be flushed */
GLuint texture_current; GLuint texture_current;
GLenum texture_target; GLenum texture_target;
GLenum texture_wrap_mode;
/* Framebuffer objects */ /* Framebuffer objects */
GArray *fbo_handles; GArray *fbo_handles;
@ -81,6 +81,9 @@ typedef struct
/* Programs */ /* Programs */
GArray *program_handles; GArray *program_handles;
/* Clip stack */
CoglClipStackState clip;
/* Mesh */ /* Mesh */
GArray *mesh_handles; GArray *mesh_handles;

View File

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

View File

@ -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 static gboolean
_cogl_texture_slices_create (CoglTexture *tex) _cogl_texture_slices_create (CoglTexture *tex)
{ {
@ -891,13 +914,8 @@ _cogl_texture_slices_create (CoglTexture *tex)
g_array_set_size (tex->slice_gl_handles, n_slices); g_array_set_size (tex->slice_gl_handles, n_slices);
/* Wrap mode not yet set */
/* Hardware repeated tiling if supported, else tile in software*/ tex->wrap_mode = GL_FALSE;
if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)
&& n_slices == 1)
tex->wrap_mode = GL_REPEAT;
else
tex->wrap_mode = GL_CLAMP_TO_EDGE;
/* Generate a "working set" of GL texture objects /* Generate a "working set" of GL texture objects
* (some implementations might supported faster * (some implementations might supported faster
@ -930,11 +948,6 @@ _cogl_texture_slices_create (CoglTexture *tex)
GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER,
tex->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) if (tex->auto_mipmap)
GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP,
GL_TRUE) ); GL_TRUE) );
@ -1415,8 +1428,14 @@ cogl_texture_new_from_foreign (GLuint gl_handle,
CoglTexSliceSpan x_span; CoglTexSliceSpan x_span;
CoglTexSliceSpan y_span; CoglTexSliceSpan y_span;
/* Allow 2-dimensional textures only */ /* GL_ARB_texture_rectangle textures are supported if they are
if (gl_target != GL_TEXTURE_2D) 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; return COGL_INVALID_HANDLE;
/* Make sure it is a valid GL texture object */ /* Make sure it is a valid GL texture object */
@ -1508,6 +1527,9 @@ cogl_texture_new_from_foreign (GLuint gl_handle,
tex->mag_filter = gl_mag_filter; tex->mag_filter = gl_mag_filter;
tex->max_waste = 0; tex->max_waste = 0;
/* Wrap mode not yet set */
tex->wrap_mode = GL_FALSE;
/* Create slice arrays */ /* Create slice arrays */
tex->slice_x_spans = tex->slice_x_spans =
g_array_sized_new (FALSE, FALSE, g_array_sized_new (FALSE, FALSE,
@ -1534,23 +1556,6 @@ cogl_texture_new_from_foreign (GLuint gl_handle,
g_array_append_val (tex->slice_gl_handles, gl_handle); 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); return _cogl_texture_handle_new (tex);
} }
@ -1957,6 +1962,14 @@ _cogl_texture_quad_sw (CoglTexture *tex,
printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n");
#endif #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 /* If the texture coordinates are backwards then swap both the
geometry and texture coordinates so that the texture will be geometry and texture coordinates so that the texture will be
flipped but we can still use the same algorithm to iterate the flipped but we can still use the same algorithm to iterate the
@ -2026,9 +2039,11 @@ _cogl_texture_quad_sw (CoglTexture *tex,
/* Normalize texture coordinates to current slice /* Normalize texture coordinates to current slice
(rectangle texture targets take denormalized) */ (rectangle texture targets take denormalized) */
slice_ty1 /= iter_y.span->size; if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB)
slice_ty2 /= iter_y.span->size; {
slice_ty1 /= iter_y.span->size;
slice_ty2 /= iter_y.span->size;
}
/* Iterate until whole quad width covered */ /* Iterate until whole quad width covered */
for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans,
@ -2052,8 +2067,11 @@ _cogl_texture_quad_sw (CoglTexture *tex,
/* Normalize texture coordinates to current slice /* Normalize texture coordinates to current slice
(rectangle texture targets take denormalized) */ (rectangle texture targets take denormalized) */
slice_tx1 /= iter_x.span->size; if (tex->gl_target != CGL_TEXTURE_RECTANGLE_ARB)
slice_tx2 /= iter_x.span->size; {
slice_tx1 /= iter_x.span->size;
slice_tx2 /= iter_x.span->size;
}
#if COGL_DEBUG #if COGL_DEBUG
printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index);
@ -2128,6 +2146,7 @@ _cogl_texture_quad_hw (CoglTexture *tex,
CoglTexSliceSpan *x_span; CoglTexSliceSpan *x_span;
CoglTexSliceSpan *y_span; CoglTexSliceSpan *y_span;
CoglTextureGLVertex *p; CoglTextureGLVertex *p;
GLenum wrap_mode;
#if COGL_DEBUG #if COGL_DEBUG
printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n");
@ -2135,15 +2154,31 @@ _cogl_texture_quad_hw (CoglTexture *tex,
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _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 */ /* Pick and bind opengl texture object */
gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
/* If we're using a different texture from the one already queued /* If we're using a different texture from the one already queued
then flush the vertices */ 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 (); _cogl_texture_flush_vertices ();
ctx->texture_target = tex->gl_target; ctx->texture_target = tex->gl_target;
ctx->texture_current = gl_handle; 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 */ /* Don't include the waste in the texture coordinates */
x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); 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, p = &g_array_index (ctx->texture_vertices, CoglTextureGLVertex,
ctx->texture_vertices->len - 6); 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) #define CFX_F(x) COGL_FIXED_TO_FLOAT(x)
p->v[0] = CFX_F (x1); p->v[1] = CFX_F (y2); 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) guint n_rects)
{ {
CoglTexture *tex; CoglTexture *tex;
gulong enable_flags = (COGL_ENABLE_TEXTURE_2D gulong enable_flags = (COGL_ENABLE_VERTEX_ARRAY
| COGL_ENABLE_VERTEX_ARRAY
| COGL_ENABLE_TEXCOORD_ARRAY); | COGL_ENABLE_TEXCOORD_ARRAY);
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -2201,6 +2244,8 @@ cogl_texture_multiple_rectangles (CoglHandle handle,
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* Make sure we got stuff to draw */ /* Make sure we got stuff to draw */
@ -2211,6 +2256,11 @@ cogl_texture_multiple_rectangles (CoglHandle handle,
return; return;
/* Prepare GL state */ /* 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 if (ctx->color_alpha < 255
|| tex->bitmap.format & COGL_A_BIT) || tex->bitmap.format & COGL_A_BIT)
enable_flags |= COGL_ENABLE_BLEND; 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 NPOT (no waste) or all of the coordinates are in the
range [0,1] then we can use hardware tiling */ range [0,1] then we can use hardware tiling */
if (tex->slice_gl_handles->len == 1 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[4] >= 0 && verts[4] <= COGL_FIXED_1
&& verts[6] >= 0 && verts[6] <= COGL_FIXED_1 && verts[6] >= 0 && verts[6] <= COGL_FIXED_1
&& verts[5] >= 0 && verts[5] <= 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)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* The polygon will have artifacts where the slices join if the wrap /* The polygon will have artifacts where the slices join if the wrap
@ -2321,11 +2374,14 @@ cogl_texture_polygon (CoglHandle handle,
p = (CoglTextureGLVertex *) ctx->texture_vertices->data; p = (CoglTextureGLVertex *) ctx->texture_vertices->data;
/* Prepare GL state */ /* Prepare GL state */
enable_flags = (COGL_ENABLE_TEXTURE_2D enable_flags = (COGL_ENABLE_VERTEX_ARRAY
| COGL_ENABLE_VERTEX_ARRAY
| COGL_ENABLE_TEXCOORD_ARRAY | COGL_ENABLE_TEXCOORD_ARRAY
| COGL_ENABLE_BLEND); | 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) if (ctx->enable_backface_culling)
enable_flags |= COGL_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 /* Temporarily change the wrapping mode on all of the slices to use
a transparent border */ a transparent border */
for (i = 0; i < tex->slice_gl_handles->len; i++) _cogl_texture_set_wrap_mode_parameter (tex, GL_CLAMP_TO_BORDER);
{
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) );
}
tex_num = 0; tex_num = 0;
@ -2375,19 +2423,31 @@ cogl_texture_polygon (CoglHandle handle,
OpenGL */ OpenGL */
for (i = 0; i < n_vertices; i++, p++) for (i = 0; i < n_vertices; i++, p++)
{ {
CoglFixed tx, ty;
#define CFX_F COGL_FIXED_TO_FLOAT #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[0] = CFX_F(vertices[i].x);
p->v[1] = CFX_F(vertices[i].y); p->v[1] = CFX_F(vertices[i].y);
p->v[2] = CFX_F(vertices[i].z); p->v[2] = CFX_F(vertices[i].z);
p->t[0] = CFX_F((vertices[i].tx p->t[0] = CFX_F(tx);
- (COGL_FIXED_FROM_INT (x_span->start) p->t[1] = CFX_F(ty);
/ 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->c[0] = cogl_color_get_red_byte(&vertices[i].color); p->c[0] = cogl_color_get_red_byte(&vertices[i].color);
p->c[1] = cogl_color_get_green_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); 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) ); 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) );
}
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -2325,6 +2325,8 @@ cogl_texture_rectangle (CoglHandle handle,
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* Make sure we got stuff to draw */ /* Make sure we got stuff to draw */
@ -2379,6 +2381,8 @@ cogl_texture_polygon (CoglHandle handle,
if (!cogl_is_texture (handle)) if (!cogl_is_texture (handle))
return; return;
cogl_clip_ensure ();
tex = _cogl_texture_pointer_from_handle (handle); tex = _cogl_texture_pointer_from_handle (handle);
/* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to

View File

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

View File

@ -40,6 +40,13 @@
* *
* The class requires the GLX_EXT_texture_from_pixmap OpenGL extension * The class requires the GLX_EXT_texture_from_pixmap OpenGL extension
* (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt) * (http://people.freedesktop.org/~davidr/GLX_EXT_texture_from_pixmap.txt)
*
* The GL_ARB_texture_non_power_of_two extension will be used if it is
* available. Otherwise it will try to use the
* GL_ARB_texture_rectangle extension. If both are available you can
* force it to prefer the rectangle extension by setting the
* CLUTTER_PIXMAP_TEXTURE_RECTANGLE to 'force'. To prevent it ever
* using the rectangle extension you can set it to 'disable'.
*/ */
@ -48,6 +55,8 @@
#include "config.h" #include "config.h"
#endif #endif
#include <string.h>
#include "../x11/clutter-x11-texture-pixmap.h" #include "../x11/clutter-x11-texture-pixmap.h"
#include "clutter-glx-texture-pixmap.h" #include "clutter-glx-texture-pixmap.h"
#include "clutter-glx.h" #include "clutter-glx.h"
@ -68,12 +77,20 @@ typedef void (*ReleaseTexImage) (Display *display,
typedef void (*GenerateMipmap) (GLenum target); typedef void (*GenerateMipmap) (GLenum target);
typedef enum
{
CLUTTER_GLX_RECTANGLE_DISALLOW,
CLUTTER_GLX_RECTANGLE_ALLOW,
CLUTTER_GLX_RECTANGLE_FORCE
} RectangleState;
static BindTexImage _gl_bind_tex_image = NULL; static BindTexImage _gl_bind_tex_image = NULL;
static ReleaseTexImage _gl_release_tex_image = NULL; static ReleaseTexImage _gl_release_tex_image = NULL;
static GenerateMipmap _gl_generate_mipmap = NULL; static GenerateMipmap _gl_generate_mipmap = NULL;
static gboolean _have_tex_from_pixmap_ext = FALSE; static gboolean _have_tex_from_pixmap_ext = FALSE;
static gboolean _ext_check_done = FALSE; static gboolean _ext_check_done = FALSE;
static gboolean _have_tex_rectangle = FALSE;
static RectangleState _rectangle_state = CLUTTER_GLX_RECTANGLE_ALLOW;
struct _ClutterGLXTexturePixmapPrivate struct _ClutterGLXTexturePixmapPrivate
{ {
@ -86,6 +103,8 @@ struct _ClutterGLXTexturePixmapPrivate
gboolean bound; gboolean bound;
gint can_mipmap; gint can_mipmap;
gint mipmap_generate_queued; gint mipmap_generate_queued;
gboolean using_rectangle;
}; };
static void static void
@ -170,7 +189,9 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self)
if (_ext_check_done == FALSE) if (_ext_check_done == FALSE)
{ {
const char *gl_extensions = NULL;
const gchar *glx_extensions = NULL; const gchar *glx_extensions = NULL;
const char *rect_env;
glx_extensions = glx_extensions =
glXQueryExtensionsString (clutter_x11_get_default_display (), glXQueryExtensionsString (clutter_x11_get_default_display (),
@ -191,16 +212,59 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self)
_gl_generate_mipmap = _gl_generate_mipmap =
(GenerateMipmap)cogl_get_proc_address ("glGenerateMipmapEXT"); (GenerateMipmap)cogl_get_proc_address ("glGenerateMipmapEXT");
gl_extensions = (char *) glGetString (GL_EXTENSIONS);
_have_tex_rectangle = cogl_check_extension ("GL_ARB_texture_rectangle",
gl_extensions);
if ((rect_env = g_getenv ("CLUTTER_PIXMAP_TEXTURE_RECTANGLE")))
{
if (strcasecmp (rect_env, "force") == 0)
_rectangle_state = CLUTTER_GLX_RECTANGLE_FORCE;
else if (strcasecmp (rect_env, "disable") == 0)
_rectangle_state = CLUTTER_GLX_RECTANGLE_DISALLOW;
else if (rect_env[0])
g_warning ("Unknown value for CLUTTER_PIXMAP_TEXTURE_RECTANGLE, "
"should be 'force' or 'disable'");
}
_ext_check_done = TRUE; _ext_check_done = TRUE;
} }
} }
static void
clutter_glx_texture_pixmap_free_rectangle (ClutterGLXTexturePixmap *texture)
{
ClutterGLXTexturePixmapPrivate *priv = texture->priv;
CoglHandle cogl_tex;
/* Cogl won't free the GL texture resource if it was created with
new_from_foreign so we need to free it manually */
if (priv->using_rectangle)
{
cogl_tex = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (texture));
if (cogl_tex != COGL_INVALID_HANDLE)
{
GLuint gl_handle;
GLenum gl_target;
cogl_texture_get_gl_texture (cogl_tex, &gl_handle, &gl_target);
if (gl_target == CGL_TEXTURE_RECTANGLE_ARB)
glDeleteTextures (1, &gl_handle);
}
priv->using_rectangle = FALSE;
}
}
static void static void
clutter_glx_texture_pixmap_dispose (GObject *object) clutter_glx_texture_pixmap_dispose (GObject *object)
{ {
ClutterGLXTexturePixmapPrivate *priv; ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (object);
ClutterGLXTexturePixmapPrivate *priv = texture->priv;
priv = CLUTTER_GLX_TEXTURE_PIXMAP (object)->priv; clutter_glx_texture_pixmap_free_rectangle (texture);
if (priv->glx_pixmap != None) if (priv->glx_pixmap != None)
{ {
@ -228,20 +292,70 @@ clutter_glx_texture_pixmap_notify (GObject *object, GParamSpec *pspec)
} }
} }
static gboolean
should_use_rectangle (void)
{
/* Use the rectangle only if it is available and either:
the CLUTTER_PIXMAP_TEXTURE_RECTANGLE environment variable is
set to 'force'
*or*
the env var is set to 'allow' (which is the default) and NPOTs
textures are not available */
return _have_tex_rectangle
&& ((_rectangle_state == CLUTTER_GLX_RECTANGLE_ALLOW
&& !clutter_feature_available (CLUTTER_FEATURE_TEXTURE_NPOT))
|| _rectangle_state == CLUTTER_GLX_RECTANGLE_FORCE);
}
static gboolean static gboolean
create_cogl_texture (ClutterTexture *texture, create_cogl_texture (ClutterTexture *texture,
guint width, guint width,
guint height) guint height)
{ {
ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture);
ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv;
CoglHandle handle; CoglHandle handle;
gboolean using_rectangle;
handle /* We want to use the GL_ARB_texture_rectangle extension on some
= cogl_texture_new_with_size (width, height, chipsets because GL_ARB_texture_non_power_of_two is not always
-1, FALSE, supported or might be buggy */
COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT); if (should_use_rectangle ())
{
GLuint tex = 0;
using_rectangle = TRUE;
glGenTextures (1, &tex);
glBindTexture (CGL_TEXTURE_RECTANGLE_ARB, tex);
glTexImage2D (CGL_TEXTURE_RECTANGLE_ARB, 0,
GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
handle = cogl_texture_new_from_foreign (tex, CGL_TEXTURE_RECTANGLE_ARB,
width, height,
0, 0,
COGL_PIXEL_FORMAT_RGBA_8888
| COGL_BGR_BIT);
}
else
{
handle
= cogl_texture_new_with_size (width, height,
-1, FALSE,
COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT);
using_rectangle = FALSE;
}
if (handle) if (handle)
{ {
clutter_glx_texture_pixmap_free_rectangle (texture_glx);
priv->using_rectangle = using_rectangle;
clutter_texture_set_cogl_texture (texture, handle); clutter_texture_set_cogl_texture (texture, handle);
CLUTTER_ACTOR_SET_FLAGS (texture, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_SET_FLAGS (texture, CLUTTER_ACTOR_REALIZED);
@ -301,12 +415,14 @@ clutter_glx_texture_pixmap_realize (ClutterActor *actor)
static void static void
clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) clutter_glx_texture_pixmap_unrealize (ClutterActor *actor)
{ {
ClutterGLXTexturePixmapPrivate *priv; ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor);
ClutterGLXTexturePixmapPrivate *priv = texture->priv;
Display *dpy; Display *dpy;
priv = CLUTTER_GLX_TEXTURE_PIXMAP (actor)->priv;
dpy = clutter_x11_get_default_display(); dpy = clutter_x11_get_default_display();
clutter_glx_texture_pixmap_free_rectangle (texture);
if (!_have_tex_from_pixmap_ext) if (!_have_tex_from_pixmap_ext)
{ {
CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)-> CLUTTER_ACTOR_CLASS (clutter_glx_texture_pixmap_parent_class)->
@ -524,8 +640,7 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture)
dpy = clutter_x11_get_default_display (); dpy = clutter_x11_get_default_display ();
if (priv->use_fallback == TRUE if (!clutter_glx_texture_pixmap_using_extension (texture))
|| !clutter_glx_texture_pixmap_using_extension (texture))
goto cleanup; goto cleanup;
priv->use_fallback = FALSE; priv->use_fallback = FALSE;
@ -576,7 +691,8 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture)
attribs[i++] = GLX_TEXTURE_TARGET_EXT; attribs[i++] = GLX_TEXTURE_TARGET_EXT;
attribs[i++] = GLX_TEXTURE_2D_EXT; attribs[i++] = (should_use_rectangle ()
? GLX_TEXTURE_RECTANGLE_EXT : GLX_TEXTURE_2D_EXT);
attribs[i++] = None; attribs[i++] = None;
@ -638,13 +754,12 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture,
gint width, gint width,
gint height) gint height)
{ {
ClutterGLXTexturePixmapPrivate *priv; ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture);
ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv;
Display *dpy; Display *dpy;
CLUTTER_NOTE (TEXTURE, "Updating texture pixmap"); CLUTTER_NOTE (TEXTURE, "Updating texture pixmap");
priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv;
dpy = clutter_x11_get_default_display(); dpy = clutter_x11_get_default_display();
if (!CLUTTER_ACTOR_IS_REALIZED (texture)) if (!CLUTTER_ACTOR_IS_REALIZED (texture))
@ -653,6 +768,9 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture,
if (priv->use_fallback) if (priv->use_fallback)
{ {
CLUTTER_NOTE (TEXTURE, "Falling back to X11"); CLUTTER_NOTE (TEXTURE, "Falling back to X11");
clutter_glx_texture_pixmap_free_rectangle (texture_glx);
parent_class->update_area (texture, parent_class->update_area (texture,
x, y, x, y,
width, height); width, height);
@ -744,7 +862,7 @@ clutter_glx_texture_pixmap_using_extension (ClutterGLXTexturePixmap *texture)
priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv; priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv;
return (_have_tex_from_pixmap_ext); return (_have_tex_from_pixmap_ext && !priv->use_fallback);
/* Assume NPOT TFP's are supported even if regular NPOT isn't advertised /* Assume NPOT TFP's are supported even if regular NPOT isn't advertised
* but tfp is. Seemingly some Intel drivers do this ? * but tfp is. Seemingly some Intel drivers do this ?
*/ */

View File

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

View File

@ -19,7 +19,8 @@ test_conformance_SOURCES = \
test-clutter-rectangle.c \ test-clutter-rectangle.c \
test-clutter-fixed.c \ test-clutter-fixed.c \
test-actor-invariants.c \ test-actor-invariants.c \
test-paint-opacity.c test-paint-opacity.c \
test-backface-culling.c
# For convenience, this provides a way to easily run individual unit tests: # For convenience, this provides a way to easily run individual unit tests:
.PHONY: wrappers .PHONY: wrappers

View File

@ -0,0 +1,277 @@
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <string.h>
#include "test-conform-common.h"
static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
#ifdef CLUTTER_COGL_HAS_GL
/* Size the texture so that it is just off a power of two to enourage
it so use software tiling when NPOTs aren't available */
#define TEXTURE_SIZE 33
#else /* CLUTTER_COGL_HAS_GL */
/* We can't use the funny-sized texture on GL ES because it will break
cogl_texture_polygon. However there is only one code path for
rendering quads so there is no need */
#define TEXTURE_SIZE 32
#endif /* CLUTTER_COGL_HAS_GL */
/* Amount of pixels to skip off the top, bottom, left and right of the
texture when reading back the stage */
#define TEST_INSET 4
typedef struct _TestState
{
guint frame;
CoglHandle texture;
} TestState;
static gboolean
validate_part (int xnum, int ynum, gboolean shown)
{
guchar *pixels, *p;
ClutterActor *stage = clutter_stage_get_default ();
gboolean ret = TRUE;
/* Read the appropriate part but skip out a few pixels around the
edges */
pixels = clutter_stage_read_pixels (CLUTTER_STAGE (stage),
xnum * TEXTURE_SIZE + TEST_INSET,
ynum * TEXTURE_SIZE + TEST_INSET,
TEXTURE_SIZE - TEST_INSET * 2,
TEXTURE_SIZE - TEST_INSET * 2);
/* Make sure every pixels is the appropriate color */
for (p = pixels;
p < pixels + ((TEXTURE_SIZE - TEST_INSET * 2)
* (TEXTURE_SIZE - TEST_INSET * 2));
p += 4)
{
if (p[0] != (shown ? 255 : 0))
ret = FALSE;
if (p[1] != 0)
ret = FALSE;
if (p[2] != 0)
ret = FALSE;
}
g_free (pixels);
return ret;
}
static void
validate_result (TestState *state)
{
/* Front-facing texture */
g_assert (validate_part (0, 0, TRUE));
/* Back-facing texture */
g_assert (validate_part (1, 0, FALSE));
/* Front-facing texture polygon */
g_assert (validate_part (2, 0, TRUE));
/* Back-facing texture polygon */
g_assert (validate_part (3, 0, FALSE));
/* Regular rectangle */
g_assert (validate_part (4, 0, TRUE));
/* Backface culling disabled - everything should be shown */
/* Front-facing texture */
g_assert (validate_part (0, 1, TRUE));
/* Back-facing texture */
g_assert (validate_part (1, 1, TRUE));
/* Front-facing texture polygon */
g_assert (validate_part (2, 1, TRUE));
/* Back-facing texture polygon */
g_assert (validate_part (3, 1, TRUE));
/* Regular rectangle */
g_assert (validate_part (4, 1, TRUE));
/* Comment this out if you want visual feedback of what this test
* paints.
*/
clutter_main_quit ();
}
static void
on_paint (ClutterActor *actor, TestState *state)
{
int i;
int frame_num;
cogl_enable_backface_culling (TRUE);
cogl_push_matrix ();
/* Render the scene twice - once with backface culling enabled and
once without. The second time is translated so that it is below
the first */
for (i = 0; i < 2; i++)
{
CoglFixed x1 = 0, x2, y1 = 0, y2 = COGL_FIXED_FROM_INT (TEXTURE_SIZE);
CoglTextureVertex verts[4];
memset (verts, 0, sizeof (verts));
/* Set the color to white so that all the textures will be drawn
at their own color */
cogl_set_source_color4x (COGL_FIXED_1, COGL_FIXED_1,
COGL_FIXED_1, COGL_FIXED_1);
x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE);
/* Draw a front-facing texture */
cogl_texture_rectangle (state->texture,
x1, y1, x2, y2,
0, 0, COGL_FIXED_1, COGL_FIXED_1);
x1 = x2;
x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE);
/* Draw a back-facing texture */
cogl_texture_rectangle (state->texture,
x2, y1, x1, y2,
0, 0, COGL_FIXED_1, COGL_FIXED_1);
x1 = x2;
x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE);
/* Draw a front-facing texture polygon */
verts[0].x = x1; verts[0].y = y2;
verts[1].x = x2; verts[1].y = y2;
verts[2].x = x2; verts[2].y = y1;
verts[3].x = x1; verts[3].y = y1;
verts[0].tx = 0; verts[0].ty = 0;
verts[1].tx = COGL_FIXED_1; verts[1].ty = 0;
verts[2].tx = COGL_FIXED_1; verts[2].ty = COGL_FIXED_1;
verts[3].tx = 0; verts[3].ty = COGL_FIXED_1;
cogl_texture_polygon (state->texture, 4,
verts, FALSE);
x1 = x2;
x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE);
/* Draw a back-facing texture polygon */
verts[0].x = x1; verts[0].y = y1;
verts[1].x = x2; verts[1].y = y1;
verts[2].x = x2; verts[2].y = y2;
verts[3].x = x1; verts[3].y = y2;
verts[0].tx = 0; verts[0].ty = 0;
verts[1].tx = COGL_FIXED_1; verts[1].ty = 0;
verts[2].tx = COGL_FIXED_1; verts[2].ty = COGL_FIXED_1;
verts[3].tx = 0; verts[3].ty = COGL_FIXED_1;
cogl_texture_polygon (state->texture, 4,
verts, FALSE);
x1 = x2;
x2 = x1 + COGL_FIXED_FROM_INT (TEXTURE_SIZE);
/* Draw a regular rectangle (this should always show) */
cogl_set_source_color4x (COGL_FIXED_1, 0, 0, COGL_FIXED_1);
cogl_rectangle (COGL_FIXED_TO_INT (x1), COGL_FIXED_TO_INT (y1),
COGL_FIXED_TO_INT (x2 - x1), COGL_FIXED_TO_INT (y2 - y1));
/* The second time round draw beneath the first with backface
culling disabled */
cogl_translate (0, TEXTURE_SIZE, 0);
cogl_enable_backface_culling (FALSE);
}
cogl_pop_matrix ();
/* XXX: Experiments have shown that for some buggy drivers, when using
* glReadPixels there is some kind of race, so we delay our test for a
* few frames and a few seconds:
*/
/* Need to increment frame first because clutter_stage_read_pixels
fires a redraw */
frame_num = state->frame++;
if (frame_num == 2)
validate_result (state);
else if (frame_num < 2)
g_usleep (G_USEC_PER_SEC);
}
static gboolean
queue_redraw (gpointer stage)
{
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
return TRUE;
}
static CoglHandle
make_texture (void)
{
guchar *tex_data, *p;
CoglHandle tex;
tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4);
for (p = tex_data + TEXTURE_SIZE * TEXTURE_SIZE * 4; p > tex_data;)
{
*(--p) = 255;
*(--p) = 0;
*(--p) = 0;
*(--p) = 255;
}
tex = cogl_texture_new_from_data (TEXTURE_SIZE,
TEXTURE_SIZE,
8,
FALSE,
COGL_PIXEL_FORMAT_RGBA_8888,
COGL_PIXEL_FORMAT_ANY,
TEXTURE_SIZE * 4,
tex_data);
g_free (tex_data);
return tex;
}
void
test_backface_culling (TestConformSimpleFixture *fixture,
gconstpointer data)
{
TestState state;
ClutterActor *stage;
ClutterActor *group;
guint idle_source;
state.frame = 0;
state.texture = make_texture ();
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
group = clutter_group_new ();
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
/* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that
* will trigger redrawing. */
idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
clutter_actor_show_all (stage);
clutter_main ();
g_source_remove (idle_source);
cogl_texture_unref (state.texture);
if (g_test_verbose ())
g_print ("OK\n");
}

View File

@ -103,5 +103,7 @@ main (int argc, char **argv)
TEST_CONFORM_SIMPLE ("/opacity", test_rectangle_opacity); TEST_CONFORM_SIMPLE ("/opacity", test_rectangle_opacity);
TEST_CONFORM_SIMPLE ("/opacity", test_paint_opacity); TEST_CONFORM_SIMPLE ("/opacity", test_paint_opacity);
TEST_CONFORM_SIMPLE ("/texture", test_backface_culling);
return g_test_run (); return g_test_run ();
} }

View File

@ -128,6 +128,7 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture,
ClutterActor *stage; ClutterActor *stage;
ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
ClutterActor *group; ClutterActor *group;
guint idle_source;
state.frame = 0; state.frame = 0;
@ -145,7 +146,7 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture,
/* We force continuous redrawing of the stage, since we need to skip /* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that * the first few frames, and we wont be doing anything else that
* will trigger redrawing. */ * will trigger redrawing. */
g_idle_add (queue_redraw, stage); idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
@ -186,6 +187,8 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture,
cogl_mesh_unref (state.mesh); cogl_mesh_unref (state.mesh);
g_source_remove (idle_source);
if (g_test_verbose ()) if (g_test_verbose ())
g_print ("OK\n"); g_print ("OK\n");
} }

View File

@ -99,6 +99,7 @@ test_mesh_interleved (TestConformSimpleFixture *fixture,
ClutterActor *stage; ClutterActor *stage;
ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
ClutterActor *group; ClutterActor *group;
guint idle_source;
state.frame = 0; state.frame = 0;
@ -116,7 +117,7 @@ test_mesh_interleved (TestConformSimpleFixture *fixture,
/* We force continuous redrawing of the stage, since we need to skip /* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that * the first few frames, and we wont be doing anything else that
* will trigger redrawing. */ * will trigger redrawing. */
g_idle_add (queue_redraw, stage); idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
@ -164,6 +165,8 @@ test_mesh_interleved (TestConformSimpleFixture *fixture,
cogl_mesh_unref (state.mesh); cogl_mesh_unref (state.mesh);
g_source_remove (idle_source);
if (g_test_verbose ()) if (g_test_verbose ())
g_print ("OK\n"); g_print ("OK\n");
} }

View File

@ -139,6 +139,7 @@ test_mesh_mutability (TestConformSimpleFixture *fixture,
ClutterActor *stage; ClutterActor *stage;
ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
ClutterActor *group; ClutterActor *group;
guint idle_source;
state.frame = 0; state.frame = 0;
@ -156,7 +157,7 @@ test_mesh_mutability (TestConformSimpleFixture *fixture,
/* We force continuous redrawing of the stage, since we need to skip /* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that * the first few frames, and we wont be doing anything else that
* will trigger redrawing. */ * will trigger redrawing. */
g_idle_add (queue_redraw, stage); idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
@ -197,6 +198,8 @@ test_mesh_mutability (TestConformSimpleFixture *fixture,
cogl_mesh_unref (state.mesh); cogl_mesh_unref (state.mesh);
g_source_remove (idle_source);
if (g_test_verbose ()) if (g_test_verbose ())
g_print ("OK\n"); g_print ("OK\n");
} }

View File

@ -1,128 +1,336 @@
#include <clutter/clutter.h> #include <clutter/clutter.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <gmodule.h> #include <gmodule.h>
#define TL_SCALE 5.0f
typedef struct _CallbackData CallbackData; typedef struct _CallbackData CallbackData;
typedef struct _Clip Clip;
typedef enum
{
CLIP_NONE,
CLIP_RECTANGLE,
CLIP_ELLIPSE,
CLIP_SHAPES
} ClipType;
struct _Clip
{
ClipType type;
gint x1, y1, x2, y2;
};
struct _CallbackData struct _CallbackData
{ {
ClutterActor *stage, *group, *rect, *hand; ClutterActor *stage;
CoglHandle hand;
Clip current_clip;
GSList *clips;
}; };
static const char
instructions[] =
"Press and drag any of the three mouse buttons to add a clip with different "
"shapes. Press 'r' to reset or 'u' to undo the last clip.";
static void static void
on_new_frame (ClutterTimeline *tl, int frame_num, CallbackData *data) path_shapes (gint x, gint y, gint width, gint height)
{
cogl_path_move_to (CLUTTER_INT_TO_FIXED (x), CLUTTER_INT_TO_FIXED (y));
cogl_path_line_to (CLUTTER_INT_TO_FIXED (x),
CLUTTER_INT_TO_FIXED (y + height * 4 / 5));
cogl_path_line_to (CLUTTER_INT_TO_FIXED (x + width * 4 / 15),
CLUTTER_INT_TO_FIXED (y + height * 4 / 5));
cogl_path_close ();
cogl_path_rectangle (CLUTTER_INT_TO_FIXED (x + width / 3),
CLUTTER_INT_TO_FIXED (y),
CLUTTER_INT_TO_FIXED (width * 4 / 15),
CLUTTER_INT_TO_FIXED (height * 4 / 5));
cogl_path_ellipse (CLUTTER_INT_TO_FIXED (x + width * 4 / 5),
CLUTTER_INT_TO_FIXED (y + height * 2 / 5),
CLUTTER_INT_TO_FIXED (width * 2 / 15),
CLUTTER_INT_TO_FIXED (height * 2 / 5));
}
static void
draw_shapes (gint x, gint y)
{
path_shapes (x, y, 300, 100);
cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
cogl_path_fill_preserve ();
cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
cogl_path_stroke ();
}
static void
make_clip_path (Clip *clip)
{
switch (clip->type)
{
case CLIP_NONE:
break;
case CLIP_RECTANGLE:
cogl_path_rectangle (CLUTTER_INT_TO_FIXED (clip->x1),
CLUTTER_INT_TO_FIXED (clip->y1),
CLUTTER_INT_TO_FIXED (clip->x2 - clip->x1),
CLUTTER_INT_TO_FIXED (clip->y2 - clip->y1));
break;
case CLIP_ELLIPSE:
cogl_path_ellipse (CLUTTER_INT_TO_FIXED (clip->x1 + clip->x2) / 2,
CLUTTER_INT_TO_FIXED (clip->y1 + clip->y2) / 2,
CLUTTER_INT_TO_FIXED (clip->x2 - clip->x1) / 2,
CLUTTER_INT_TO_FIXED (clip->y2 - clip->y1) / 2);
break;
case CLIP_SHAPES:
{
int x, y, width, height;
if (clip->x1 < clip->x2)
{
x = clip->x1;
width = clip->x2 - x;
}
else
{
x = clip->x2;
width = clip->x1 - x;
}
if (clip->y1 < clip->y2)
{
y = clip->y1;
height = clip->y2 - y;
}
else
{
y = clip->y2;
height = clip->y1 - y;
}
path_shapes (x, y, width, height);
}
break;
}
}
static void
on_paint (ClutterActor *actor, CallbackData *data)
{ {
int i; int i;
int stage_width = clutter_actor_get_width (data->stage); ClutterGeometry stage_size;
int stage_height = clutter_actor_get_height (data->stage); guint hand_width, hand_height;
gdouble progress = clutter_timeline_get_progress (tl); GSList *node;
gdouble angle = progress * 2 * M_PI * TL_SCALE;
gdouble rotation[3];
gdouble xpos = stage_width * 0.45 * sin (angle) + stage_width / 8; clutter_actor_get_allocation_geometry (data->stage, &stage_size);
gdouble ypos = stage_height * 0.45 * sin (angle) + stage_height / 8;
gdouble zpos = stage_width * cos (angle) - stage_width / 2;
clutter_actor_set_position (data->hand, xpos, ypos); hand_width = cogl_texture_get_width (data->hand);
clutter_actor_set_depth (data->hand, zpos); hand_height = cogl_texture_get_height (data->hand);
clutter_actor_set_rotation (data->hand, CLUTTER_Y_AXIS,
angle / M_PI * 180.0 * 3,
clutter_actor_get_width (data->hand) / 2,
clutter_actor_get_height (data->hand) / 2,
0);
memset (rotation, 0, sizeof (rotation)); /* Setup the clipping */
for (node = data->clips; node; node = node->next)
if (progress < 1 / 3.0)
rotation[2] = 360 * progress * 3;
else if (progress < 2 / 3.0)
rotation[1] = 360 * progress * 3;
else
rotation[0] = 360 * progress * 3;
for (i = 0; i < 3; i++)
{ {
clutter_actor_set_rotation (data->group, i, Clip *clip = (Clip *) node->data;
rotation[i],
clutter_actor_get_width (data->rect) / 2, if (clip->type == CLIP_RECTANGLE)
clutter_actor_get_height (data->rect) / 2, cogl_clip_set (CLUTTER_INT_TO_FIXED (clip->x1),
0); CLUTTER_INT_TO_FIXED (clip->y1),
clutter_actor_set_rotation (data->rect, i, CLUTTER_INT_TO_FIXED (clip->x2 - clip->x1),
rotation[i], CLUTTER_INT_TO_FIXED (clip->y2 - clip->y1));
clutter_actor_get_width (data->rect) / 2, else
clutter_actor_get_height (data->rect) / 2, {
0); make_clip_path (clip);
cogl_clip_set_from_path ();
}
} }
/* Draw a rectangle filling the entire stage */
cogl_set_source_color4ub (0x80, 0x80, 0xff, 0xff);
cogl_rectangle (0, 0, stage_size.width, stage_size.height);
draw_shapes (10, 10);
/* Draw the hand at different rotations */
for (i = -2; i <= 2; i++)
{
cogl_push_matrix ();
cogl_translate (stage_size.width / 2 + stage_size.width / 6 * i,
stage_size.height / 2, 0);
cogl_rotate (i * 40, 0, 1, 0);
cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
cogl_texture_rectangle (data->hand,
CLUTTER_INT_TO_FIXED (-hand_width / 2),
CLUTTER_INT_TO_FIXED (-hand_height / 2),
CLUTTER_INT_TO_FIXED (hand_width / 2),
CLUTTER_INT_TO_FIXED (hand_height / 2),
0, 0, CFX_ONE, CFX_ONE);
cogl_pop_matrix ();
}
draw_shapes (stage_size.width - 310, stage_size.height - 110);
/* Remove all of the clipping */
g_slist_foreach (data->clips, (GFunc) cogl_clip_unset, NULL);
/* Draw the bounding box for each of the clips */
for (node = data->clips; node; node = node->next)
{
Clip *clip = (Clip *) node->data;
make_clip_path (clip);
cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
cogl_path_stroke ();
}
/* Draw the bounding box for the pending new clip */
if (data->current_clip.type != CLIP_NONE)
{
make_clip_path (&data->current_clip);
cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
cogl_path_stroke ();
}
}
static gboolean
on_button_press (ClutterActor *stage, ClutterButtonEvent *event,
CallbackData *data)
{
data->current_clip.x1 = data->current_clip.x2 = event->x;
data->current_clip.y1 = data->current_clip.y2 = event->y;
data->current_clip.type
= event->button == 1 ? CLIP_RECTANGLE
: event->button == 2 ? CLIP_SHAPES
: CLIP_ELLIPSE;
clutter_actor_queue_redraw (stage);
return FALSE;
}
static gboolean
on_button_release (ClutterActor *stage, ClutterButtonEvent *event,
CallbackData *data)
{
if (data->current_clip.type != CLIP_NONE)
{
data->clips = g_slist_prepend (data->clips,
g_slice_copy (sizeof (Clip),
&data->current_clip));
data->current_clip.type = CLIP_NONE;
}
clutter_actor_queue_redraw (stage);
return FALSE;
}
static gboolean
on_motion (ClutterActor *stage, ClutterMotionEvent *event,
CallbackData *data)
{
if (data->current_clip.type != CLIP_NONE)
{
data->current_clip.x2 = event->x;
data->current_clip.y2 = event->y;
clutter_actor_queue_redraw (stage);
}
return FALSE;
}
static void
free_clips (CallbackData *data)
{
GSList *node;
for (node = data->clips; node; node = node->next)
g_slice_free (Clip, node->data);
g_slist_free (data->clips);
data->clips = NULL;
}
static gpointer
on_key_press (ClutterActor *stage, ClutterKeyEvent *event,
CallbackData *data)
{
switch (event->keyval)
{
case CLUTTER_r:
free_clips (data);
clutter_actor_queue_redraw (stage);
break;
case CLUTTER_u:
if (data->clips)
{
g_slice_free (Clip, data->clips->data);
data->clips = g_slist_delete_link (data->clips, data->clips);
clutter_actor_queue_redraw (stage);
}
break;
}
return FALSE;
} }
G_MODULE_EXPORT int G_MODULE_EXPORT int
test_clip_main (int argc, char **argv) test_clip_main (int argc, char **argv)
{ {
ClutterGeometry geom;
ClutterTimeline *tl;
ClutterColor blue = { 0x40, 0x40, 0xff, 0xff };
CallbackData data; CallbackData data;
ClutterActor *other_hand; ClutterActor *stub_actor, *label;
int x, y;
clutter_init (&argc, &argv); clutter_init (&argc, &argv);
data.current_clip.type = CLIP_NONE;
data.clips = NULL;
data.stage = clutter_stage_get_default (); data.stage = clutter_stage_get_default ();
data.group = clutter_group_new (); stub_actor = clutter_rectangle_new ();
clutter_container_add (CLUTTER_CONTAINER (data.stage), stub_actor, NULL);
clutter_actor_get_geometry (data.stage, &geom); data.hand = cogl_texture_new_from_file ("redhand.png", 64, FALSE,
geom.x = geom.width / 4; COGL_PIXEL_FORMAT_ANY, NULL);
geom.y = geom.height / 4;
geom.width /= 2;
geom.height /= 2;
clutter_actor_set_geometry (data.group, &geom);
data.rect = clutter_rectangle_new_with_color (&blue); label = clutter_label_new_with_text ("Sans 12px", instructions);
clutter_actor_set_geometry (data.rect, &geom); clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE);
clutter_container_add (CLUTTER_CONTAINER (data.stage), data.rect, NULL); clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310);
clutter_actor_set_y (label,
clutter_actor_get_height (data.stage)
- clutter_actor_get_height (label));
clutter_container_add (CLUTTER_CONTAINER (data.stage), label, NULL);
clutter_container_add (CLUTTER_CONTAINER (data.stage), data.group, NULL); g_signal_connect (stub_actor, "paint", G_CALLBACK (on_paint), &data);
clutter_actor_set_clip (data.group, 0, 0, geom.width, geom.height); g_signal_connect (data.stage, "button-press-event",
G_CALLBACK (on_button_press), &data);
data.hand = clutter_texture_new_from_file ("redhand.png", NULL); g_signal_connect (data.stage, "button-release-event",
if (data.hand == NULL) G_CALLBACK (on_button_release), &data);
{ g_signal_connect (data.stage, "motion-event",
g_critical ("pixbuf loading failed"); G_CALLBACK (on_motion), &data);
exit (1); g_signal_connect (data.stage, "key-press-event",
} G_CALLBACK (on_key_press), &data);
clutter_container_add (CLUTTER_CONTAINER (data.group), data.hand, NULL);
/* Add a hand at each of the four corners of the group */
for (y = 0; y < 2; y++)
for (x = 0; x < 2; x++)
{
other_hand = clutter_clone_texture_new (CLUTTER_TEXTURE (data.hand));
clutter_actor_set_anchor_point_from_gravity
(other_hand, CLUTTER_GRAVITY_CENTER);
clutter_actor_set_position (other_hand,
x * geom.width,
y * geom.height);
clutter_container_add (CLUTTER_CONTAINER (data.group),
other_hand, NULL);
}
clutter_actor_raise_top (data.hand);
tl = clutter_timeline_new (360 * TL_SCALE, 60);
clutter_timeline_start (tl);
clutter_timeline_set_loop (tl, TRUE);
g_signal_connect (tl, "new-frame", G_CALLBACK (on_new_frame), &data);
clutter_actor_show (data.stage); clutter_actor_show (data.stage);
clutter_main (); clutter_main ();
cogl_texture_unref (data.hand);
free_clips (&data);
return 0; return 0;
} }

View File

@ -207,7 +207,7 @@ test_coglbox_paint(ClutterActor *self)
cogl_translate (100, 100, 0); cogl_translate (100, 100, 0);
cogl_set_source_color4ub (0, 160, 0, 255); cogl_set_source_color4ub (0, 160, 0, 255);
cogl_path_stroke (); cogl_path_stroke_preserve ();
cogl_translate (150, 0, 0); cogl_translate (150, 0, 0);
cogl_set_source_color4ub (200, 0, 0, 255); cogl_set_source_color4ub (200, 0, 0, 255);