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

View File

@ -278,7 +278,9 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
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
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));
silhouette_callback (user_data);
silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data);
if (merge)
{
@ -382,11 +384,15 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
}
static void
paint_path_silhouette (void *user_data)
paint_path_silhouette (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
void *user_data)
{
CoglPath *path = user_data;
if (path->data->path_nodes->len >= 3)
_cogl_path_fill_nodes (path,
framebuffer,
pipeline,
COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION |
COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
@ -411,10 +417,12 @@ add_stencil_clip_path (CoglFramebuffer *framebuffer,
}
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_get_source (),
_cogl_framebuffer_draw_primitive (framebuffer,
pipeline,
user_data,
COGL_DRAW_SKIP_JOURNAL_FLUSH |
COGL_DRAW_SKIP_PIPELINE_VALIDATION |

View File

@ -47,6 +47,7 @@
#include "cogl1-context.h"
#include "cogl-private.h"
#include "cogl-primitives-private.h"
#include "cogl-path-private.h"
#ifndef GL_FRAMEBUFFER
#define GL_FRAMEBUFFER 0x8D40
@ -3522,3 +3523,27 @@ cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer,
n_rectangles,
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,
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
* the CoglBufferBits type which doesn't seem future proof? */
/**

View File

@ -116,6 +116,14 @@ CoglBool
_cogl_path_is_rectangle (CoglPath *path);
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 */

View File

@ -202,37 +202,37 @@ _cogl_path_add_node (CoglPath *path,
data->is_rectangle = FALSE;
}
static void
_cogl_path_stroke_nodes (CoglPath *path)
void
_cogl_path_stroke_nodes (CoglPath *path,
CoglFramebuffer *framebuffer,
CoglPipeline *pipeline)
{
CoglPathData *data = path->data;
CoglPipeline *copy = NULL;
CoglPipeline *source;
unsigned int path_start;
int path_num = 0;
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);
source = copy;
pipeline = copy;
}
_cogl_path_build_stroke_attribute_buffer (path);
cogl_push_source (source);
for (path_start = 0;
path_start < data->path_nodes->len;
path_start += node->path_size)
{
node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
cogl_framebuffer_vdraw_attributes (cogl_get_draw_framebuffer (),
source,
cogl_framebuffer_vdraw_attributes (framebuffer,
pipeline,
COGL_VERTICES_MODE_LINE_STRIP,
0, node->path_size,
data->stroke_attributes[path_num],
@ -241,8 +241,6 @@ _cogl_path_stroke_nodes (CoglPath *path)
path_num++;
}
cogl_pop_source ();
if (copy)
cogl_object_unref (copy);
}
@ -273,10 +271,10 @@ _cogl_path_get_bounds (CoglPath *path,
}
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 &
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 (fb, path);
cogl_rectangle (path->data->path_nodes_min.x,
path->data->path_nodes_min.y,
path->data->path_nodes_max.x,
path->data->path_nodes_max.y);
cogl_framebuffer_pop_clip (fb);
cogl_framebuffer_push_path_clip (framebuffer, path);
cogl_framebuffer_draw_rectangle (framebuffer,
pipeline,
path->data->path_nodes_min.x,
path->data->path_nodes_min.y,
path->data->path_nodes_max.x,
path->data->path_nodes_max.y);
cogl_framebuffer_pop_clip (framebuffer);
}
static CoglBool
@ -321,30 +320,55 @@ validate_layer_cb (CoglPipelineLayer *layer, void *user_data)
}
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;
CoglPipeline *pipeline = cogl_get_source ();
if (path->data->path_nodes->len == 0)
return;
_cogl_pipeline_foreach_layer_internal (pipeline,
validate_layer_cb, &needs_fallback);
if (needs_fallback)
/* 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)
{
_cogl_path_fill_nodes_with_clipped_rectangle (path);
return;
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_path_build_fill_attribute_buffer (path);
_cogl_pipeline_foreach_layer_internal (pipeline,
validate_layer_cb,
&needs_fallback);
if (needs_fallback)
{
_cogl_path_fill_nodes_with_clipped_rectangle (path,
framebuffer,
pipeline);
return;
}
_cogl_framebuffer_draw_indexed_attributes (cogl_get_draw_framebuffer (),
pipeline,
COGL_VERTICES_MODE_TRIANGLES,
0, /* first_vertex */
path->data->fill_vbo_n_indices,
path->data->fill_vbo_indices,
path->data->fill_attributes,
COGL_PATH_N_ATTRIBUTES,
flags);
_cogl_path_build_fill_attribute_buffer (path);
_cogl_framebuffer_draw_indexed_attributes (framebuffer,
pipeline,
COGL_VERTICES_MODE_TRIANGLES,
0, /* first_vertex */
path->data->fill_vbo_n_indices,
path->data->fill_vbo_indices,
path->data->fill_attributes,
COGL_PATH_N_ATTRIBUTES,
flags);
}
}
void
@ -367,7 +391,10 @@ cogl2_path_fill (CoglPath *path)
cogl_rectangle (x_1, y_1, x_2, y_2);
}
else
_cogl_path_fill_nodes (path, 0);
_cogl_path_fill_nodes (path,
cogl_get_draw_framebuffer (),
cogl_get_source (),
0);
}
void
@ -378,7 +405,9 @@ cogl2_path_stroke (CoglPath *path)
if (path->data->path_nodes->len == 0)
return;
_cogl_path_stroke_nodes (path);
_cogl_path_stroke_nodes (path,
cogl_get_draw_framebuffer (),
cogl_get_source ());
}
void

