cogl: Support retained paths

This adds three new API calls:

  CoglHandle cogl_path_get()
  void cogl_path_set(CoglHandle path)
  CoglHandle cogl_path_copy(CoglHandle path)

All of the fields relating to the path have been moved from the Cogl
context to a new CoglPath handle type. The cogl context now just
contains a CoglPath handle. All of the existing path commands
manipulate the data in the current path handle. cogl_path_new now just
creates a new path handle and unrefs the old one.

The path handle can be stored for later with cogl_path_get. The path
can then be copied with cogl_path_copy. Internally it implements
copy-on-write semantics with an extra optimisation that it will only
copy the data if the new path is modified, but not if the original
path is modified. It can do this because the only way to modify a path
is by appending to it so the copied path is able to store its own path
length and only render the nodes up to that length. For this to work
the copied path also needs to keep its own copies of the path extents
because the parent path may change these by adding nodes.

The clip stack now uses the cogl_path_copy mechanism to store paths in
the stack instead of directly copying the data. This should save some
memory and processing time.
This commit is contained in:
Neil Roberts 2010-04-08 17:43:27 +01:00
parent 45885850d2
commit 9ee6dd240b
9 changed files with 413 additions and 146 deletions

View File

@ -97,6 +97,7 @@ cogl_sources_c = \
$(srcdir)/cogl-bitmap-fallback.c \ $(srcdir)/cogl-bitmap-fallback.c \
$(srcdir)/cogl-primitives.h \ $(srcdir)/cogl-primitives.h \
$(srcdir)/cogl-primitives.c \ $(srcdir)/cogl-primitives.c \
$(srcdir)/cogl-path-private.h \
$(srcdir)/cogl-path.h \ $(srcdir)/cogl-path.h \
$(srcdir)/cogl-path.c \ $(srcdir)/cogl-path.c \
$(srcdir)/cogl-bitmap-pixbuf.c \ $(srcdir)/cogl-bitmap-pixbuf.c \

View File

