cogl: Support retained paths
authorNeil Roberts <neil@linux.intel.com>
Thu, 8 Apr 2010 16:43:27 +0000 (17:43 +0100)
committerNeil Roberts <neil@linux.intel.com>
Thu, 8 Apr 2010 18:53:38 +0000 (19:53 +0100)
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.

clutter/cogl/cogl/Makefile.am
clutter/cogl/cogl/cogl-clip-stack.c
clutter/cogl/cogl/cogl-context.c
clutter/cogl/cogl/cogl-context.h
clutter/cogl/cogl/cogl-internal.h
clutter/cogl/cogl/cogl-path-private.h [new file with mode: 0644]
clutter/cogl/cogl/cogl-path.c
clutter/cogl/cogl/cogl-path.h
doc/reference/cogl/cogl-sections.txt

index c6dba04..ac93439 100644 (file)
@@ -97,6 +97,7 @@ cogl_sources_c = \
        $(srcdir)/cogl-bitmap-fallback.c                \
        $(srcdir)/cogl-primitives.h                     \
        $(srcdir)/cogl-primitives.c                     \
+       $(srcdir)/cogl-path-private.h                   \
        $(srcdir)/cogl-path.h                           \
        $(srcdir)/cogl-path.c                           \
        $(srcdir)/cogl-bitmap-pixbuf.c                  \
index 0a3d133..4276725 100644 (file)
 #include "cogl-framebuffer-private.h"
 #include "cogl-journal-private.h"
 #include "cogl-util.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);
+#include "cogl-path-private.h"
 
 typedef struct _CoglClipStack CoglClipStack;
 
@@ -96,11 +90,7 @@ struct _CoglClipStackEntryPath
   /* The matrix that was current when the clip was set */
   CoglMatrix             matrix;
 
-  floatVec2              path_nodes_min;
-  floatVec2              path_nodes_max;
-
-  unsigned int           path_size;
-  CoglPathNode           path[1];
+  CoglHandle             path;
 };
 
 static void
@@ -567,15 +557,10 @@ cogl_clip_push_from_path_preserve (void)
 
   stack = clip_state->stacks->data;
 
-  entry = g_malloc (sizeof (CoglClipStackEntryPath)
-                    + sizeof (CoglPathNode) * (ctx->path_nodes->len - 1));
+  entry = g_slice_new (CoglClipStackEntryPath);
 
   entry->type = COGL_CLIP_STACK_PATH;
-  entry->path_nodes_min = ctx->path_nodes_min;
-  entry->path_nodes_max = ctx->path_nodes_max;
-  entry->path_size = ctx->path_nodes->len;
-  memcpy (entry->path, ctx->path_nodes->data,
-          sizeof (CoglPathNode) * ctx->path_nodes->len);
+  entry->path = cogl_path_copy (cogl_path_get ());
 
   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)
     g_slice_free (CoglClipStackEntryWindowRect, entry);
   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);
