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>
* clutter/clutter-group.c:

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -40,6 +40,13 @@
*
* The class requires the GLX_EXT_texture_from_pixmap OpenGL extension
* (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"
#endif
#include <string.h>
#include "../x11/clutter-x11-texture-pixmap.h"
#include "clutter-glx-texture-pixmap.h"
#include "clutter-glx.h"
@ -68,12 +77,20 @@ typedef void (*ReleaseTexImage) (Display *display,
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 ReleaseTexImage _gl_release_tex_image = NULL;
static GenerateMipmap _gl_generate_mipmap = NULL;
static gboolean _have_tex_from_pixmap_ext = FALSE;
static gboolean _ext_check_done = FALSE;
static gboolean _have_tex_rectangle = FALSE;
static RectangleState _rectangle_state = CLUTTER_GLX_RECTANGLE_ALLOW;
struct _ClutterGLXTexturePixmapPrivate
{
@ -86,6 +103,8 @@ struct _ClutterGLXTexturePixmapPrivate
gboolean bound;
gint can_mipmap;
gint mipmap_generate_queued;
gboolean using_rectangle;
};
static void
@ -170,7 +189,9 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self)
if (_ext_check_done == FALSE)
{
const char *gl_extensions = NULL;
const gchar *glx_extensions = NULL;
const char *rect_env;
glx_extensions =
glXQueryExtensionsString (clutter_x11_get_default_display (),
@ -191,16 +212,59 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self)
_gl_generate_mipmap =
(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;
}
}
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
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)
{
@ -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
create_cogl_texture (ClutterTexture *texture,
guint width,
guint height)
{
ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture);
ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv;
CoglHandle handle;
gboolean using_rectangle;
handle
= cogl_texture_new_with_size (width, height,
-1, FALSE,
COGL_PIXEL_FORMAT_RGBA_8888|COGL_BGR_BIT);
/* We want to use the GL_ARB_texture_rectangle extension on some
chipsets because GL_ARB_texture_non_power_of_two is not always
supported or might be buggy */
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)
{
clutter_glx_texture_pixmap_free_rectangle (texture_glx);
priv->using_rectangle = using_rectangle;
clutter_texture_set_cogl_texture (texture, handle);
CLUTTER_ACTOR_SET_FLAGS (texture, CLUTTER_ACTOR_REALIZED);
@ -301,12 +415,14 @@ clutter_glx_texture_pixmap_realize (ClutterActor *actor)
static void
clutter_glx_texture_pixmap_unrealize (ClutterActor *actor)
{
ClutterGLXTexturePixmapPrivate *priv;
ClutterGLXTexturePixmap *texture = CLUTTER_GLX_TEXTURE_PIXMAP (actor);
ClutterGLXTexturePixmapPrivate *priv = texture->priv;
Display *dpy;
priv = CLUTTER_GLX_TEXTURE_PIXMAP (actor)->priv;
dpy = clutter_x11_get_default_display();
clutter_glx_texture_pixmap_free_rectangle (texture);
if (!_have_tex_from_pixmap_ext)
{
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 ();
if (priv->use_fallback == TRUE
|| !clutter_glx_texture_pixmap_using_extension (texture))
if (!clutter_glx_texture_pixmap_using_extension (texture))
goto cleanup;
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_2D_EXT;
attribs[i++] = (should_use_rectangle ()
? GLX_TEXTURE_RECTANGLE_EXT : GLX_TEXTURE_2D_EXT);
attribs[i++] = None;
@ -638,13 +754,12 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture,
gint width,
gint height)
{
ClutterGLXTexturePixmapPrivate *priv;
ClutterGLXTexturePixmap *texture_glx = CLUTTER_GLX_TEXTURE_PIXMAP (texture);
ClutterGLXTexturePixmapPrivate *priv = texture_glx->priv;
Display *dpy;
CLUTTER_NOTE (TEXTURE, "Updating texture pixmap");
priv = CLUTTER_GLX_TEXTURE_PIXMAP (texture)->priv;
dpy = clutter_x11_get_default_display();
if (!CLUTTER_ACTOR_IS_REALIZED (texture))
@ -653,6 +768,9 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture,
if (priv->use_fallback)
{
CLUTTER_NOTE (TEXTURE, "Falling back to X11");
clutter_glx_texture_pixmap_free_rectangle (texture_glx);
parent_class->update_area (texture,
x, y,
width, height);
@ -744,7 +862,7 @@ clutter_glx_texture_pixmap_using_extension (ClutterGLXTexturePixmap *texture)
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
* but tfp is. Seemingly some Intel drivers do this ?
*/

View File

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

View File

@ -19,7 +19,8 @@ test_conformance_SOURCES = \
test-clutter-rectangle.c \
test-clutter-fixed.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:
.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_paint_opacity);
TEST_CONFORM_SIMPLE ("/texture", test_backface_culling);
return g_test_run ();
}

View File

@ -128,6 +128,7 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture,
ClutterActor *stage;
ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
ClutterActor *group;
guint idle_source;
state.frame = 0;
@ -145,7 +146,7 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture,
/* 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. */
g_idle_add (queue_redraw, stage);
idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
@ -186,6 +187,8 @@ test_mesh_contiguous (TestConformSimpleFixture *fixture,
cogl_mesh_unref (state.mesh);
g_source_remove (idle_source);
if (g_test_verbose ())
g_print ("OK\n");
}

View File

@ -99,6 +99,7 @@ test_mesh_interleved (TestConformSimpleFixture *fixture,
ClutterActor *stage;
ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
ClutterActor *group;
guint idle_source;
state.frame = 0;
@ -116,7 +117,7 @@ test_mesh_interleved (TestConformSimpleFixture *fixture,
/* 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. */
g_idle_add (queue_redraw, stage);
idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
@ -164,6 +165,8 @@ test_mesh_interleved (TestConformSimpleFixture *fixture,
cogl_mesh_unref (state.mesh);
g_source_remove (idle_source);
if (g_test_verbose ())
g_print ("OK\n");
}

View File

@ -139,6 +139,7 @@ test_mesh_mutability (TestConformSimpleFixture *fixture,
ClutterActor *stage;
ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
ClutterActor *group;
guint idle_source;
state.frame = 0;
@ -156,7 +157,7 @@ test_mesh_mutability (TestConformSimpleFixture *fixture,
/* 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. */
g_idle_add (queue_redraw, stage);
idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
@ -197,6 +198,8 @@ test_mesh_mutability (TestConformSimpleFixture *fixture,
cogl_mesh_unref (state.mesh);
g_source_remove (idle_source);
if (g_test_verbose ())
g_print ("OK\n");
}

View File

@ -1,128 +1,336 @@
#include <clutter/clutter.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <gmodule.h>
#define TL_SCALE 5.0f
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
{
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
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 stage_width = clutter_actor_get_width (data->stage);
int stage_height = clutter_actor_get_height (data->stage);
gdouble progress = clutter_timeline_get_progress (tl);
gdouble angle = progress * 2 * M_PI * TL_SCALE;
gdouble rotation[3];
ClutterGeometry stage_size;
guint hand_width, hand_height;
GSList *node;
gdouble xpos = stage_width * 0.45 * sin (angle) + stage_width / 8;
gdouble ypos = stage_height * 0.45 * sin (angle) + stage_height / 8;
gdouble zpos = stage_width * cos (angle) - stage_width / 2;
clutter_actor_get_allocation_geometry (data->stage, &stage_size);
clutter_actor_set_position (data->hand, xpos, ypos);
clutter_actor_set_depth (data->hand, zpos);
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);
hand_width = cogl_texture_get_width (data->hand);
hand_height = cogl_texture_get_height (data->hand);
memset (rotation, 0, sizeof (rotation));
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++)
/* Setup the clipping */
for (node = data->clips; node; node = node->next)
{
clutter_actor_set_rotation (data->group, i,
rotation[i],
clutter_actor_get_width (data->rect) / 2,
clutter_actor_get_height (data->rect) / 2,
0);
clutter_actor_set_rotation (data->rect, i,
rotation[i],
clutter_actor_get_width (data->rect) / 2,
clutter_actor_get_height (data->rect) / 2,
0);
Clip *clip = (Clip *) node->data;
if (clip->type == CLIP_RECTANGLE)
cogl_clip_set (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));
else
{
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
test_clip_main (int argc, char **argv)
{
ClutterGeometry geom;
ClutterTimeline *tl;
ClutterColor blue = { 0x40, 0x40, 0xff, 0xff };
CallbackData data;
ClutterActor *other_hand;
int x, y;
ClutterActor *stub_actor, *label;
clutter_init (&argc, &argv);
data.current_clip.type = CLIP_NONE;
data.clips = NULL;
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);
geom.x = geom.width / 4;
geom.y = geom.height / 4;
geom.width /= 2;
geom.height /= 2;
clutter_actor_set_geometry (data.group, &geom);
data.hand = cogl_texture_new_from_file ("redhand.png", 64, FALSE,
COGL_PIXEL_FORMAT_ANY, NULL);
data.rect = clutter_rectangle_new_with_color (&blue);
clutter_actor_set_geometry (data.rect, &geom);
clutter_container_add (CLUTTER_CONTAINER (data.stage), data.rect, NULL);
label = clutter_label_new_with_text ("Sans 12px", instructions);
clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE);
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);
data.hand = clutter_texture_new_from_file ("redhand.png", NULL);
if (data.hand == NULL)
{
g_critical ("pixbuf loading failed");
exit (1);
}
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);
g_signal_connect (data.stage, "button-press-event",
G_CALLBACK (on_button_press), &data);
g_signal_connect (data.stage, "button-release-event",
G_CALLBACK (on_button_release), &data);
g_signal_connect (data.stage, "motion-event",
G_CALLBACK (on_motion), &data);
g_signal_connect (data.stage, "key-press-event",
G_CALLBACK (on_key_press), &data);
clutter_actor_show (data.stage);
clutter_main ();
cogl_texture_unref (data.hand);
free_clips (&data);
return 0;
}

View File

@ -204,11 +204,11 @@ test_coglbox_paint(ClutterActor *self)
cogl_push_matrix ();
paint_func[paint_index] ();
cogl_translate (100, 100, 0);
cogl_set_source_color4ub (0, 160, 0, 255);
cogl_path_stroke ();
cogl_path_stroke_preserve ();
cogl_translate (150, 0, 0);
cogl_set_source_color4ub (200, 0, 0, 255);
cogl_path_fill ();