Bug 918 - Group doesn't clip if it's children are clipped

* clutter/cogl/common/cogl-clip-stack.h: 
	* clutter/cogl/common/cogl-clip-stack.c: Added functions to
	maintain a stack of clipping rectangles.

	* clutter/cogl/gles/cogl.c: 
	* clutter/cogl/gl/cogl.c: The cogl_clip_set and unset functions
	have moved into cogl-clip-stack.c which calls back to cogl.c to
	set the actual rectangles. Multiple clip rectangles are combined
	by merging the stencil buffers.

	* clutter/cogl/gles/cogl-primitives.c (_cogl_path_fill_nodes): 
	* clutter/cogl/gl/cogl-primitives.c (_cogl_path_fill_nodes): Merge
	the stencil buffer with the contents of the clipping stack after
	drawing the path.

	* clutter/cogl/gles/cogl-context.h (CoglContext): 
	* clutter/cogl/gl/cogl-context.h (CoglContext): Store the number
	of available stencil bits.

	* clutter/cogl/common/Makefile.am
	(libclutter_cogl_common_la_SOURCES): Added cogl-clip-stack.c
This commit is contained in:
Neil Roberts 2008-06-23 14:57:36 +00:00
parent 76b5a93afa
commit 7c69ca2997
9 changed files with 745 additions and 151 deletions

View File

@ -23,4 +23,5 @@ libclutter_cogl_common_la_SOURCES = \
cogl-bitmap-fallback.c \
cogl-primitives.c \
cogl-primitives.h \
cogl-bitmap-pixbuf.c
cogl-bitmap-pixbuf.c \
cogl-clip-stack.c

180
common/cogl-clip-stack.c Normal file
View File

@ -0,0 +1,180 @@
/*
* Clutter COGL
*
* A basic GL/GLES Abstraction/Utility Layer
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* Copyright (C) 2008 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-clip-stack.h"
/* These are defined in the particular backend (float in GL vs fixed
in GL ES) */
void _cogl_set_clip_planes (ClutterFixed x,
ClutterFixed y,
ClutterFixed width,
ClutterFixed height);
void _cogl_init_stencil_buffer (void);
void _cogl_add_stencil_clip (ClutterFixed x,
ClutterFixed y,
ClutterFixed width,
ClutterFixed height,
gboolean first);
void _cogl_disable_clip_planes (void);
void _cogl_disable_stencil_buffer (void);
void _cogl_set_matrix (const ClutterFixed *matrix);
typedef struct _CoglClipStackEntry CoglClipStackEntry;
struct _CoglClipStackEntry
{
/* The rectangle for this clip */
ClutterFixed x_offset;
ClutterFixed y_offset;
ClutterFixed width;
ClutterFixed height;
/* The matrix that was current when the clip was set */
ClutterFixed matrix[16];
};
static GList *cogl_clip_stack_top = NULL;
static GList *cogl_clip_stack_bottom = NULL;
static int cogl_clip_stack_depth = 0;
static void
_cogl_clip_stack_add (const CoglClipStackEntry *entry, int depth)
{
int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
/* 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));
}
void
cogl_clip_set (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height)
{
CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry);
/* Make a new entry */
entry->x_offset = x_offset;
entry->y_offset = y_offset;
entry->width = width;
entry->height = height;
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 */
cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
if (cogl_clip_stack_bottom == NULL)
cogl_clip_stack_bottom = cogl_clip_stack_top;
}
void
cogl_clip_unset (void)
{
g_return_if_fail (cogl_clip_stack_top != NULL);
/* 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);
if (cogl_clip_stack_top == NULL)
cogl_clip_stack_bottom = NULL;
cogl_clip_stack_depth--;
/* Rebuild the clip */
_cogl_clip_stack_rebuild (FALSE);
}
void
_cogl_clip_stack_rebuild (gboolean just_stencil)
{
int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
GList *node;
int depth = 1;
/* Disable clip planes if the stack is empty */
if (has_clip_planes && cogl_clip_stack_depth < 1)
_cogl_disable_clip_planes ();
/* Disable the stencil buffer if there isn't enough entries */
if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1))
_cogl_disable_stencil_buffer ();
/* Re-add every entry from the bottom of the stack up */
for (node = cogl_clip_stack_bottom; node; 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 ();
}
}
void
_cogl_clip_stack_merge (void)
{
GList *node = cogl_clip_stack_bottom;
/* Merge the current clip stack on top of whatever is in the stencil
buffer */
if (node)
{
/* 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;
}
}
}

37
common/cogl-clip-stack.h Normal file
View File

@ -0,0 +1,37 @@
/*
* Clutter COGL
*
* A basic GL/GLES Abstraction/Utility Layer
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* Copyright (C) 2008 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __COGL_CLIP_STACK_H
#define __COGL_CLIP_STACK_H
void cogl_clip_set (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height);
void cogl_clip_unset (void);
void _cogl_clip_stack_rebuild (gboolean just_stencil);
void _cogl_clip_stack_merge (void);
#endif /* __COGL_CLIP_STACK_H */

View File

@ -33,6 +33,7 @@ typedef struct
/* Features cache */
CoglFeatureFlags feature_flags;
gboolean features_cached;
GLint num_stencil_bits;
/* Enable cache */
gulong enable_flags;

View File

@ -30,6 +30,7 @@
#include "cogl.h"
#include "cogl-internal.h"
#include "cogl-context.h"
#include "cogl-clip-stack.h"
#include <string.h>
#include <gmodule.h>
@ -145,9 +146,10 @@ _cogl_path_fill_nodes ()
GE( glClear (GL_STENCIL_BUFFER_BIT) );
GE( glEnable (GL_STENCIL_TEST) );
GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) );
GE( glStencilFunc (GL_NEVER, 0x0, 0x1) );
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) );
GE( glStencilMask (1) );
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
@ -155,9 +157,13 @@ _cogl_path_fill_nodes ()
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_ZERO, GL_ZERO, GL_ZERO) );
GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x);
bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y);
@ -166,5 +172,6 @@ _cogl_path_fill_nodes ()
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
GE( glDisable (GL_STENCIL_TEST) );
/* Rebuild the stencil clip */
_cogl_clip_stack_rebuild (TRUE);
}