@ -38,13 +38,7 @@
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-journal-private.h" #include "cogl-journal-private.h"
#include "cogl-util.h" #include "cogl-util.h"
#include "cogl-path-private.h"
void _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
floatVec2 nodes_max,
unsigned int path_size,
CoglPathNode *path,
gboolean merge,
gboolean need_clear);
typedef struct _CoglClipStack CoglClipStack; typedef struct _CoglClipStack CoglClipStack;
@ -96,11 +90,7 @@ struct _CoglClipStackEntryPath
/* The matrix that was current when the clip was set */ /* The matrix that was current when the clip was set */
CoglMatrix matrix; CoglMatrix matrix;
floatVec2 path_nodes_min; CoglHandle path;
floatVec2 path_nodes_max;
unsigned int path_size;
CoglPathNode path[1];
}; };
static void static void
@ -567,15 +557,10 @@ cogl_clip_push_from_path_preserve (void)
stack = clip_state->stacks->data; stack = clip_state->stacks->data;
entry = g_malloc (sizeof (CoglClipStackEntryPath) entry = g_slice_new (CoglClipStackEntryPath);
+ sizeof (CoglPathNode) * (ctx->path_nodes->len - 1));
entry->type = COGL_CLIP_STACK_PATH; entry->type = COGL_CLIP_STACK_PATH;
entry->path_nodes_min = ctx->path_nodes_min; entry->path = cogl_path_copy (cogl_path_get ());
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); cogl_get_modelview_matrix (&entry->matrix);
@ -617,7 +602,10 @@ _cogl_clip_pop_real (CoglClipStackState *clip_state)
else if (type == COGL_CLIP_STACK_WINDOW_RECT) else if (type == COGL_CLIP_STACK_WINDOW_RECT)
g_slice_free (CoglClipStackEntryWindowRect, entry); g_slice_free (CoglClipStackEntryWindowRect, entry);
else else
g_free (entry); {
cogl_handle_unref (((CoglClipStackEntryPath *) entry)->path);
g_slice_free (CoglClipStackEntryPath, entry);
}
stack->stack_top = g_list_delete_link (stack->stack_top, stack->stack_top = g_list_delete_link (stack->stack_top,
stack->stack_top); stack->stack_top);
@ -693,15 +681,12 @@ _cogl_flush_clip_state (CoglClipStackState *clip_state)
if (type == COGL_CLIP_STACK_PATH) if (type == COGL_CLIP_STACK_PATH)
{ {
CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry; CoglClipStackEntryPath *path_entry = (CoglClipStackEntryPath *) entry;
_cogl_matrix_stack_push (modelview_stack); _cogl_matrix_stack_push (modelview_stack);
_cogl_matrix_stack_set (modelview_stack, &path->matrix); _cogl_matrix_stack_set (modelview_stack, &path_entry->matrix);
_cogl_add_path_to_stencil_buffer (path->path_nodes_min, _cogl_add_path_to_stencil_buffer (path_entry->path,
path->path_nodes_max,
path->path_size,
path->path,
using_stencil_buffer, using_stencil_buffer,
TRUE); TRUE);

View File

@ -33,6 +33,7 @@
#include "cogl-texture-private.h" #include "cogl-texture-private.h"
#include "cogl-material-private.h" #include "cogl-material-private.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-path-private.h"
#include <string.h> #include <string.h>
@ -108,8 +109,7 @@ cogl_create_context (void)
_context->dirty_bound_framebuffer = TRUE; _context->dirty_bound_framebuffer = TRUE;
_context->dirty_gl_viewport = TRUE; _context->dirty_gl_viewport = TRUE;
_context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); _context->current_path = _cogl_path_new ();
_context->last_path = 0;
_context->stencil_material = cogl_material_new (); _context->stencil_material = cogl_material_new ();
_context->in_begin_gl_block = FALSE; _context->in_begin_gl_block = FALSE;
@ -168,8 +168,8 @@ _cogl_destroy_context ()
_cogl_free_framebuffer_stack (_context->framebuffer_stack); _cogl_free_framebuffer_stack (_context->framebuffer_stack);
if (_context->path_nodes) if (_context->current_path)
g_array_free (_context->path_nodes, TRUE); cogl_handle_unref (_context->current_path);
if (_context->default_gl_texture_2d_tex) if (_context->default_gl_texture_2d_tex)
cogl_handle_unref (_context->default_gl_texture_2d_tex); cogl_handle_unref (_context->default_gl_texture_2d_tex);

View File

@ -99,12 +99,7 @@ typedef struct
gboolean dirty_gl_viewport; gboolean dirty_gl_viewport;
/* Primitives */ /* Primitives */
floatVec2 path_start; CoglHandle current_path;
floatVec2 path_pen;
GArray *path_nodes;
unsigned int last_path;
floatVec2 path_nodes_min;
floatVec2 path_nodes_max;
CoglHandle stencil_material; CoglHandle stencil_material;
/* Pre-generated VBOs containing indices to generate GL_TRIANGLES /* Pre-generated VBOs containing indices to generate GL_TRIANGLES

View File

@ -27,34 +27,6 @@
#include "cogl.h" #include "cogl.h"
#include "cogl-matrix-stack.h" #include "cogl-matrix-stack.h"
typedef struct _floatVec2
{
float x;
float y;
} floatVec2;
typedef struct _CoglPathNode
{
float x;
float y;
unsigned int path_size;
} CoglPathNode;
typedef struct _CoglBezQuad
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
} CoglBezQuad;
typedef struct _CoglBezCubic
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
floatVec2 p4;
} CoglBezCubic;
typedef enum typedef enum
{ {
COGL_FRONT_WINDING_CLOCKWISE, COGL_FRONT_WINDING_CLOCKWISE,

View File

@ -0,0 +1,96 @@
/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2010 Intel Corporation.
*
* 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef __COGL_PATH_PRIVATE_H
#define __COGL_PATH_PRIVATE_H
#include "cogl-handle.h"
#define COGL_PATH(tex) ((CoglPath *)(tex))
typedef struct _floatVec2
{
float x;
float y;
} floatVec2;
typedef struct _CoglPathNode
{
float x;
float y;
unsigned int path_size;
} CoglPathNode;
typedef struct _CoglBezQuad
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
} CoglBezQuad;
typedef struct _CoglBezCubic
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
floatVec2 p4;
} CoglBezCubic;
typedef struct _CoglPath CoglPath;
struct _CoglPath
{
CoglHandleObject _parent;
/* If this path was created with cogl_path_copy then parent_path
will point to the copied path. Otherwise it will be
COGL_INVALID_HANDLE to indicate that we own path_nodes. */
CoglHandle parent_path;
/* Pointer to the path nodes array. This will point directly into
the parent path if this path is a copy */
GArray *path_nodes;
/* Number of nodes to render from the data. This may be different
from path_nodes->len if this is a copied path and the parent path
was appended to. If that is the case then we need to be careful
to check that the size of a sub path doesn't extend past
path_size */
unsigned int path_size;
floatVec2 path_start;
floatVec2 path_pen;
unsigned int last_path;
floatVec2 path_nodes_min;
floatVec2 path_nodes_max;
};
/* This is an internal version of cogl_path_new that doesn't affect
the current path and just creates a new handle */
CoglHandle
_cogl_path_new (void);
void
_cogl_add_path_to_stencil_buffer (CoglHandle path,
gboolean merge,
gboolean need_clear);
#endif /* __COGL_PATH_PRIVATE_H */

