Replace cogl_path_{stroke,fill} with framebuffer API

The existing functions for stroking and filling a path depend on the
global framebuffer and source stacks. These are now replaced with
cogl_framebuffer_{stroke,fill}_path which get explicitly passed the
framebuffer and pipeline.

Reviewed-by: Robert Bragg <robert@linux.intel.com>

(cherry picked from commit 713a8f8160bc5884b091c69eb7a84b069e0950e6)
This commit is contained in:
Neil Roberts 2012-04-18 15:57:33 +01:00 committed by Robert Bragg
parent 54735dec84
commit 8dd77de009
8 changed files with 199 additions and 83 deletions

View File

@ -437,6 +437,7 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl,
case COGL_PANGO_DISPLAY_LIST_TRAPEZOID: case COGL_PANGO_DISPLAY_LIST_TRAPEZOID:
{ {
CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
float points[8]; float points[8];
CoglPath *path; CoglPath *path;
@ -453,7 +454,7 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl,
path = cogl_path_new (); path = cogl_path_new ();
cogl_path_polygon (path, points, 4); cogl_path_polygon (path, points, 4);
cogl_path_fill (path); cogl_framebuffer_fill_path (framebuffer, node->pipeline, path);
cogl_object_unref (path); cogl_object_unref (path);
} }
break; break;

View File

@ -278,7 +278,9 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
} }
typedef void (*SilhouettePaintCallback) (void *user_data); typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
void *user_data);
static void static void
add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
@ -346,7 +348,7 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT)); GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT));
silhouette_callback (user_data); silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data);
if (merge) if (merge)
{ {
@ -382,11 +384,15 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
} }
static void static void
paint_path_silhouette (void *user_data) paint_path_silhouette (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
void *user_data)
{ {
CoglPath *path = user_data; CoglPath *path = user_data;
if (path->data->path_nodes->len >= 3) if (path->data->path_nodes->len >= 3)
_cogl_path_fill_nodes (path, _cogl_path_fill_nodes (path,
framebuffer,
pipeline,
COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION | COGL_DRAW_SKIP_PIPELINE_VALIDATION |
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH); COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
@ -411,10 +417,12 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer,
} }
static void static void
paint_primitive_silhouette (void *user_data) paint_primitive_silhouette (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
void *user_data)
{ {
_cogl_framebuffer_draw_primitive (cogl_get_draw_framebuffer (), _cogl_framebuffer_draw_primitive (framebuffer,
cogl_get_source (), pipeline,
user_data, user_data,
COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION | COGL_DRAW_SKIP_PIPELINE_VALIDATION |

View File

@ -47,6 +47,7 @@
#include "cogl1-context.h" #include "cogl1-context.h"
#include "cogl-private.h" #include "cogl-private.h"
#include "cogl-primitives-private.h" #include "cogl-primitives-private.h"
#include "cogl-path-private.h"
#ifndef GL_FRAMEBUFFER #ifndef GL_FRAMEBUFFER
#define GL_FRAMEBUFFER 0x8D40 #define GL_FRAMEBUFFER 0x8D40
@ -3522,3 +3523,27 @@ cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer,
n_rectangles, n_rectangles,
TRUE); TRUE);
} }
void
cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path)
{
_COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
_COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
_COGL_RETURN_IF_FAIL (cogl_is_path (path));
_cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */);
}
void
cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path)
{
_COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
_COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
_COGL_RETURN_IF_FAIL (cogl_is_path (path));
_cogl_path_stroke_nodes (path, framebuffer, pipeline);
}

View File