@@ -693,15 +681,12 @@ _cogl_flush_clip_state (CoglClipStackState *clip_state)
 
       if (type == COGL_CLIP_STACK_PATH)
         {
-          CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry;
+          CoglClipStackEntryPath *path_entry = (CoglClipStackEntryPath *) entry;
 
           _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,
-                                            path->path_nodes_max,
-                                            path->path_size,
-                                            path->path,
+          _cogl_add_path_to_stencil_buffer (path_entry->path,
                                             using_stencil_buffer,
                                             TRUE);
 
index 72601c7..cdbd99e 100644 (file)
@@ -33,6 +33,7 @@
 #include "cogl-texture-private.h"
 #include "cogl-material-private.h"
 #include "cogl-framebuffer-private.h"
+#include "cogl-path-private.h"
 
 #include <string.h>
 
@@ -108,8 +109,7 @@ cogl_create_context (void)
   _context->dirty_bound_framebuffer = TRUE;
   _context->dirty_gl_viewport = TRUE;
 
-  _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
-  _context->last_path = 0;
+  _context->current_path = _cogl_path_new ();
   _context->stencil_material = cogl_material_new ();
 
   _context->in_begin_gl_block = FALSE;
@@ -168,8 +168,8 @@ _cogl_destroy_context ()
 
   _cogl_free_framebuffer_stack (_context->framebuffer_stack);
 
-  if (_context->path_nodes)
-    g_array_free (_context->path_nodes, TRUE);
+  if (_context->current_path)
+    cogl_handle_unref (_context->current_path);
 
   if (_context->default_gl_texture_2d_tex)
     cogl_handle_unref (_context->default_gl_texture_2d_tex);
index c745dae..a8b3e5e 100644 (file)
@@ -99,12 +99,7 @@ typedef struct
   gboolean          dirty_gl_viewport;
 
   /* Primitives */
-  floatVec2         path_start;
-  floatVec2         path_pen;
-  GArray           *path_nodes;
-  unsigned int      last_path;
-  floatVec2         path_nodes_min;
-  floatVec2         path_nodes_max;
+  CoglHandle        current_path;
   CoglHandle        stencil_material;
 
   /* Pre-generated VBOs containing indices to generate GL_TRIANGLES
index 3cf7c17..5783f02 100644 (file)
 #include "cogl.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
 {
   COGL_FRONT_WINDING_CLOCKWISE,
diff --git a/clutter/cogl/cogl/cogl-path-private.h b/clutter/cogl/cogl/cogl-path-private.h
new file mode 100644 (file)
index 0000000..a7c9d39
--- /dev/null
@@ -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 */
index 1255a18..1a75558 100644 (file)
@@ -31,6 +31,7 @@
 #include "cogl-journal-private.h"
 #include "cogl-material-private.h"
 #include "cogl-framebuffer-private.h"
+#include "cogl-path-private.h"
 
 #include <string.h>
 #include <math.h>
 #define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
 #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
 _cogl_path_add_node (gboolean new_sub_path,
                     float x,
                     float y)
 {
   CoglPathNode new_node;
+  CoglPath *path;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  path = COGL_PATH (ctx->current_path);
+
+  _cogl_path_modify (path);
+
   new_node.x = x;
   new_node.y = y;
   new_node.path_size = 0;
 
-  if (new_sub_path || ctx->path_nodes->len == 0)
-    ctx->last_path = ctx->path_nodes->len;
+  if (new_sub_path || path->path_size == 0)
+    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;
-      ctx->path_nodes_min.y = ctx->path_nodes_max.y = y;
+      path->path_nodes_min.x = path->path_nodes_max.x = x;
+      path->path_nodes_min.y = path->path_nodes_max.y = y;
     }
   else
     {
-      if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x;
-      if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x;
-      if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y;
-      if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y;
+      if (x < path->path_nodes_min.x) path->path_nodes_min.x = x;
+      if (x > path->path_nodes_max.x) path->path_nodes_max.x = x;
+      if (y < path->path_nodes_min.y) path->path_nodes_min.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 long  enable_flags = COGL_ENABLE_VERTEX_ARRAY;
+  CoglPath      *path;
   CoglMaterialFlushOptions options;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  path = COGL_PATH (ctx->current_path);
+
   _cogl_journal_flush ();
 
   /* 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);
 
-  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);
 
-      GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode),
-                           (guint8 *) path
-                           + G_STRUCT_OFFSET (CoglPathNode, x)) );
-      GE( glDrawArrays (GL_LINE_STRIP, 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_LINE_STRIP, 0,
+                        MIN (node->path_size, path->path_size - path_start)) );
 
-      path_start += path->path_size;
+      path_start += node->path_size;
     }
 }
 
@@ -129,12 +184,9 @@ _cogl_path_get_bounds (floatVec2 nodes_min,
 }
 
 void
-_cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
-                                  floatVec2 nodes_max,
-                                  unsigned int  path_size,
-                                  CoglPathNode *path,
-                                  gboolean      merge,
-                                  gboolean      need_clear)
+_cogl_add_path_to_stencil_buffer (CoglHandle path_handle,
+                                  gboolean   merge,
+                                  gboolean   need_clear)
 {
   unsigned int     path_start = 0;
   unsigned int     sub_path_num = 0;
@@ -150,10 +202,12 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
     _cogl_framebuffer_get_modelview_stack (framebuffer);
   CoglMatrixStack *projection_stack =
     _cogl_framebuffer_get_projection_stack (framebuffer);
-
+  CoglPath        *path;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  path = COGL_PATH (path_handle);
+
   /* We don't track changes to the stencil buffer in the journal
    * so we need to flush any batched geometry first */
   _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_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);
 
   GE( glEnable (GL_STENCIL_TEST) );
@@ -222,12 +276,17 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
     }
   ctx->n_texcoord_arrays_enabled = 0;
 
-  while (path_start < path_size)
+  while (path_start < path->path_size)
     {
-      GE (glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode),
-                           (guint8 *) path
-                           + G_STRUCT_OFFSET (CoglPathNode, x)));
-      GE (glDrawArrays (GL_TRIANGLE_FAN, 0, path->path_size));
+      CoglPathNode *node =
+        &g_array_index (path->path_nodes, CoglPathNode, path_start);
+
+      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)
         {
@@ -251,8 +310,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
 
       GE (glStencilMask (merge ? 4 : 2));
 
-      path_start += path->path_size;
-      path += path->path_size;
+      path_start += node->path_size;
       sub_path_num++;
     }
 