View File

@ -31,6 +31,7 @@
#include "cogl-journal-private.h" #include "cogl-journal-private.h"
#include "cogl-material-private.h" #include "cogl-material-private.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-path-private.h"
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
@ -41,37 +42,86 @@
#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture #define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
#endif #endif
static void _cogl_path_free (CoglPath *path);
COGL_HANDLE_DEFINE (Path, path);
static void
_cogl_path_modify (CoglPath *path)
{
/* This needs to be called whenever the path is about to be modified
to implement copy-on-write semantics. Note that the current
mechanism assumes that a path will only ever be appended to (ie,
the path won't be cleared or have nodes in the middle
changed). This means that we don't need to keep track of how many
copies a node has because the copies can just keep track of the
number of nodes they should draw */
/* If this path is a copy then we need to actually copy the data so
we can modify it */
if (path->parent_path)
{
CoglPath *old_path = COGL_PATH (path->parent_path);
CoglPathNode *old_nodes = &g_array_index (old_path->path_nodes,
CoglPathNode, 0);
CoglPathNode *new_nodes;
int i;
path->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
/* The parent path may have extra nodes added after the copy was
made so we need to truncate it */
g_array_set_size (path->path_nodes, path->path_size);
memcpy (path->path_nodes->data, old_nodes,
sizeof (CoglPathNode) * path->path_size);
/* We need to make sure the last path size doesn't extend past
the total path size */
new_nodes = &g_array_index (path->path_nodes, CoglPathNode, 0);
for (i = 0; i < path->path_size; i += new_nodes[i].path_size)
if (new_nodes[i].path_size >= path->path_size)
new_nodes[i].path_size = path->path_size - i;
cogl_handle_unref (path->parent_path);
path->parent_path = COGL_INVALID_HANDLE;
}
}
static void static void
_cogl_path_add_node (gboolean new_sub_path, _cogl_path_add_node (gboolean new_sub_path,
float x, float x,
float y) float y)
{ {
CoglPathNode new_node; CoglPathNode new_node;
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
path = COGL_PATH (ctx->current_path);
_cogl_path_modify (path);
new_node.x = x; new_node.x = x;
new_node.y = y; new_node.y = y;
new_node.path_size = 0; new_node.path_size = 0;
if (new_sub_path || ctx->path_nodes->len == 0) if (new_sub_path || path->path_size == 0)
ctx->last_path = ctx->path_nodes->len; path->last_path = path->path_size;
g_array_append_val (ctx->path_nodes, new_node); g_array_append_val (path->path_nodes, new_node);
path->path_size++;
g_array_index (ctx->path_nodes, CoglPathNode, ctx->last_path).path_size++; g_array_index (path->path_nodes, CoglPathNode, path->last_path).path_size++;
if (ctx->path_nodes->len == 1) if (path->path_size == 1)
{ {
ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; path->path_nodes_min.x = path->path_nodes_max.x = x;
ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; path->path_nodes_min.y = path->path_nodes_max.y = y;
} }
else else
{ {
if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x; if (x < path->path_nodes_min.x) path->path_nodes_min.x = x;
if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x; if (x > path->path_nodes_max.x) path->path_nodes_max.x = x;
if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y; if (y < path->path_nodes_min.y) path->path_nodes_min.y = y;
if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y; if (y > path->path_nodes_max.y) path->path_nodes_max.y = y;
} }
} }
@ -80,10 +130,13 @@ _cogl_path_stroke_nodes (void)
{ {
unsigned int path_start = 0; unsigned int path_start = 0;
unsigned long enable_flags = COGL_ENABLE_VERTEX_ARRAY; unsigned long enable_flags = COGL_ENABLE_VERTEX_ARRAY;
CoglPath *path;
CoglMaterialFlushOptions options; CoglMaterialFlushOptions options;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
path = COGL_PATH (ctx->current_path);
_cogl_journal_flush (); _cogl_journal_flush ();
/* NB: _cogl_framebuffer_flush_state may disrupt various state (such /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
@ -100,17 +153,19 @@ _cogl_path_stroke_nodes (void)
_cogl_material_flush_gl_state (ctx->source_material, &options); _cogl_material_flush_gl_state (ctx->source_material, &options);
while (path_start < ctx->path_nodes->len) while (path_start < path->path_size)
{ {
CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, CoglPathNode *node = &g_array_index (path->path_nodes, CoglPathNode,
path_start); path_start);
GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), &node->x) );
(guint8 *) path /* We need to limit the size of the sub path to the size of our
+ G_STRUCT_OFFSET (CoglPathNode, x)) ); path in case this path is a copy and the parent path has
GE( glDrawArrays (GL_LINE_STRIP, 0, path->path_size) ); grown */
GE( glDrawArrays (GL_LINE_STRIP, 0,
MIN (node->path_size, path->path_size - path_start)) );
path_start += path->path_size; path_start += node->path_size;
} }
} }
@ -129,10 +184,7 @@ _cogl_path_get_bounds (floatVec2 nodes_min,
} }
void void
_cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, _cogl_add_path_to_stencil_buffer (CoglHandle path_handle,
floatVec2 nodes_max,
unsigned int path_size,
CoglPathNode *path,
gboolean merge, gboolean merge,
gboolean need_clear) gboolean need_clear)
{ {
@ -150,10 +202,12 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
_cogl_framebuffer_get_modelview_stack (framebuffer); _cogl_framebuffer_get_modelview_stack (framebuffer);
CoglMatrixStack *projection_stack = CoglMatrixStack *projection_stack =
_cogl_framebuffer_get_projection_stack (framebuffer); _cogl_framebuffer_get_projection_stack (framebuffer);
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
path = COGL_PATH (path_handle);
/* We don't track changes to the stencil buffer in the journal /* We don't track changes to the stencil buffer in the journal
* so we need to flush any batched geometry first */ * so we need to flush any batched geometry first */
_cogl_journal_flush (); _cogl_journal_flush ();
@ -173,7 +227,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
_cogl_material_get_cogl_enable_flags (ctx->source_material); _cogl_material_get_cogl_enable_flags (ctx->source_material);
_cogl_enable (enable_flags); _cogl_enable (enable_flags);
_cogl_path_get_bounds (nodes_min, nodes_max, _cogl_path_get_bounds (path->path_nodes_min, path->path_nodes_max,
&bounds_x, &bounds_y, &bounds_w, &bounds_h); &bounds_x, &bounds_y, &bounds_w, &bounds_h);
GE( glEnable (GL_STENCIL_TEST) ); GE( glEnable (GL_STENCIL_TEST) );
@ -222,12 +276,17 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
} }
ctx->n_texcoord_arrays_enabled = 0; ctx->n_texcoord_arrays_enabled = 0;
while (path_start < path_size) while (path_start < path->path_size)
{ {
GE (glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), CoglPathNode *node =
(guint8 *) path &g_array_index (path->path_nodes, CoglPathNode, path_start);
+ G_STRUCT_OFFSET (CoglPathNode, x)));
GE (glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size)); GE (glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), &node->x));
/* We need to limit the size of the sub path to the size of our
path in case this path is a copy and the parent path has
grown */
GE (glDrawArrays (GL_TRIANGLE_FAN, 0,
MIN (node->path_size, path->path_size - path_start)));
if (sub_path_num > 0) if (sub_path_num > 0)
{ {
@ -251,8 +310,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
GE (glStencilMask (merge ? 4 : 2)); GE (glStencilMask (merge ? 4 : 2));
path_start += path->path_size; path_start += node->path_size;
path += path->path_size;
sub_path_num++; sub_path_num++;
} }
@ -488,6 +546,7 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path,
static void static void
_cogl_path_fill_nodes (void) _cogl_path_fill_nodes (void)
{ {
CoglPath *path;
float bounds_x; float bounds_x;
float bounds_y; float bounds_y;
float bounds_w; float bounds_w;
@ -495,7 +554,9 @@ _cogl_path_fill_nodes (void)
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max, path = COGL_PATH (ctx->current_path);
_cogl_path_get_bounds (path->path_nodes_min, path->path_nodes_max,
&bounds_x, &bounds_y, &bounds_w, &bounds_h); &bounds_x, &bounds_y, &bounds_w, &bounds_h);
if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_FORCE_SCANLINE_PATHS)) && if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_FORCE_SCANLINE_PATHS)) &&
@ -509,11 +570,7 @@ _cogl_path_fill_nodes (void)
framebuffer = _cogl_get_framebuffer (); framebuffer = _cogl_get_framebuffer ();
clip_state = _cogl_framebuffer_get_clip_state (framebuffer); clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
_cogl_add_path_to_stencil_buffer (ctx->path_nodes_min, _cogl_add_path_to_stencil_buffer (ctx->current_path,
ctx->path_nodes_max,
ctx->path_nodes->len,
&g_array_index (ctx->path_nodes,
CoglPathNode, 0),
clip_state->stencil_used, clip_state->stencil_used,
FALSE); FALSE);
@ -535,17 +592,21 @@ _cogl_path_fill_nodes (void)
{ {
unsigned int path_start = 0; unsigned int path_start = 0;
while (path_start < ctx->path_nodes->len) while (path_start < path->path_size)
{ {
CoglPathNode *path = &g_array_index (ctx->path_nodes, CoglPathNode, CoglPathNode *node = &g_array_index (path->path_nodes, CoglPathNode,
path_start); path_start);
_cogl_path_fill_nodes_scanlines (path, /* We need to limit the size of the sub path to the size of
path->path_size, our path in case this path is a copy and the parent path
has grown */
_cogl_path_fill_nodes_scanlines (node,
MIN (node->path_size,
path->path_size - path_start),
bounds_x, bounds_y, bounds_x, bounds_y,
bounds_w, bounds_h); bounds_w, bounds_h);
path_start += path->path_size; path_start += node->path_size;
} }
} }
} }
@ -563,7 +624,7 @@ cogl_path_fill_preserve (void)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes->len == 0) if (COGL_PATH (ctx->current_path)->path_size == 0)
return; return;
_cogl_path_fill_nodes (); _cogl_path_fill_nodes ();
@ -582,7 +643,7 @@ cogl_path_stroke_preserve (void)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->path_nodes->len == 0) if (COGL_PATH (ctx->current_path)->path_size == 0)
return; return;
_cogl_path_stroke_nodes (); _cogl_path_stroke_nodes ();
@ -592,57 +653,77 @@ void
cogl_path_move_to (float x, cogl_path_move_to (float x,
float y) float y)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); CoglPath *path;
/* FIXME: handle multiple contours maybe? */ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_add_node (TRUE, x, y); _cogl_path_add_node (TRUE, x, y);
ctx->path_start.x = x; path = COGL_PATH (ctx->current_path);
ctx->path_start.y = y;
ctx->path_pen = ctx->path_start; path->path_start.x = x;
path->path_start.y = y;
path->path_pen = path->path_start;
} }
void void
cogl_path_rel_move_to (float x, cogl_path_rel_move_to (float x,
float y) float y)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_path_move_to (ctx->path_pen.x + x, path = COGL_PATH (ctx->current_path);
ctx->path_pen.y + y);
cogl_path_move_to (path->path_pen.x + x,
path->path_pen.y + y);
} }
void void
cogl_path_line_to (float x, cogl_path_line_to (float x,
float y) float y)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_add_node (FALSE, x, y); _cogl_path_add_node (FALSE, x, y);
ctx->path_pen.x = x; path = COGL_PATH (ctx->current_path);
ctx->path_pen.y = y;
path->path_pen.x = x;
path->path_pen.y = y;
} }
void void
cogl_path_rel_line_to (float x, cogl_path_rel_line_to (float x,
float y) float y)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_path_line_to (ctx->path_pen.x + x, path = COGL_PATH (ctx->current_path);
ctx->path_pen.y + y);
cogl_path_line_to (path->path_pen.x + x,
path->path_pen.y + y);
} }
void void
cogl_path_close (void) cogl_path_close (void)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_add_node (FALSE, ctx->path_start.x, ctx->path_start.y); path = COGL_PATH (ctx->current_path);
ctx->path_pen = ctx->path_start;
_cogl_path_add_node (FALSE, path->path_start.x,
path->path_start.y);
path->path_pen = path->path_start;
} }
void void
@ -650,7 +731,8 @@ cogl_path_new (void)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
g_array_set_size (ctx->path_nodes, 0); cogl_handle_unref (ctx->current_path);
ctx->current_path = _cogl_path_new ();
} }
void void
@ -789,10 +871,14 @@ cogl_path_arc_rel (float center_x,
float angle_2, float angle_2,
float angle_step) float angle_step)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_path_arc (ctx->path_pen.x + center_x, path = COGL_PATH (ctx->current_path);
ctx->path_pen.y + center_y,
_cogl_path_arc (path->path_pen.x + center_x,
path->path_pen.y + center_y,
radius_x, radius_y, radius_x, radius_y,
angle_1, angle_2, angle_1, angle_2,
angle_step, 0 /* no move */); angle_step, 0 /* no move */);
@ -825,11 +911,14 @@ cogl_path_round_rectangle (float x_1,
float radius, float radius,
float arc_step) float arc_step)
{ {
CoglPath *path;
float inner_width = x_2 - x_1 - radius * 2; float inner_width = x_2 - x_1 - radius * 2;
float inner_height = y_2 - y_1 - radius * 2; float inner_height = y_2 - y_1 - radius * 2;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
path = COGL_PATH (ctx->current_path);
cogl_path_move_to (x_1, y_1 + radius); cogl_path_move_to (x_1, y_1 + radius);
cogl_path_arc_rel (radius, 0, cogl_path_arc_rel (radius, 0,
radius, radius, radius, radius,
@ -837,16 +926,16 @@ cogl_path_round_rectangle (float x_1,
270, 270,
arc_step); arc_step);
cogl_path_line_to (ctx->path_pen.x + inner_width, cogl_path_line_to (path->path_pen.x + inner_width,
ctx->path_pen.y); path->path_pen.y);
cogl_path_arc_rel (0, radius, cogl_path_arc_rel (0, radius,
radius, radius, radius, radius,
-90, -90,
0, 0,
arc_step); arc_step);
cogl_path_line_to (ctx->path_pen.x, cogl_path_line_to (path->path_pen.x,
ctx->path_pen.y + inner_height); path->path_pen.y + inner_height);
cogl_path_arc_rel (-radius, 0, cogl_path_arc_rel (-radius, 0,
radius, radius, radius, radius,
@ -854,8 +943,8 @@ cogl_path_round_rectangle (float x_1,
90, 90,
arc_step); arc_step);
cogl_path_line_to (ctx->path_pen.x - inner_width, cogl_path_line_to (path->path_pen.x - inner_width,
ctx->path_pen.y); path->path_pen.y);
cogl_path_arc_rel (0, -radius, cogl_path_arc_rel (0, -radius,
radius, radius, radius, radius,
90, 90,
@ -970,11 +1059,14 @@ cogl_path_curve_to (float x_1,
float y_3) float y_3)
{ {
CoglBezCubic cubic; CoglBezCubic cubic;
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
path = COGL_PATH (ctx->current_path);
/* Prepare cubic curve */ /* Prepare cubic curve */
cubic.p1 = ctx->path_pen; cubic.p1 = path->path_pen;
cubic.p2.x = x_1; cubic.p2.x = x_1;
cubic.p2.y = y_1; cubic.p2.y = y_1;
cubic.p3.x = x_2; cubic.p3.x = x_2;
@ -987,7 +1079,7 @@ cogl_path_curve_to (float x_1,
/* Add last point */ /* Add last point */
_cogl_path_add_node (FALSE, cubic.p4.x, cubic.p4.y); _cogl_path_add_node (FALSE, cubic.p4.x, cubic.p4.y);
ctx->path_pen = cubic.p4; path->path_pen = cubic.p4;
} }
void void
@ -998,16 +1090,85 @@ cogl_path_rel_curve_to (float x_1,
float x_3, float x_3,
float y_3) float y_3)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_path_curve_to (ctx->path_pen.x + x_1, path = COGL_PATH (ctx->current_path);
ctx->path_pen.y + y_1,
ctx->path_pen.x + x_2, cogl_path_curve_to (path->path_pen.x + x_1,
ctx->path_pen.y + y_2, path->path_pen.y + y_1,
ctx->path_pen.x + x_3, path->path_pen.x + x_2,
ctx->path_pen.y + y_3); path->path_pen.y + y_2,
path->path_pen.x + x_3,
path->path_pen.y + y_3);
} }
CoglHandle
cogl_path_get (void)
{
_COGL_GET_CONTEXT (ctx, FALSE);
return ctx->current_path;
}
void
cogl_path_set (CoglHandle handle)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_path (handle))
return;
/* Reference the new handle first in case it is the same as the old
handle */
cogl_handle_ref (handle);
cogl_handle_unref (ctx->current_path);
ctx->current_path = handle;
}
CoglHandle
_cogl_path_new (void)
{
CoglPath *path;
path = g_slice_new (CoglPath);
path->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
path->last_path = 0;
path->parent_path = COGL_INVALID_HANDLE;
path->path_size = 0;
return _cogl_path_handle_new (path);
}
CoglHandle
cogl_path_copy (CoglHandle handle)
{
CoglPath *old_path, *new_path;
_COGL_GET_CONTEXT (ctx, FALSE);
if (!cogl_is_path (handle))
return COGL_INVALID_HANDLE;
old_path = COGL_PATH (handle);
new_path = g_slice_dup (CoglPath, old_path);
new_path->parent_path = cogl_handle_ref (handle);
return _cogl_path_handle_new (new_path);
}
static void
_cogl_path_free (CoglPath *path)
{
if (path->parent_path)
cogl_handle_unref (path->parent_path);
else
g_array_free (path->path_nodes, TRUE);
g_slice_free (CoglPath, path);
}
/* If second order beziers were needed the following code could /* If second order beziers were needed the following code could
* be re-enabled: * be re-enabled:
@ -1086,9 +1247,12 @@ cogl_path_curve2_to (float x_1,
float x_2, float x_2,
float y_2) float y_2)
{ {
CoglPath *path;
CoglBezQuad quad;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
CoglBezQuad quad; path = COGL_PATH (ctx->current_path);
/* Prepare quadratic curve */ /* Prepare quadratic curve */
quad.p1 = ctx->path_pen; quad.p1 = ctx->path_pen;
@ -1102,7 +1266,7 @@ cogl_path_curve2_to (float x_1,
/* Add last point */ /* Add last point */
_cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y); _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y);
ctx->path_pen = quad.p3; path->path_pen = quad.p3;
} }
void void
@ -1111,11 +1275,16 @@ cogl_rel_curve2_to (float x_1,
float x_2, float x_2,
float y_2) float y_2)
{ {
CoglPath *path;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_path_curve2_to (ctx->path_pen.x + x_1, path = COGL_PATH (ctx->current_path);
ctx->path_pen.y + y_1,
ctx->path_pen.x + x_2, cogl_path_curve2_to (path->path_pen.x + x_1,
ctx->path_pen.y + y_2); path->path_pen.y + y_1,
path->path_pen.x + x_2,
path->path_pen.y + y_2);
} }
#endif #endif