@ -1491,6 +1491,47 @@ cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer,
const float *coordinates, const float *coordinates,
unsigned int n_rectangles); unsigned int n_rectangles);
/**
* cogl_framebuffer_fill_path:
* @framebuffer: A #CoglFramebuffer
* @pipeline: A #CoglPipeline to render with
* @path: The #CoglPath to fill
*
* Fills the interior of the path using the fragment operations
* defined by the pipeline.
*
* The interior of the shape is determined using the fill rule of the
* path. See %CoglPathFillRule for details.
*
* <note>The result of referencing sliced textures in your current
* pipeline when filling a path are undefined. You should pass
* the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will
* use while filling a path.</note>
*
* Since: 2.0
*/
void
cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path);
/**
* cogl_framebuffer_stroke_path:
* @framebuffer: A #CoglFramebuffer
* @pipeline: A #CoglPipeline to render with
* @path: The #CoglPath to stroke
*
* Strokes the edge of the path using the fragment operations defined
* by the pipeline. The stroke line will have a width of 1 pixel
* regardless of the current transformation matrix.
*
* Since: 2.0
*/
void
cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path);
/* XXX: Should we take an n_buffers + buffer id array instead of using /* XXX: Should we take an n_buffers + buffer id array instead of using
* the CoglBufferBits type which doesn't seem future proof? */ * the CoglBufferBits type which doesn't seem future proof? */
/** /**

View File

@ -116,6 +116,14 @@ CoglBool
_cogl_path_is_rectangle (CoglPath *path); _cogl_path_is_rectangle (CoglPath *path);
void void
_cogl_path_fill_nodes (CoglPath *path, CoglDrawFlags flags); _cogl_path_stroke_nodes (CoglPath *path,
CoglFramebuffer *framebuffer,
CoglPipeline *pipeline);
void
_cogl_path_fill_nodes (CoglPath *path,
CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglDrawFlags flags);
#endif /* __COGL_PATH_PRIVATE_H */ #endif /* __COGL_PATH_PRIVATE_H */

View File