@@ -488,6 +546,7 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path,
 static void
 _cogl_path_fill_nodes (void)
 {
+  CoglPath *path;
   float bounds_x;
   float bounds_y;
   float bounds_w;
@@ -495,7 +554,9 @@ _cogl_path_fill_nodes (void)
 
   _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);
 
   if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_FORCE_SCANLINE_PATHS)) &&
@@ -509,11 +570,7 @@ _cogl_path_fill_nodes (void)
       framebuffer = _cogl_get_framebuffer ();
       clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
-      _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min,
-                                        ctx->path_nodes_max,
-                                        ctx->path_nodes->len,
-                                        &g_array_index (ctx->path_nodes,
-                                                        CoglPathNode, 0),
+      _cogl_add_path_to_stencil_buffer (ctx->current_path,
                                         clip_state->stencil_used,
                                         FALSE);
 
@@ -535,17 +592,21 @@ _cogl_path_fill_nodes (void)
     {
       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);
 
-          _cogl_path_fill_nodes_scanlines (path,
-                                           path->path_size,
+          /* 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 */
+          _cogl_path_fill_nodes_scanlines (node,
+                                           MIN (node->path_size,
+                                                path->path_size - path_start),
                                            bounds_x, bounds_y,
                                            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);
 
-  if (ctx->path_nodes->len == 0)
+  if (COGL_PATH (ctx->current_path)->path_size == 0)
     return;
 
   _cogl_path_fill_nodes ();
@@ -582,7 +643,7 @@ cogl_path_stroke_preserve (void)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  if (ctx->path_nodes->len == 0)
+  if (COGL_PATH (ctx->current_path)->path_size == 0)
     return;
 
   _cogl_path_stroke_nodes ();
@@ -592,57 +653,77 @@ void
 cogl_path_move_to (float x,
                    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);
 
-  ctx->path_start.x = x;
-  ctx->path_start.y = y;
+  path = COGL_PATH (ctx->current_path);
+
+  path->path_start.x = x;
+  path->path_start.y = y;
 
-  ctx->path_pen = ctx->path_start;
+  path->path_pen = path->path_start;
 }
 
 void
 cogl_path_rel_move_to (float x,
                        float y)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  cogl_path_move_to (ctx->path_pen.x + x,
-                     ctx->path_pen.y + y);
+  path = COGL_PATH (ctx->current_path);
+
+  cogl_path_move_to (path->path_pen.x + x,
+                     path->path_pen.y + y);
 }
 
 void
 cogl_path_line_to (float x,
                    float y)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
   _cogl_path_add_node (FALSE, x, y);
 
-  ctx->path_pen.x = x;
-  ctx->path_pen.y = y;
+  path = COGL_PATH (ctx->current_path);
+
+  path->path_pen.x = x;
+  path->path_pen.y = y;
 }
 
 void
 cogl_path_rel_line_to (float x,
                        float y)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  cogl_path_line_to (ctx->path_pen.x + x,
-                     ctx->path_pen.y + y);
+  path = COGL_PATH (ctx->current_path);
+
+  cogl_path_line_to (path->path_pen.x + x,
+                     path->path_pen.y + y);
 }
 
 void
 cogl_path_close (void)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  _cogl_path_add_node (FALSE, ctx->path_start.x, ctx->path_start.y);
-  ctx->path_pen = ctx->path_start;
+  path = COGL_PATH (ctx->current_path);
+
+  _cogl_path_add_node (FALSE, path->path_start.x,
+                       path->path_start.y);
+
+  path->path_pen = path->path_start;
 }
 
 void