334
gl/cogl.c
View File

@ -32,6 +32,7 @@
#include <string.h>
#include <gmodule.h>
#include <math.h>
#include <stdlib.h>
#ifdef HAVE_CLUTTER_GLX
#include <dlfcn.h>
@ -44,7 +45,6 @@ typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName);
#include "cogl-util.h"
#include "cogl-context.h"
/* GL error to string conversion */
#if COGL_DEBUG
struct token_string
@ -447,94 +447,280 @@ set_clip_plane (GLint plane_num,
}
void
cogl_clip_set (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height)
_cogl_set_clip_planes (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height)
{
if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
GLfloat modelview[16], projection[16];
GLfloat vertex_tl[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset),
0.0f, 1.0f };
GLfloat vertex_tr[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset),
0.0f, 1.0f };
GLfloat vertex_bl[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset + height),
0.0f, 1.0f };
GLfloat vertex_br[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset + height),
0.0f, 1.0f };
GE( glGetFloatv (GL_MODELVIEW_MATRIX, modelview) );
GE( glGetFloatv (GL_PROJECTION_MATRIX, projection) );
project_vertex (modelview, projection, vertex_tl);
project_vertex (modelview, projection, vertex_tr);
project_vertex (modelview, projection, vertex_bl);
project_vertex (modelview, projection, vertex_br);
/* If the order of the top and bottom lines is different from the
order of the left and right lines then the clip rect must have
been transformed so that the back is visible. We therefore need
to swap one pair of vertices otherwise all of the planes will be
the wrong way around */
if ((vertex_tl[0] < vertex_tr[0] ? 1 : 0)
!= (vertex_bl[1] < vertex_tl[1] ? 1 : 0))
{
GLfloat modelview[16], projection[16];
GLfloat vertex_tl[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset),
0.0f, 1.0f };
GLfloat vertex_tr[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset),
0.0f, 1.0f };
GLfloat vertex_bl[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset + height),
0.0f, 1.0f };
GLfloat vertex_br[4] = { CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset + height),
0.0f, 1.0f };
GE( glGetFloatv (GL_MODELVIEW_MATRIX, modelview) );
GE( glGetFloatv (GL_PROJECTION_MATRIX, projection) );
project_vertex (modelview, projection, vertex_tl);
project_vertex (modelview, projection, vertex_tr);
project_vertex (modelview, projection, vertex_bl);
project_vertex (modelview, projection, vertex_br);
/* If the order of the top and bottom lines is different from
the order of the left and right lines then the clip rect must
have been transformed so that the back is visible. We
therefore need to swap one pair of vertices otherwise all of
the planes will be the wrong way around */
if ((vertex_tl[0] < vertex_tr[0] ? 1 : 0)
!= (vertex_bl[1] < vertex_tl[1] ? 1 : 0))
{
GLfloat temp[4];
memcpy (temp, vertex_tl, sizeof (temp));
memcpy (vertex_tl, vertex_tr, sizeof (temp));
memcpy (vertex_tr, temp, sizeof (temp));
memcpy (temp, vertex_bl, sizeof (temp));
memcpy (vertex_bl, vertex_br, sizeof (temp));
memcpy (vertex_br, temp, sizeof (temp));
}
set_clip_plane (GL_CLIP_PLANE0, vertex_tl, vertex_tr);
set_clip_plane (GL_CLIP_PLANE1, vertex_tr, vertex_br);
set_clip_plane (GL_CLIP_PLANE2, vertex_br, vertex_bl);
set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl);
GLfloat temp[4];
memcpy (temp, vertex_tl, sizeof (temp));
memcpy (vertex_tl, vertex_tr, sizeof (temp));
memcpy (vertex_tr, temp, sizeof (temp));
memcpy (temp, vertex_bl, sizeof (temp));
memcpy (vertex_bl, vertex_br, sizeof (temp));
memcpy (vertex_br, temp, sizeof (temp));
}
else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
{
GE( glEnable (GL_STENCIL_TEST) );
GE( glClearStencil (0.0f) );
GE( glClear (GL_STENCIL_BUFFER_BIT) );
set_clip_plane (GL_CLIP_PLANE0, vertex_tl, vertex_tr);
set_clip_plane (GL_CLIP_PLANE1, vertex_tr, vertex_br);
set_clip_plane (GL_CLIP_PLANE2, vertex_br, vertex_bl);
set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl);
}
GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
static int
compare_y_coordinate (const void *a, const void *b)
{
GLfloat ay = ((const GLfloat *) a)[1];
GLfloat by = ((const GLfloat *) b)[1];
GE( glColor3f (1.0f, 1.0f, 1.0f) );
GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset),
CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset + height)) );
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
}
return ay < by ? -1 : ay > by ? 1 : 0;
}
void
cogl_clip_unset (void)
_cogl_add_stencil_clip (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height,
gboolean first)
{
if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
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) );
}
else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
if (first)
{
GE( glDisable (GL_STENCIL_TEST) );
GE( glEnable (GL_STENCIL_TEST) );
/* Initially disallow everything */
GE( glClearStencil (0) );
GE( glClear (GL_STENCIL_BUFFER_BIT) );
/* Punch out a hole to allow the rectangle */
GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
GE( glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) );
GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset),
CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset + height)) );
}
else if (ctx->num_stencil_bits > 1)
{
/* Add one to every pixel of the stencil buffer in the
rectangle */
GE( glStencilFunc (GL_NEVER, 0x1, 0x3) );
GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset),
CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset + height)) );
/* Subtract one from all pixels in the stencil buffer so that
only pixels where both the original stencil buffer and the
rectangle are set will be valid */
GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
GE( glPushMatrix () );
GE( glLoadIdentity () );
GE( glMatrixMode (GL_PROJECTION) );
GE( glPushMatrix () );
GE( glLoadIdentity () );
GE( glRecti (-1, 1, 1, -1) );
GE( glPopMatrix () );
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] =
{
CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset),
0, 1,
CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_FIXED_TO_FLOAT (y_offset),
0, 1,
CLUTTER_FIXED_TO_FLOAT (x_offset),
CLUTTER_FIXED_TO_FLOAT (y_offset + height),
0, 1,
CLUTTER_FIXED_TO_FLOAT (x_offset + width),
CLUTTER_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) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
}
void
_cogl_set_matrix (const ClutterFixed *matrix)
{
float float_matrix[16];
int i;
for (i = 0; i < 16; i++)
float_matrix[i] = CLUTTER_FIXED_TO_FLOAT (matrix[i]);
GE( glLoadIdentity () );
GE( glMultMatrixf (float_matrix) );
}
void
_cogl_disable_stencil_buffer (void)
{
GE( glDisable (GL_STENCIL_TEST) );
}
void
_cogl_disable_clip_planes (void)
{
GE( glDisable (GL_CLIP_PLANE3) );
GE( glDisable (GL_CLIP_PLANE2) );
GE( glDisable (GL_CLIP_PLANE1) );
GE( glDisable (GL_CLIP_PLANE0) );
}
void
@ -660,7 +846,6 @@ _cogl_features_init ()
ClutterFeatureFlags flags = 0;
const gchar *gl_extensions;
GLint max_clip_planes = 0;
GLint stencil_bits = 0;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -819,8 +1004,9 @@ _cogl_features_init ()
flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE;
}
GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) );
if (stencil_bits > 0)
ctx->num_stencil_bits = 0;
GE( glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) );
if (ctx->num_stencil_bits > 0)
flags |= COGL_FEATURE_STENCIL_BUFFER;
GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );

View File

@ -42,6 +42,7 @@ typedef struct
/* Features cache */
CoglFeatureFlags feature_flags;
gboolean features_cached;
GLint num_stencil_bits;
/* Enable cache */
gulong enable_flags;

View File

@ -30,6 +30,7 @@
#include "cogl.h"
#include "cogl-internal.h"
#include "cogl-context.h"
#include "cogl-clip-stack.h"
#include <string.h>
#include <gmodule.h>
@ -177,9 +178,10 @@ _cogl_path_fill_nodes ()
GE( glClear (GL_STENCIL_BUFFER_BIT) );
GE( cogl_wrap_glEnable (GL_STENCIL_TEST) );
GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) );
GE( glStencilFunc (GL_NEVER, 0x0, 0x1) );
GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) );
GE( glStencilMask (1) );
cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
@ -187,13 +189,18 @@ _cogl_path_fill_nodes ()
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_ZERO, GL_ZERO, GL_ZERO) );
GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
cogl_rectangle (bounds_x, bounds_y, bounds_w, bounds_h);
GE( cogl_wrap_glDisable (GL_STENCIL_TEST) );
/* Rebuild the stencil clip */
_cogl_clip_stack_rebuild (TRUE);
}
else
{

View File

@ -30,6 +30,7 @@
#include "cogl.h"
#include <string.h>
#include <stdlib.h>
#include "cogl-internal.h"
#include "cogl-util.h"
@ -381,84 +382,257 @@ set_clip_plane (GLint plane_num,
}
void
cogl_clip_set (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height)
_cogl_set_clip_planes (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height)
{
if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
GLfixed modelview[16], projection[16];
ClutterFixed vertex_tl[4] = { x_offset, y_offset, 0, CFX_ONE };
ClutterFixed vertex_tr[4] = { x_offset + width, y_offset, 0, CFX_ONE };
ClutterFixed vertex_bl[4] = { x_offset, y_offset + height, 0, CFX_ONE };
ClutterFixed vertex_br[4] = { x_offset + width, y_offset + height,
0, CFX_ONE };
GE( cogl_wrap_glGetFixedv (GL_MODELVIEW_MATRIX, modelview) );
GE( cogl_wrap_glGetFixedv (GL_PROJECTION_MATRIX, projection) );
project_vertex (modelview, projection, vertex_tl);
project_vertex (modelview, projection, vertex_tr);
project_vertex (modelview, projection, vertex_bl);
project_vertex (modelview, projection, vertex_br);
/* If the order of the top and bottom lines is different from
the order of the left and right lines then the clip rect must
have been transformed so that the back is visible. We
therefore need to swap one pair of vertices otherwise all of
the planes will be the wrong way around */
if ((vertex_tl[0] < vertex_tr[0] ? 1 : 0)
!= (vertex_bl[1] < vertex_tl[1] ? 1 : 0))
{
GLfixed modelview[16], projection[16];
ClutterFixed vertex_tl[4] = { x_offset, y_offset, 0, CFX_ONE };
ClutterFixed vertex_tr[4] = { x_offset + width, y_offset, 0, CFX_ONE };
ClutterFixed vertex_bl[4] = { x_offset, y_offset + height, 0, CFX_ONE };
ClutterFixed vertex_br[4] = { x_offset + width, y_offset + height,
0, CFX_ONE };
GE( cogl_wrap_glGetFixedv (GL_MODELVIEW_MATRIX, modelview) );
GE( cogl_wrap_glGetFixedv (GL_PROJECTION_MATRIX, projection) );
project_vertex (modelview, projection, vertex_tl);
project_vertex (modelview, projection, vertex_tr);
project_vertex (modelview, projection, vertex_bl);
project_vertex (modelview, projection, vertex_br);
/* If the order of the top and bottom lines is different from
the order of the left and right lines then the clip rect must
have been transformed so that the back is visible. We
therefore need to swap one pair of vertices otherwise all of
the planes will be the wrong way around */
if ((vertex_tl[0] < vertex_tr[0] ? 1 : 0)
!= (vertex_bl[1] < vertex_tl[1] ? 1 : 0))
{
ClutterFixed temp[4];
memcpy (temp, vertex_tl, sizeof (temp));
memcpy (vertex_tl, vertex_tr, sizeof (temp));
memcpy (vertex_tr, temp, sizeof (temp));
memcpy (temp, vertex_bl, sizeof (temp));
memcpy (vertex_bl, vertex_br, sizeof (temp));
memcpy (vertex_br, temp, sizeof (temp));
}
set_clip_plane (GL_CLIP_PLANE0, vertex_tl, vertex_tr);
set_clip_plane (GL_CLIP_PLANE1, vertex_tr, vertex_br);
set_clip_plane (GL_CLIP_PLANE2, vertex_br, vertex_bl);
set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl);
ClutterFixed temp[4];
memcpy (temp, vertex_tl, sizeof (temp));
memcpy (vertex_tl, vertex_tr, sizeof (temp));
memcpy (vertex_tr, temp, sizeof (temp));
memcpy (temp, vertex_bl, sizeof (temp));
memcpy (vertex_bl, vertex_br, sizeof (temp));
memcpy (vertex_br, temp, sizeof (temp));
}
else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
{
GE( cogl_wrap_glEnable (GL_STENCIL_TEST) );
GE( glClearStencil (0) );
GE( glClear (GL_STENCIL_BUFFER_BIT) );
set_clip_plane (GL_CLIP_PLANE0, vertex_tl, vertex_tr);
set_clip_plane (GL_CLIP_PLANE1, vertex_tr, vertex_br);
set_clip_plane (GL_CLIP_PLANE2, vertex_br, vertex_bl);
set_clip_plane (GL_CLIP_PLANE3, vertex_bl, vertex_tl);
}
GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
static int
compare_y_coordinate (const void *a, const void *b)
{
GLfixed ay = ((const GLfixed *) a)[1];
GLfixed by = ((const GLfixed *) b)[1];
GE( cogl_wrap_glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) );
cogl_rectanglex (x_offset, y_offset, width, height);
GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
}
return ay < by ? -1 : ay > by ? 1 : 0;
}
void
cogl_clip_unset (void)
_cogl_add_stencil_clip (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height,
gboolean first)
{
if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
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) );
}
else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER))
if (first)
{
GE( cogl_wrap_glDisable (GL_STENCIL_TEST) );
GE( cogl_wrap_glEnable (GL_STENCIL_TEST) );
/* Initially disallow everything */
GE( glClearStencil (0) );
GE( glClear (GL_STENCIL_BUFFER_BIT) );
/* Punch out a hole to allow the rectangle */
GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
GE( glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) );
cogl_rectanglex (x_offset, y_offset, width, height);
}
else if (ctx->num_stencil_bits > 1)
{
/* Add one to every pixel of the stencil buffer in the
rectangle */
GE( glStencilFunc (GL_NEVER, 0x1, 0x3) );
GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
cogl_rectanglex (x_offset, y_offset, width, height);
/* Subtract one from all pixels in the stencil buffer so that
only pixels where both the original stencil buffer and the
rectangle are set will be valid */
GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
GE( cogl_wrap_glPushMatrix () );
GE( cogl_wrap_glLoadIdentity () );
GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
GE( cogl_wrap_glPushMatrix () );
GE( cogl_wrap_glLoadIdentity () );
cogl_rectanglex (-CFX_ONE, -CFX_ONE,
CLUTTER_INT_TO_FIXED (2),
CLUTTER_INT_TO_FIXED (2));
GE( cogl_wrap_glPopMatrix () );
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, CFX_ONE,
x_offset + width, y_offset, 0, CFX_ONE,
x_offset, y_offset + height, 0, CFX_ONE,
x_offset + width, y_offset + height, 0, CFX_ONE
};
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 (-CFX_ONE, MIN (points[0], points[8]));
right_edge = MAX ( CFX_ONE, MAX (points[4], points[12]));
bottom_edge = MIN (-CFX_ONE, MIN (points[1], points[5]));
top_edge = MAX ( CFX_ONE, 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) );
GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
}
void
_cogl_set_matrix (const ClutterFixed *matrix)
{
GE( cogl_wrap_glLoadIdentity () );
GE( cogl_wrap_glMultMatrixx (matrix) );
}
void
_cogl_disable_stencil_buffer (void)
{
GE( cogl_wrap_glDisable (GL_STENCIL_TEST) );
}
void
_cogl_disable_clip_planes (void)
{
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) );
}
void
@ -583,13 +757,13 @@ static void
_cogl_features_init ()
{
ClutterFeatureFlags flags = 0;
int stencil_bits = 0;
int max_clip_planes = 0;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) );
if (stencil_bits > 0)
ctx->num_stencil_bits = 0;
GE( cogl_wrap_glGetIntegerv (GL_STENCIL_BITS, &ctx->num_stencil_bits) );
if (ctx->num_stencil_bits > 0)
flags |= COGL_FEATURE_STENCIL_BUFFER;
GE( cogl_wrap_glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) );