View File

@ -341,6 +341,52 @@ cogl_path_round_rectangle (float x_1,
float radius, float radius,
float arc_step); float arc_step);
/**
* cogl_path_get:
*
* Gets a handle to the current path. The path can later be used again
* by calling cogl_path_set(). Note that the path isn't copied so if
* you later call any functions to add to the path it will affect the
* returned handle too. No reference is taken on the path so if you
* want to retain it you should take your own reference with
* cogl_handle_ref().
*
* Return value: a handle to the current path.
*
* Since: 1.4
*/
CoglHandle
cogl_path_get (void);
/**
* cogl_path_set:
* @handle: A %CoglHandle to a path
*
* Replaces the current path with @handle. A reference is taken on the
* handle so if you no longer need the path you should unref with
* cogl_handle_unref().
*
* Since: 1.4
*/
void
cogl_path_set (CoglHandle handle);
/**
* cogl_path_copy:
* @handle: A %CoglHandle to a path
*
* Returns a new copy of the path in @handle. The new path has a
* reference count of 1 so you should unref it with
* cogl_handle_unref() if you no longer need it.
*
* Internally the path will share the data until one of the paths is
* modified so copying paths should be relatively cheap.
*
* Return value: a copy of the path in @handle.
*/
CoglHandle
cogl_path_copy (CoglHandle handle);
G_END_DECLS G_END_DECLS
#endif /* __COGL_PATH_H__ */ #endif /* __COGL_PATH_H__ */

View File

@ -155,6 +155,9 @@ cogl_polygon
<SUBSECTION> <SUBSECTION>
cogl_path_new cogl_path_new
cogl_path_get
cogl_path_set
cogl_path_copy
cogl_path_move_to cogl_path_move_to
cogl_path_close cogl_path_close
cogl_path_line_to cogl_path_line_to