@ -202,37 +202,37 @@ _cogl_path_add_node (CoglPath *path,
data->is_rectangle = FALSE; data->is_rectangle = FALSE;
} }
static void void
_cogl_path_stroke_nodes (CoglPath *path) _cogl_path_stroke_nodes (CoglPath *path,
CoglFramebuffer *framebuffer,
CoglPipeline *pipeline)
{ {
CoglPathData *data = path->data; CoglPathData *data = path->data;
CoglPipeline *copy = NULL; CoglPipeline *copy = NULL;
CoglPipeline *source;
unsigned int path_start; unsigned int path_start;
int path_num = 0; int path_num = 0;
CoglPathNode *node; CoglPathNode *node;
source = cogl_get_source (); if (data->path_nodes->len == 0)
return;
if (cogl_pipeline_get_n_layers (source) != 0) if (cogl_pipeline_get_n_layers (pipeline) != 0)
{ {
copy = cogl_pipeline_copy (source); copy = cogl_pipeline_copy (pipeline);
_cogl_pipeline_prune_to_n_layers (copy, 0); _cogl_pipeline_prune_to_n_layers (copy, 0);
source = copy; pipeline = copy;
} }
_cogl_path_build_stroke_attribute_buffer (path); _cogl_path_build_stroke_attribute_buffer (path);
cogl_push_source (source);
for (path_start = 0; for (path_start = 0;
path_start < data->path_nodes->len; path_start < data->path_nodes->len;
path_start += node->path_size) path_start += node->path_size)
{ {
node = &g_array_index (data->path_nodes, CoglPathNode, path_start); node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
cogl_framebuffer_vdraw_attributes (cogl_get_draw_framebuffer (), cogl_framebuffer_vdraw_attributes (framebuffer,
source, pipeline,
COGL_VERTICES_MODE_LINE_STRIP, COGL_VERTICES_MODE_LINE_STRIP,
0, node->path_size, 0, node->path_size,
data->stroke_attributes[path_num], data->stroke_attributes[path_num],
@ -241,8 +241,6 @@ _cogl_path_stroke_nodes (CoglPath *path)
path_num++; path_num++;
} }
cogl_pop_source ();
if (copy) if (copy)
cogl_object_unref (copy); cogl_object_unref (copy);
} }
@ -273,10 +271,10 @@ _cogl_path_get_bounds (CoglPath *path,
} }
static void static void
_cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path) _cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path,
CoglFramebuffer *framebuffer,
CoglPipeline *pipeline)
{ {
CoglFramebuffer *fb;
if (!(path->data->context->private_feature_flags & if (!(path->data->context->private_feature_flags &
COGL_PRIVATE_FEATURE_STENCIL_BUFFER)) COGL_PRIVATE_FEATURE_STENCIL_BUFFER))
{ {
@ -291,13 +289,14 @@ _cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path)
} }
} }
fb = cogl_get_draw_framebuffer (); cogl_framebuffer_push_path_clip (framebuffer, path);
cogl_framebuffer_push_path_clip (fb, path); cogl_framebuffer_draw_rectangle (framebuffer,
cogl_rectangle (path->data->path_nodes_min.x, pipeline,
path->data->path_nodes_min.x,
path->data->path_nodes_min.y, path->data->path_nodes_min.y,
path->data->path_nodes_max.x, path->data->path_nodes_max.x,
path->data->path_nodes_max.y); path->data->path_nodes_max.y);
cogl_framebuffer_pop_clip (fb); cogl_framebuffer_pop_clip (framebuffer);
} }
static CoglBool static CoglBool
@ -321,22 +320,46 @@ validate_layer_cb (CoglPipelineLayer *layer, void *user_data)
} }
void void
_cogl_path_fill_nodes (CoglPath *path, CoglDrawFlags flags) _cogl_path_fill_nodes (CoglPath *path,
CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglDrawFlags flags)
{ {
gboolean needs_fallback = FALSE; if (path->data->path_nodes->len == 0)
CoglPipeline *pipeline = cogl_get_source (); return;
/* If the path is a simple rectangle then we can divert to using
cogl_framebuffer_draw_rectangle which should be faster because it
can go through the journal instead of uploading the geometry just
for two triangles */
if (path->data->is_rectangle && flags == 0)
{
float x_1, y_1, x_2, y_2;
_cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
cogl_framebuffer_draw_rectangle (framebuffer,
pipeline,
x_1, y_1,
x_2, y_2);
}
else
{
CoglBool needs_fallback = FALSE;
_cogl_pipeline_foreach_layer_internal (pipeline, _cogl_pipeline_foreach_layer_internal (pipeline,
validate_layer_cb, &needs_fallback); validate_layer_cb,
&needs_fallback);
if (needs_fallback) if (needs_fallback)
{ {
_cogl_path_fill_nodes_with_clipped_rectangle (path); _cogl_path_fill_nodes_with_clipped_rectangle (path,
framebuffer,
pipeline);
return; return;
} }
_cogl_path_build_fill_attribute_buffer (path); _cogl_path_build_fill_attribute_buffer (path);
_cogl_framebuffer_draw_indexed_attributes (cogl_get_draw_framebuffer (), _cogl_framebuffer_draw_indexed_attributes (framebuffer,
pipeline, pipeline,
COGL_VERTICES_MODE_TRIANGLES, COGL_VERTICES_MODE_TRIANGLES,
0, /* first_vertex */ 0, /* first_vertex */
@ -345,6 +368,7 @@ _cogl_path_fill_nodes (CoglPath *path, CoglDrawFlags flags)
path->data->fill_attributes, path->data->fill_attributes,
COGL_PATH_N_ATTRIBUTES, COGL_PATH_N_ATTRIBUTES,
flags); flags);
}
} }
void void
@ -367,7 +391,10 @@ cogl2_path_fill (CoglPath *path)
cogl_rectangle (x_1, y_1, x_2, y_2); cogl_rectangle (x_1, y_1, x_2, y_2);
} }
else else
_cogl_path_fill_nodes (path, 0); _cogl_path_fill_nodes (path,
cogl_get_draw_framebuffer (),
cogl_get_source (),
0);
} }
void void
@ -378,7 +405,9 @@ cogl2_path_stroke (CoglPath *path)
if (path->data->path_nodes->len == 0) if (path->data->path_nodes->len == 0)
return; return;
_cogl_path_stroke_nodes (path); _cogl_path_stroke_nodes (path,
cogl_get_draw_framebuffer (),
cogl_get_source ());
} }
void void