@@ -650,7 +731,8 @@ cogl_path_new (void)
 {
   _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
@@ -789,10 +871,14 @@ cogl_path_arc_rel (float center_x,
                   float angle_2,
                   float angle_step)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  _cogl_path_arc (ctx->path_pen.x + center_x,
-                 ctx->path_pen.y + center_y,
+  path = COGL_PATH (ctx->current_path);
+
+  _cogl_path_arc (path->path_pen.x + center_x,
+                 path->path_pen.y + center_y,
                  radius_x,   radius_y,
                  angle_1,    angle_2,
                  angle_step, 0 /* no move */);
@@ -825,11 +911,14 @@ cogl_path_round_rectangle (float x_1,
                            float radius,
                            float arc_step)
 {
+  CoglPath *path;
   float inner_width = x_2 - x_1 - radius * 2;
   float inner_height = y_2 - y_1 - radius * 2;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  path = COGL_PATH (ctx->current_path);
+
   cogl_path_move_to (x_1, y_1 + radius);
   cogl_path_arc_rel (radius, 0,
                     radius, radius,
@@ -837,16 +926,16 @@ cogl_path_round_rectangle (float x_1,
                     270,
                     arc_step);
 
-  cogl_path_line_to       (ctx->path_pen.x + inner_width,
-                           ctx->path_pen.y);
+  cogl_path_line_to       (path->path_pen.x + inner_width,
+                           path->path_pen.y);
   cogl_path_arc_rel       (0, radius,
                           radius, radius,
                           -90,
                           0,
                           arc_step);
 
-  cogl_path_line_to       (ctx->path_pen.x,
-                           ctx->path_pen.y + inner_height);
+  cogl_path_line_to       (path->path_pen.x,
+                           path->path_pen.y + inner_height);
 
   cogl_path_arc_rel       (-radius, 0,
                           radius, radius,
@@ -854,8 +943,8 @@ cogl_path_round_rectangle (float x_1,
                           90,
                           arc_step);
 
-  cogl_path_line_to       (ctx->path_pen.x - inner_width,
-                           ctx->path_pen.y);
+  cogl_path_line_to       (path->path_pen.x - inner_width,
+                           path->path_pen.y);
   cogl_path_arc_rel       (0, -radius,
                           radius, radius,
                           90,
@@ -970,11 +1059,14 @@ cogl_path_curve_to (float x_1,
                     float y_3)
 {
   CoglBezCubic cubic;
+  CoglPath *path;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  path = COGL_PATH (ctx->current_path);
+
   /* Prepare cubic curve */
-  cubic.p1 = ctx->path_pen;
+  cubic.p1 = path->path_pen;
   cubic.p2.x = x_1;
   cubic.p2.y = y_1;
   cubic.p3.x = x_2;
@@ -987,7 +1079,7 @@ cogl_path_curve_to (float x_1,
 
   /* Add last point */
   _cogl_path_add_node (FALSE, cubic.p4.x, cubic.p4.y);
-  ctx->path_pen = cubic.p4;
+  path->path_pen = cubic.p4;
 }
 
 void
@@ -998,16 +1090,85 @@ cogl_path_rel_curve_to (float x_1,
                         float x_3,
                         float y_3)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  cogl_path_curve_to (ctx->path_pen.x + x_1,
-                      ctx->path_pen.y + y_1,
-                      ctx->path_pen.x + x_2,
-                      ctx->path_pen.y + y_2,
-                      ctx->path_pen.x + x_3,
-                      ctx->path_pen.y + y_3);
+  path = COGL_PATH (ctx->current_path);
+
+  cogl_path_curve_to (path->path_pen.x + x_1,
+                      path->path_pen.y + y_1,
+                      path->path_pen.x + x_2,
+                      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
  * be re-enabled:
@@ -1086,9 +1247,12 @@ cogl_path_curve2_to (float x_1,
                      float x_2,
                      float y_2)
 {
+  CoglPath *path;
+  CoglBezQuad quad;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  CoglBezQuad quad;
+  path = COGL_PATH (ctx->current_path);
 
   /* Prepare quadratic curve */
   quad.p1 = ctx->path_pen;
@@ -1102,7 +1266,7 @@ cogl_path_curve2_to (float x_1,
 
   /* Add last point */
   _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y);
-  ctx->path_pen = quad.p3;
+  path->path_pen = quad.p3;
 }
 
 void
@@ -1111,11 +1275,16 @@ cogl_rel_curve2_to (float x_1,
                     float x_2,
                     float y_2)
 {
+  CoglPath *path;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  cogl_path_curve2_to (ctx->path_pen.x + x_1,
-                       ctx->path_pen.y + y_1,
-                       ctx->path_pen.x + x_2,
-                       ctx->path_pen.y + y_2);
+  path = COGL_PATH (ctx->current_path);
+
+  cogl_path_curve2_to (path->path_pen.x + x_1,
+                       path->path_pen.y + y_1,
+                       path->path_pen.x + x_2,
+                       path->path_pen.y + y_2);
 }
+
 #endif
index 5f8d2a1..4037cc3 100644 (file)
@@ -341,6 +341,52 @@ cogl_path_round_rectangle (float x_1,
                            float radius,
                            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
 
 #endif /* __COGL_PATH_H__ */
index cde92d5..92c812c 100644 (file)
@@ -155,6 +155,9 @@ cogl_polygon
 
 <SUBSECTION>
 cogl_path_new
+cogl_path_get
+cogl_path_set
+cogl_path_copy
 cogl_path_move_to
 cogl_path_close
 cogl_path_line_to