View File

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

View File

@ -15,11 +15,13 @@ typedef struct _TestState
} TestState;
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_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);
}
@ -64,9 +66,10 @@ check_block (int block_x, int block_y, int block_mask)
static void
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
rectangles so that we have a sub path in the path */
@ -75,22 +78,21 @@ paint (TestState *state)
BLOCK_SIZE, BLOCK_SIZE);
cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2,
BLOCK_SIZE * 3 / 4, BLOCK_SIZE);
path_a = cogl_handle_ref (cogl_get_path ());
draw_path_at (0, 0);
path_a = cogl_object_ref (cogl_get_path ());
draw_path_at (path_a, white, 0, 0);
/* Create another path filling the whole block */
cogl_path_new ();
cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
path_b = cogl_handle_ref (cogl_get_path ());
draw_path_at (1, 0);
path_b = cogl_object_ref (cogl_get_path ());
draw_path_at (path_b, white, 1, 0);
/* Draw the first path again */
cogl_set_path (path_a);
draw_path_at (2, 0);
draw_path_at (path_a, white, 2, 0);
/* Draw a copy of path a */
path_c = cogl_path_copy (path_a);
cogl_set_path (path_c);
draw_path_at (3, 0);
draw_path_at (path_c, white, 3, 0);
/* 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
@ -100,11 +102,10 @@ paint (TestState *state)
cogl_path_line_to (0, 0);
cogl_path_line_to (BLOCK_SIZE / 2, 0);
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 */
cogl_set_path (path_c);
draw_path_at (5, 0);
draw_path_at (path_c, white, 5, 0);
/* 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
@ -115,32 +116,39 @@ paint (TestState *state)
cogl_path_line_to (BLOCK_SIZE * 3 / 4, 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);
draw_path_at (6, 0);
draw_path_at (path_c, white, 6, 0);
/* Draw the original path again. It should not have changed */
cogl_set_path (path_a);
draw_path_at (7, 0);
draw_path_at (path_a, white, 7, 0);
cogl_handle_unref (path_a);
cogl_handle_unref (path_b);
cogl_handle_unref (path_c);
cogl_object_unref (path_a);
cogl_object_unref (path_b);
cogl_object_unref (path_c);
/* Draw a self-intersecting path. The part that intersects should be
inverted */
cogl_path_new ();
cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
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, 0);
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
inverted */
cogl_path_new ();
cogl_path_rectangle (0, 0, 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 */
cogl_path_new ();
cogl_path_move_to (0, 0);
cogl_path_line_to (BLOCK_SIZE, 0);
cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE);
@ -159,15 +167,15 @@ paint (TestState *state)
cogl_path_line_to (BLOCK_SIZE, 0);
cogl_path_close ();
/* Retain the path for the next test */
path_a = cogl_handle_ref (cogl_get_path ());
draw_path_at (10, 0);
path_a = cogl_object_ref (cogl_get_path ());
draw_path_at (path_a, white, 10, 0);
/* Draw the same path again with the other fill rule */
cogl_set_path (path_a);
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
@ -199,11 +207,7 @@ test_path (void)
-1,
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);
cogl_pop_framebuffer ();
validate_result ();
if (cogl_test_verbose ())