View File

@ -286,8 +286,6 @@ cogl_path_ellipse
CoglPathFillRule CoglPathFillRule
cogl_path_set_fill_rule cogl_path_set_fill_rule
cogl_path_get_fill_rule cogl_path_get_fill_rule
cogl_path_fill
cogl_path_stroke
</SECTION> </SECTION>
<SECTION> <SECTION>
@ -425,6 +423,8 @@ cogl_framebuffer_draw_textured_rectangle
cogl_framebuffer_draw_multitextured_rectangle cogl_framebuffer_draw_multitextured_rectangle
cogl_framebuffer_draw_rectangles cogl_framebuffer_draw_rectangles
cogl_framebuffer_draw_textured_rectangles cogl_framebuffer_draw_textured_rectangles
cogl_framebuffer_stroke_path
cogl_framebuffer_fill_path
<SUBSECTION> <SUBSECTION>
cogl_framebuffer_swap_buffers cogl_framebuffer_swap_buffers

View File

@ -15,11 +15,13 @@ typedef struct _TestState
} TestState; } TestState;
static void static void
draw_path_at (int x, int y) draw_path_at (CoglPath *path, CoglPipeline *pipeline, int x, int y)
{ {
cogl_framebuffer_push_matrix (fb); cogl_framebuffer_push_matrix (fb);
cogl_framebuffer_translate (fb, x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f); cogl_framebuffer_translate (fb, x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f);
cogl_path_fill ();
cogl_framebuffer_fill_path (fb, pipeline, path);
cogl_framebuffer_pop_matrix (fb); cogl_framebuffer_pop_matrix (fb);
} }
@ -64,9 +66,10 @@ check_block (int block_x, int block_y, int block_mask)
static void static void
paint (TestState *state) paint (TestState *state)
{ {
CoglHandle path_a, path_b, path_c; CoglPath *path_a, *path_b, *path_c;
CoglPipeline *white = cogl_pipeline_new (ctx);
cogl_set_source_color4ub (255, 255, 255, 255); cogl_pipeline_set_color4f (white, 1, 1, 1, 1);
/* Create a path filling just a quarter of a block. It will use two /* Create a path filling just a quarter of a block. It will use two
rectangles so that we have a sub path in the path */ rectangles so that we have a sub path in the path */
@ -75,22 +78,21 @@ paint (TestState *state)
BLOCK_SIZE, BLOCK_SIZE); BLOCK_SIZE, BLOCK_SIZE);
cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2,
BLOCK_SIZE * 3 / 4, BLOCK_SIZE); BLOCK_SIZE * 3 / 4, BLOCK_SIZE);
path_a = cogl_handle_ref (cogl_get_path ()); path_a = cogl_object_ref (cogl_get_path ());
draw_path_at (0, 0); draw_path_at (path_a, white, 0, 0);
/* Create another path filling the whole block */ /* Create another path filling the whole block */
cogl_path_new ();
cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
path_b = cogl_handle_ref (cogl_get_path ()); path_b = cogl_object_ref (cogl_get_path ());
draw_path_at (1, 0); draw_path_at (path_b, white, 1, 0);
/* Draw the first path again */ /* Draw the first path again */
cogl_set_path (path_a); draw_path_at (path_a, white, 2, 0);
draw_path_at (2, 0);
/* Draw a copy of path a */ /* Draw a copy of path a */
path_c = cogl_path_copy (path_a); path_c = cogl_path_copy (path_a);
cogl_set_path (path_c); draw_path_at (path_c, white, 3, 0);
draw_path_at (3, 0);
/* Add another rectangle to path a. We'll use line_to's instead of /* Add another rectangle to path a. We'll use line_to's instead of
cogl_rectangle so that we don't create another sub-path because cogl_rectangle so that we don't create another sub-path because
@ -100,11 +102,10 @@ paint (TestState *state)
cogl_path_line_to (0, 0); cogl_path_line_to (0, 0);
cogl_path_line_to (BLOCK_SIZE / 2, 0); cogl_path_line_to (BLOCK_SIZE / 2, 0);
cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
draw_path_at (4, 0); draw_path_at (path_a, white, 4, 0);
/* Draw the copy again. It should not have changed */ /* Draw the copy again. It should not have changed */
cogl_set_path (path_c); draw_path_at (path_c, white, 5, 0);
draw_path_at (5, 0);
/* Add another rectangle to path c. It will be added in two halves, /* Add another rectangle to path c. It will be added in two halves,
one as an extension of the previous path and the other as a new one as an extension of the previous path and the other as a new
@ -115,32 +116,39 @@ paint (TestState *state)
cogl_path_line_to (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2);
cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
cogl_path_rectangle (BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2); cogl_path_rectangle (BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2);
draw_path_at (6, 0); draw_path_at (path_c, white, 6, 0);
/* Draw the original path again. It should not have changed */ /* Draw the original path again. It should not have changed */
cogl_set_path (path_a); cogl_set_path (path_a);
draw_path_at (7, 0); draw_path_at (path_a, white, 7, 0);
cogl_handle_unref (path_a); cogl_object_unref (path_a);
cogl_handle_unref (path_b); cogl_object_unref (path_b);
cogl_handle_unref (path_c); cogl_object_unref (path_c);
/* Draw a self-intersecting path. The part that intersects should be /* Draw a self-intersecting path. The part that intersects should be
inverted */ inverted */
cogl_path_new ();
cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
cogl_path_line_to (0, BLOCK_SIZE / 2); cogl_path_line_to (0, BLOCK_SIZE / 2);
cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
cogl_path_line_to (BLOCK_SIZE / 2, 0); cogl_path_line_to (BLOCK_SIZE / 2, 0);
cogl_path_close (); cogl_path_close ();
draw_path_at (8, 0); path_a = cogl_object_ref (cogl_get_path ());
draw_path_at (path_a, white, 8, 0);
cogl_object_unref (path_a);
/* Draw two sub paths. Where the paths intersect it should be /* Draw two sub paths. Where the paths intersect it should be
inverted */ inverted */
cogl_path_new ();
cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE);
draw_path_at (9, 0); path_a = cogl_object_ref (cogl_get_path ());
draw_path_at (path_a, white, 9, 0);
cogl_object_unref (path_a);
/* Draw a clockwise outer path */ /* Draw a clockwise outer path */
cogl_path_new ();
cogl_path_move_to (0, 0); cogl_path_move_to (0, 0);
cogl_path_line_to (BLOCK_SIZE, 0); cogl_path_line_to (BLOCK_SIZE, 0);
cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE); cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE);
@ -159,15 +167,15 @@ paint (TestState *state)
cogl_path_line_to (BLOCK_SIZE, 0); cogl_path_line_to (BLOCK_SIZE, 0);
cogl_path_close (); cogl_path_close ();
/* Retain the path for the next test */ /* Retain the path for the next test */
path_a = cogl_handle_ref (cogl_get_path ()); path_a = cogl_object_ref (cogl_get_path ());
draw_path_at (10, 0); draw_path_at (path_a, white, 10, 0);
/* Draw the same path again with the other fill rule */ /* Draw the same path again with the other fill rule */
cogl_set_path (path_a); cogl_set_path (path_a);
cogl_path_set_fill_rule (COGL_PATH_FILL_RULE_NON_ZERO); cogl_path_set_fill_rule (COGL_PATH_FILL_RULE_NON_ZERO);
draw_path_at (11, 0); draw_path_at (path_a, white, 11, 0);
cogl_handle_unref (path_a); cogl_object_unref (path_a);
} }
static void static void
@ -199,11 +207,7 @@ test_path (void)
-1, -1,
100); 100);
/* XXX: we have to push/pop a framebuffer since this test currently
* uses the legacy cogl_rectangle() api. */
cogl_push_framebuffer (fb);
paint (&state); paint (&state);
cogl_pop_framebuffer ();
validate_result (); validate_result ();
if (cogl_test_verbose ()) if (cogl_test_verbose ())