[gtk/wip/matthiasc/lottie-stroke: 31/60] path: Split GskPathBuilder into its own file
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/matthiasc/lottie-stroke: 31/60] path: Split GskPathBuilder into its own file
- Date: Mon, 30 Nov 2020 19:40:19 +0000 (UTC)
commit a431d53d2935da8da73f9589855972f93e191a9c
Author: Benjamin Otte <otte redhat com>
Date: Sat Nov 28 05:36:53 2020 +0100
path: Split GskPathBuilder into its own file
... and add missing API docs.
gsk/gsk.h | 1 +
gsk/gskpath.c | 630 +++++++++++----------------------------------------
gsk/gskpath.h | 48 ----
gsk/gskpathbuilder.c | 469 ++++++++++++++++++++++++++++++++++++++
gsk/gskpathbuilder.h | 80 +++++++
gsk/gskpathmeasure.c | 13 +-
gsk/gskpathprivate.h | 48 +++-
gsk/gsktypes.h | 1 +
gsk/meson.build | 2 +
9 files changed, 724 insertions(+), 568 deletions(-)
---
diff --git a/gsk/gsk.h b/gsk/gsk.h
index 9b9ddaceae..b26fdc66be 100644
--- a/gsk/gsk.h
+++ b/gsk/gsk.h
@@ -23,6 +23,7 @@
#include <gsk/gskenums.h>
#include <gsk/gskglshader.h>
#include <gsk/gskpath.h>
+#include <gsk/gskpathbuilder.h>
#include <gsk/gskpathmeasure.h>
#include <gsk/gskrenderer.h>
#include <gsk/gskrendernode.h>
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 1f34eb7e20..9dd0b5be4b 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -21,13 +21,14 @@
#include "gskpathprivate.h"
+#include "gskpathbuilder.h"
#include "gsksplineprivate.h"
/**
* SECTION:gskpath
* @Title: Path
* @Short_description: Lines and Curves
- * @See_also: #GskRenderNode
+ * @See_also: #GskRenderNode, #GskPathBuilder
*
* This section describes the #GskPath structure that is used to
* describe lines and curves that are more complex than simple rectangles.
@@ -37,15 +38,6 @@
* The #GskPathBuilder structure is meant to help in this endeavor.
*/
-typedef enum
-{
- GSK_PATH_FLAT,
- GSK_PATH_CLOSED
-} GskPathFlags;
-
-typedef struct _GskContour GskContour;
-typedef struct _GskContourClass GskContourClass;
-
struct _GskContour
{
const GskContourClass *klass;
@@ -123,10 +115,6 @@ gsk_contour_get_size_default (const GskContour *contour)
return contour->klass->struct_size;
}
-static GskContour *
-gsk_path_builder_add_contour_by_klass (GskPathBuilder *builder,
- const GskContourClass *klass);
-
static void
gsk_find_point_on_line (const graphene_point_t *a,
const graphene_point_t *b,
@@ -493,20 +481,21 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS =
gsk_rect_contour_add_segment
};
-static void
-gsk_rect_contour_init (GskContour *contour,
- float x,
- float y,
- float width,
- float height)
+GskContour *
+gsk_rect_contour_new (const graphene_rect_t *rect)
{
- GskRectContour *self = (GskRectContour *) contour;
+ GskRectContour *self;
+
+ self = g_new0 (GskRectContour, 1);
self->contour.klass = &GSK_RECT_CONTOUR_CLASS;
- self->x = x;
- self->y = y;
- self->width = width;
- self->height = height;
+
+ self->x = rect->origin.x;
+ self->y = rect->origin.y;
+ self->width = rect->size.width;
+ self->height = rect->size.height;
+
+ return (GskContour *) self;
}
/* CIRCLE CONTOUR */
@@ -743,13 +732,6 @@ gsk_circle_contour_copy (const GskContour *contour,
*target = *self;
}
-static void
-gsk_circle_contour_init (GskContour *contour,
- const graphene_point_t *center,
- float radius,
- float start_angle,
- float end_angle);
-
static void
gsk_circle_contour_add_segment (const GskContour *contour,
GskPathBuilder *builder,
@@ -762,12 +744,10 @@ gsk_circle_contour_add_segment (const GskContour *contour,
float length = self->radius * DEG_TO_RAD (delta);
GskContour *segment;
- segment = gsk_path_builder_add_contour_by_klass (builder, contour->klass);
-
- gsk_circle_contour_init (segment,
- &self->center, self->radius,
- self->start_angle + start/length * delta,
- self->start_angle + end/length * delta);
+ segment = gsk_circle_contour_new (&self->center, self->radius,
+ self->start_angle + start/length * delta,
+ self->start_angle + end/length * delta);
+ gsk_path_builder_add_contour (builder, segment);
}
static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
@@ -787,14 +767,17 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
gsk_circle_contour_add_segment
};
-static void
-gsk_circle_contour_init (GskContour *contour,
- const graphene_point_t *center,
- float radius,
- float start_angle,
- float end_angle)
+GskContour *
+gsk_circle_contour_new (const graphene_point_t *center,
+ float radius,
+ float start_angle,
+ float end_angle)
{
- GskCircleContour *self = (GskCircleContour *) contour;
+ GskCircleContour *self;
+
+ self = g_new0 (GskCircleContour, 1);
+
+ self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
g_assert (fabs (start_angle - end_angle) <= 360);
@@ -803,17 +786,12 @@ gsk_circle_contour_init (GskContour *contour,
self->radius = radius;
self->start_angle = start_angle;
self->end_angle = end_angle;
+
+ return (GskContour *) self;
}
/* STANDARD CONTOUR */
-typedef struct _GskStandardOperation GskStandardOperation;
-
-struct _GskStandardOperation {
- GskPathOperation op;
- gsize point; /* index into points array of the start point (last point of previous op) */
-};
-
typedef struct _GskStandardContour GskStandardContour;
struct _GskStandardContour
{
@@ -1433,12 +1411,12 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
* see gsk_standard_contour_compute_size()
*/
static void
-gsk_standard_contour_init (GskContour *contour,
- GskPathFlags flags,
- const GskStandardOperation *ops,
- gsize n_ops,
- const graphene_point_t *points,
- gsize n_points)
+gsk_standard_contour_init (GskContour *contour,
+ GskPathFlags flags,
+ const GskStandardOperation *ops,
+ gsize n_ops,
+ const graphene_point_t *points,
+ gsize n_points)
{
GskStandardContour *self = (GskStandardContour *) contour;
@@ -1452,6 +1430,22 @@ gsk_standard_contour_init (GskContour *contour,
memcpy (self->points, points, sizeof (graphene_point_t) * n_points);
}
+GskContour *
+gsk_standard_contour_new (GskPathFlags flags,
+ const GskStandardOperation *ops,
+ gsize n_ops,
+ const graphene_point_t *points,
+ gsize n_points)
+{
+ GskContour *contour;
+
+ contour = g_malloc0 (gsk_standard_contour_compute_size (n_ops, n_points));
+
+ gsk_standard_contour_init (contour, flags, ops, n_ops, points, n_points);
+
+ return contour;
+}
+
/* CONTOUR */
static gsize
@@ -1460,24 +1454,6 @@ gsk_contour_get_size (const GskContour *contour)
return contour->klass->get_size (contour);
}
-static void
-gsk_contour_copy (GskContour *dest,
- const GskContour *src)
-{
- src->klass->copy (src, dest);
-}
-
-static GskContour *
-gsk_contour_dup (const GskContour *src)
-{
- GskContour *copy;
-
- copy = g_malloc0 (gsk_contour_get_size (src));
- gsk_contour_copy (copy, src);
-
- return copy;
-}
-
static gboolean
gsk_contour_foreach (const GskContour *contour,
float tolerance,
@@ -1546,18 +1522,78 @@ gsk_contour_get_closest_point (GskPath *path,
out_tangent);
}
+void
+gsk_contour_add_segment (const GskContour *self,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end)
+{
+ self->klass->add_segment (self, builder, measure_data, start, end);
+}
+
+static inline void
+gsk_contour_copy (GskContour *dest,
+ const GskContour *src)
+{
+ src->klass->copy (src, dest);
+}
+
+GskContour *
+gsk_contour_dup (const GskContour *src)
+{
+ GskContour *copy;
+
+ copy = g_malloc0 (gsk_contour_get_size (src));
+ gsk_contour_copy (copy, src);
+
+ return copy;
+}
+
/* PATH */
-static GskPath *
-gsk_path_alloc (gsize extra_size)
+GskPath *
+gsk_path_new_from_contours (const GSList *contours)
{
- GskPath *self;
+ GskPath *path;
+ const GSList *l;
+ gsize size;
+ gsize n_contours;
+ guint8 *contour_data;
+ GskPathFlags flags;
- self = g_malloc0 (sizeof (GskPath) + extra_size);
- self->ref_count = 1;
- self->n_contours = 0;
+ flags = GSK_PATH_CLOSED | GSK_PATH_FLAT;
+ size = 0;
+ n_contours = 0;
+ for (l = contours; l; l = l->next)
+ {
+ GskContour *contour = l->data;
- return self;
+ n_contours++;
+ size += sizeof (GskContour *);
+ size += gsk_contour_get_size (contour);
+ flags &= contour->klass->get_flags (contour);
+ }
+
+ path = g_malloc0 (sizeof (GskPath) + size);
+ path->ref_count = 1;
+ path->flags = flags;
+ path->n_contours = n_contours;
+ contour_data = (guint8 *) &path->contours[n_contours];
+ n_contours = 0;
+
+ for (l = contours; l; l = l->next)
+ {
+ GskContour *contour = l->data;
+
+ path->contours[n_contours] = (GskContour *) contour_data;
+ gsk_contour_copy ((GskContour *) contour_data, contour);
+ size = gsk_contour_get_size (contour);
+ contour_data += size;
+ n_contours++;
+ }
+
+ return path;
}
/**
@@ -1652,6 +1688,13 @@ gsk_path_unref (GskPath *self)
g_free (self);
}
+const GskContour *
+gsk_path_get_contour (GskPath *path,
+ gsize i)
+{
+ return path->contours[i];
+}
+
/**
* gsk_path_print:
* @self: a #GskPath
@@ -1890,424 +1933,3 @@ gsk_path_foreach_with_tolerance (GskPath *self,
return TRUE;
}
-/* BUILDER */
-
-/**
- * GskPathBuilder:
- *
- * A #GskPathBuilder struct is an opaque struct. It is meant to
- * not be kept around and only be used to create new #GskPath
- * objects.
- */
-
-struct _GskPathBuilder
-{
- int ref_count;
-
- GSList *contours; /* (reverse) list of already recorded contours */
-
- GskPathFlags flags; /* flags for the current path */
- GArray *ops; /* operations for current contour - size == 0 means no current contour */
- GArray *points; /* points for the operations */
-};
-
-G_DEFINE_BOXED_TYPE (GskPathBuilder,
- gsk_path_builder,
- gsk_path_builder_ref,
- gsk_path_builder_unref)
-
-
-void
-gsk_path_builder_add_contour (GskPathBuilder *builder,
- GskPath *path,
- gsize i)
-{
- GskContour *copy;
-
- copy = gsk_contour_dup (path->contours[i]);
- builder->contours = g_slist_prepend (builder->contours, copy);
-}
-
-void
-gsk_path_builder_add_contour_segment (GskPathBuilder *builder,
- GskPath *path,
- gsize i,
- gpointer measure_data,
- float start,
- float end)
-{
- const GskContour *self = path->contours[i];
-
- self->klass->add_segment (self, builder, measure_data, start, end);
-}
-
-/**
- * gsk_path_builder_new:
- *
- * Create a new #GskPathBuilder object. The resulting builder
- * would create an empty #GskPath. Use addition functions to add
- * types to it.
- *
- * Returns: a new #GskPathBuilder
- **/
-GskPathBuilder *
-gsk_path_builder_new (void)
-{
- GskPathBuilder *builder;
-
- builder = g_slice_new0 (GskPathBuilder);
- builder->ref_count = 1;
-
- builder->ops = g_array_new (FALSE, FALSE, sizeof (GskStandardOperation));
- builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
- return builder;
-}
-
-/**
- * gsk_path_builder_ref:
- * @builder: a #GskPathBuilder
- *
- * Acquires a reference on the given @builder.
- *
- * This function is intended primarily for bindings. #GskPathBuilder objects
- * should not be kept around.
- *
- * Returns: (transfer none): the given #GskPathBuilder with
- * its reference count increased
- */
-GskPathBuilder *
-gsk_path_builder_ref (GskPathBuilder *builder)
-{
- g_return_val_if_fail (builder != NULL, NULL);
- g_return_val_if_fail (builder->ref_count > 0, NULL);
-
- builder->ref_count += 1;
-
- return builder;
-}
-
-static void
-gsk_path_builder_append_current (GskPathBuilder *builder,
- GskPathOperation op,
- gsize n_points,
- const graphene_point_t *points)
-{
- g_assert (builder->ops->len > 0);
- g_assert (builder->points->len > 0);
- g_assert (n_points > 0);
-
- g_array_append_vals (builder->ops, &(GskStandardOperation) { op, builder->points->len - 1 }, 1);
- g_array_append_vals (builder->points, points, n_points);
-}
-
-static void
-gsk_path_builder_end_current (GskPathBuilder *builder)
-{
- GskContour *contour;
-
- if (builder->ops->len == 0)
- return;
-
- contour = g_malloc0 (gsk_standard_contour_compute_size (builder->ops->len, builder->points->len));
- gsk_standard_contour_init (contour,
- 0,
- (GskStandardOperation *) builder->ops->data,
- builder->ops->len,
- (graphene_point_t *) builder->points->data,
- builder->points->len);
- builder->contours = g_slist_prepend (builder->contours, contour);
-
- g_array_set_size (builder->ops, 0);
- g_array_set_size (builder->points, 0);
-}
-
-static void
-gsk_path_builder_clear (GskPathBuilder *builder)
-{
- gsk_path_builder_end_current (builder);
-
- g_slist_free_full (builder->contours, g_free);
- builder->contours = NULL;
-}
-
-/**
- * gsk_path_builder_unref:
- * @builder: a #GskPathBuilder
- *
- * Releases a reference on the given @builder.
- */
-void
-gsk_path_builder_unref (GskPathBuilder *builder)
-{
- g_return_if_fail (builder != NULL);
- g_return_if_fail (builder->ref_count > 0);
-
- builder->ref_count -= 1;
-
- if (builder->ref_count > 0)
- return;
-
- gsk_path_builder_clear (builder);
- g_array_unref (builder->ops);
- g_array_unref (builder->points);
- g_slice_free (GskPathBuilder, builder);
-}
-
-/**
- * gsk_path_builder_free_to_path: (skip)
- * @builder: a #GskPathBuilder
- *
- * Creates a new #GskPath from the current state of the
- * given @builder, and frees the @builder instance.
- *
- * Returns: (transfer full): the newly created #GskPath
- * with all the contours added to @builder
- */
-GskPath *
-gsk_path_builder_free_to_path (GskPathBuilder *builder)
-{
- GskPath *res;
-
- g_return_val_if_fail (builder != NULL, NULL);
-
- res = gsk_path_builder_to_path (builder);
-
- gsk_path_builder_unref (builder);
-
- return res;
-}
-
-/**
- * gsk_path_builder_to_path:
- * @builder: a #GskPathBuilder
- *
- * Creates a new #GskPath from the given @builder.
- *
- * The given #GskPathBuilder is reset once this function returns;
- * you cannot call this function multiple times on the same @builder instance.
- *
- * This function is intended primarily for bindings. C code should use
- * gsk_path_builder_free_to_path().
- *
- * Returns: (transfer full): the newly created #GskPath
- * with all the contours added to @builder
- */
-GskPath *
-gsk_path_builder_to_path (GskPathBuilder *builder)
-{
- GskPath *path;
- GSList *l;
- gsize size;
- gsize n_contours;
- guint8 *contour_data;
- GskPathFlags flags;
-
- g_return_val_if_fail (builder != NULL, NULL);
-
- gsk_path_builder_end_current (builder);
-
- builder->contours = g_slist_reverse (builder->contours);
- flags = GSK_PATH_CLOSED | GSK_PATH_FLAT;
- size = 0;
- n_contours = 0;
- for (l = builder->contours; l; l = l->next)
- {
- GskContour *contour = l->data;
-
- n_contours++;
- size += sizeof (GskContour *);
- size += gsk_contour_get_size (contour);
- flags &= contour->klass->get_flags (contour);
- }
-
- path = gsk_path_alloc (size);
- path->flags = flags;
- path->n_contours = n_contours;
- contour_data = (guint8 *) &path->contours[n_contours];
- n_contours = 0;
-
- for (l = builder->contours; l; l = l->next)
- {
- GskContour *contour = l->data;
-
- path->contours[n_contours] = (GskContour *) contour_data;
- gsk_contour_copy ((GskContour *) contour_data, contour);
- size = gsk_contour_get_size (contour);
- contour_data += size;
- n_contours++;
- }
-
- gsk_path_builder_clear (builder);
-
- return path;
-}
-
-/**
- * gsk_path_builder_add_path:
- * @builder: a #GskPathBuilder
- * @path: (transfer none): the path to append
- *
- * Appends all of @path to @builder.
- **/
-void
-gsk_path_builder_add_path (GskPathBuilder *builder,
- GskPath *path)
-{
- gsize i;
-
- g_return_if_fail (builder != NULL);
- g_return_if_fail (path != NULL);
-
- for (i = 0; i < path->n_contours; i++)
- {
- gsk_path_builder_add_contour (builder, path, i);
- }
-}
-
-static GskContour *
-gsk_path_builder_add_contour_by_klass (GskPathBuilder *builder,
- const GskContourClass *klass)
-{
- GskContour *contour;
-
- gsk_path_builder_end_current (builder);
-
- contour = g_malloc0 (klass->struct_size);
- builder->contours = g_slist_prepend (builder->contours, contour);
-
- return contour;
-}
-
-/**
- * gsk_path_builder_add_rect:
- * @builder: A #GskPathBuilder
- * @rect: The rectangle to create a path for
- *
- * Creates a path representing the given rectangle.
- *
- * If the width or height of the rectangle is negative, the start
- * point will be on the right or bottom, respectively.
- *
- * If the the width or height are 0, the path will be a closed
- * horizontal or vertical line. If both are 0, it'll be a closed dot.
- *
- * Returns: a new #GskPath representing a rectangle
- **/
-void
-gsk_path_builder_add_rect (GskPathBuilder *builder,
- const graphene_rect_t *rect)
-{
- GskContour *contour;
-
- g_return_if_fail (builder != NULL);
-
- contour = gsk_path_builder_add_contour_by_klass (builder, &GSK_RECT_CONTOUR_CLASS);
- gsk_rect_contour_init (contour,
- rect->origin.x, rect->origin.y,
- rect->size.width, rect->size.height);
-}
-
-/**
- * gsk_path_builder_add_circle:
- * @builder: a #GskPathBuilder
- * @center: the center of the circle
- * @radius: the radius of the circle
- *
- * Adds a circle with the @center and @radius.
- **/
-void
-gsk_path_builder_add_circle (GskPathBuilder *builder,
- const graphene_point_t *center,
- float radius)
-{
- GskContour *contour;
-
- g_return_if_fail (builder != NULL);
- g_return_if_fail (center != NULL);
- g_return_if_fail (radius > 0);
-
- contour = gsk_path_builder_add_contour_by_klass (builder, &GSK_CIRCLE_CONTOUR_CLASS);
- gsk_circle_contour_init (contour, center, radius, 0, 360);
-}
-
-void
-gsk_path_builder_move_to (GskPathBuilder *builder,
- float x,
- float y)
-{
- g_return_if_fail (builder != NULL);
-
- gsk_path_builder_end_current (builder);
-
- builder->flags = GSK_PATH_FLAT;
- g_array_append_vals (builder->ops, &(GskStandardOperation) { GSK_PATH_MOVE, 0 }, 1);
- g_array_append_val (builder->points, GRAPHENE_POINT_INIT(x, y));
-}
-
-void
-gsk_path_builder_line_to (GskPathBuilder *builder,
- float x,
- float y)
-{
- g_return_if_fail (builder != NULL);
-
- if (builder->ops->len == 0)
- {
- gsk_path_builder_move_to (builder, x, y);
- return;
- }
-
- /* skip the line if it goes to the same point */
- if (graphene_point_equal (&g_array_index (builder->points, graphene_point_t, builder->points->len - 1),
- &GRAPHENE_POINT_INIT (x, y)))
- return;
-
- gsk_path_builder_append_current (builder,
- GSK_PATH_LINE,
- 1, (graphene_point_t[1]) {
- GRAPHENE_POINT_INIT (x, y)
- });
-}
-
-void
-gsk_path_builder_curve_to (GskPathBuilder *builder,
- float x1,
- float y1,
- float x2,
- float y2,
- float x3,
- float y3)
-{
- g_return_if_fail (builder != NULL);
-
- if (builder->ops->len == 0)
- gsk_path_builder_move_to (builder, x1, y1);
-
- builder->flags ^= ~GSK_PATH_FLAT;
- gsk_path_builder_append_current (builder,
- GSK_PATH_CURVE,
- 3, (graphene_point_t[3]) {
- GRAPHENE_POINT_INIT (x1, y1),
- GRAPHENE_POINT_INIT (x2, y2),
- GRAPHENE_POINT_INIT (x3, y3)
- });
-}
-
-void
-gsk_path_builder_close (GskPathBuilder *builder)
-{
- g_return_if_fail (builder != NULL);
-
- if (builder->ops->len == 0)
- return;
-
- builder->flags |= GSK_PATH_CLOSED;
- gsk_path_builder_append_current (builder,
- GSK_PATH_CLOSE,
- 1, (graphene_point_t[1]) {
- g_array_index (builder->points, graphene_point_t, 0)
- });
-
- gsk_path_builder_end_current (builder);
-}
-
diff --git a/gsk/gskpath.h b/gsk/gskpath.h
index 7c34b3a165..57bdc5bba2 100644
--- a/gsk/gskpath.h
+++ b/gsk/gskpath.h
@@ -79,54 +79,6 @@ gboolean gsk_path_foreach (GskPath
GskPathForeachFunc func,
gpointer user_data);
-#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
-
-typedef struct _GskPathBuilder GskPathBuilder;
-
-GDK_AVAILABLE_IN_ALL
-GType gsk_path_builder_get_type (void) G_GNUC_CONST;
-
-GDK_AVAILABLE_IN_ALL
-GskPathBuilder * gsk_path_builder_new (void);
-GDK_AVAILABLE_IN_ALL
-GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *builder);
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_unref (GskPathBuilder *builder);
-GDK_AVAILABLE_IN_ALL
-GskPath * gsk_path_builder_free_to_path (GskPathBuilder *builder)
G_GNUC_WARN_UNUSED_RESULT;
-GDK_AVAILABLE_IN_ALL
-GskPath * gsk_path_builder_to_path (GskPathBuilder *builder)
G_GNUC_WARN_UNUSED_RESULT;
-
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_add_path (GskPathBuilder *builder,
- GskPath *path);
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_add_rect (GskPathBuilder *builder,
- const graphene_rect_t *rect);
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_add_circle (GskPathBuilder *builder,
- const graphene_point_t *center,
- float radius);
-
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_move_to (GskPathBuilder *builder,
- float x,
- float y);
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_line_to (GskPathBuilder *builder,
- float x,
- float y);
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_curve_to (GskPathBuilder *builder,
- float x1,
- float y1,
- float x2,
- float y2,
- float x3,
- float y3);
-GDK_AVAILABLE_IN_ALL
-void gsk_path_builder_close (GskPathBuilder *builder);
-
G_END_DECLS
#endif /* __GSK_PATH_H__ */
diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c
new file mode 100644
index 0000000000..a84da117fb
--- /dev/null
+++ b/gsk/gskpathbuilder.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright © 2020 Benjamin Otte
+ *
+ * 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.1 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/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "config.h"
+
+#include "gskpathbuilder.h"
+
+#include "gskpathprivate.h"
+
+/**
+ * SECTION:gskpathbuilder
+ * @Title: Building paths
+ * @Short_description: Building paths of lines and curves
+ * @See_also: #GskPath, #GskPathMeasure
+ *
+ * This section describes how to construct #GskPath structures.
+ *
+ * A path is constructed like this:
+ *
+ * |[<!-- language="C" -->
+ * GskPath *
+ * construct_path (void)
+ * {
+ * GskPathBuilder *builder;
+ *
+ * builder = gsk_path_builder_new ();
+ *
+ * // add contours to the path here
+ *
+ * return gsk_path_builder_free_to_path (builder);
+ * ]|
+ *
+ * Adding contours to the path can be done in two ways.
+ * The easiest option is to use the `gsk_path_builder_add_*` group
+ * of functions that add predefined contours to the current path,
+ * either common shapes like gsk_path_builder_add_circle()
+ * or by adding from other paths like gsk_path_builder_add_path().
+ *
+ * The other option is to define each line and curve manually with
+ * the `gsk_path_builder_*_to` group of functions. You start with
+ * a call to gsk_path_builder_move_to() to set the starting point
+ * and then use multiple calls to any of the drawing functions to
+ * move the pen along the plane. Once you are done, you can call
+ * gsk_path_builder_close() to close the path by connecting it
+ * back with a line to the starting point.
+ * This is similar for how paths are drawn in Cairo.
+ */
+
+/**
+ * GskPathBuilder:
+ *
+ * A #GskPathBuilder struct is an opaque struct. It is meant to
+ * not be kept around and only be used to create new #GskPath
+ * objects.
+ */
+
+struct _GskPathBuilder
+{
+ int ref_count;
+
+ GSList *contours; /* (reverse) list of already recorded contours */
+
+ GskPathFlags flags; /* flags for the current path */
+ GArray *ops; /* operations for current contour - size == 0 means no current contour */
+ GArray *points; /* points for the operations */
+};
+
+G_DEFINE_BOXED_TYPE (GskPathBuilder,
+ gsk_path_builder,
+ gsk_path_builder_ref,
+ gsk_path_builder_unref)
+
+
+/**
+ * gsk_path_builder_new:
+ *
+ * Create a new #GskPathBuilder object. The resulting builder
+ * would create an empty #GskPath. Use addition functions to add
+ * types to it.
+ *
+ * Returns: a new #GskPathBuilder
+ **/
+GskPathBuilder *
+gsk_path_builder_new (void)
+{
+ GskPathBuilder *builder;
+
+ builder = g_slice_new0 (GskPathBuilder);
+ builder->ref_count = 1;
+
+ builder->ops = g_array_new (FALSE, FALSE, sizeof (GskStandardOperation));
+ builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
+ return builder;
+}
+
+/**
+ * gsk_path_builder_ref:
+ * @builder: a #GskPathBuilder
+ *
+ * Acquires a reference on the given @builder.
+ *
+ * This function is intended primarily for bindings. #GskPathBuilder objects
+ * should not be kept around.
+ *
+ * Returns: (transfer none): the given #GskPathBuilder with
+ * its reference count increased
+ */
+GskPathBuilder *
+gsk_path_builder_ref (GskPathBuilder *builder)
+{
+ g_return_val_if_fail (builder != NULL, NULL);
+ g_return_val_if_fail (builder->ref_count > 0, NULL);
+
+ builder->ref_count += 1;
+
+ return builder;
+}
+
+static void
+gsk_path_builder_append_current (GskPathBuilder *builder,
+ GskPathOperation op,
+ gsize n_points,
+ const graphene_point_t *points)
+{
+ g_assert (builder->ops->len > 0);
+ g_assert (builder->points->len > 0);
+ g_assert (n_points > 0);
+
+ g_array_append_vals (builder->ops, &(GskStandardOperation) { op, builder->points->len - 1 }, 1);
+ g_array_append_vals (builder->points, points, n_points);
+}
+
+static void
+gsk_path_builder_end_current (GskPathBuilder *builder)
+{
+ GskContour *contour;
+
+ if (builder->ops->len == 0)
+ return;
+
+ contour = gsk_standard_contour_new (builder->flags,
+ (GskStandardOperation *) builder->ops->data,
+ builder->ops->len,
+ (graphene_point_t *) builder->points->data,
+ builder->points->len);
+
+ g_array_set_size (builder->ops, 0);
+ g_array_set_size (builder->points, 0);
+
+ /* do this at the end to avoid inflooping when add_contour calls back here */
+ gsk_path_builder_add_contour (builder, contour);
+}
+
+static void
+gsk_path_builder_clear (GskPathBuilder *builder)
+{
+ gsk_path_builder_end_current (builder);
+
+ g_slist_free_full (builder->contours, g_free);
+ builder->contours = NULL;
+}
+
+/**
+ * gsk_path_builder_unref:
+ * @builder: a #GskPathBuilder
+ *
+ * Releases a reference on the given @builder.
+ */
+void
+gsk_path_builder_unref (GskPathBuilder *builder)
+{
+ g_return_if_fail (builder != NULL);
+ g_return_if_fail (builder->ref_count > 0);
+
+ builder->ref_count -= 1;
+
+ if (builder->ref_count > 0)
+ return;
+
+ gsk_path_builder_clear (builder);
+ g_array_unref (builder->ops);
+ g_array_unref (builder->points);
+ g_slice_free (GskPathBuilder, builder);
+}
+
+/**
+ * gsk_path_builder_free_to_path: (skip)
+ * @builder: a #GskPathBuilder
+ *
+ * Creates a new #GskPath from the current state of the
+ * given @builder, and frees the @builder instance.
+ *
+ * Returns: (transfer full): the newly created #GskPath
+ * with all the contours added to @builder
+ */
+GskPath *
+gsk_path_builder_free_to_path (GskPathBuilder *builder)
+{
+ GskPath *res;
+
+ g_return_val_if_fail (builder != NULL, NULL);
+
+ res = gsk_path_builder_to_path (builder);
+
+ gsk_path_builder_unref (builder);
+
+ return res;
+}
+
+/**
+ * gsk_path_builder_to_path:
+ * @builder: a #GskPathBuilder
+ *
+ * Creates a new #GskPath from the given @builder.
+ *
+ * The given #GskPathBuilder is reset once this function returns;
+ * you cannot call this function multiple times on the same @builder instance.
+ *
+ * This function is intended primarily for bindings. C code should use
+ * gsk_path_builder_free_to_path().
+ *
+ * Returns: (transfer full): the newly created #GskPath
+ * with all the contours added to @builder
+ */
+GskPath *
+gsk_path_builder_to_path (GskPathBuilder *builder)
+{
+ GskPath *path;
+
+ g_return_val_if_fail (builder != NULL, NULL);
+
+ gsk_path_builder_end_current (builder);
+
+ builder->contours = g_slist_reverse (builder->contours);
+
+ path = gsk_path_new_from_contours (builder->contours);
+
+ gsk_path_builder_clear (builder);
+
+ return path;
+}
+
+void
+gsk_path_builder_add_contour (GskPathBuilder *builder,
+ GskContour *contour)
+{
+ gsk_path_builder_end_current (builder);
+
+ builder->contours = g_slist_prepend (builder->contours, contour);
+}
+
+/**
+ * gsk_path_builder_add_path:
+ * @builder: a #GskPathBuilder
+ * @path: (transfer none): the path to append
+ *
+ * Appends all of @path to @builder.
+ **/
+void
+gsk_path_builder_add_path (GskPathBuilder *builder,
+ GskPath *path)
+{
+ gsize i;
+
+ g_return_if_fail (builder != NULL);
+ g_return_if_fail (path != NULL);
+
+ for (i = 0; i < gsk_path_get_n_contours (path); i++)
+ {
+ const GskContour *contour = gsk_path_get_contour (path, i);
+
+ gsk_path_builder_add_contour (builder, gsk_contour_dup (contour));
+ }
+}
+
+/**
+ * gsk_path_builder_add_rect:
+ * @builder: A #GskPathBuilder
+ * @rect: The rectangle to create a path for
+ *
+ * Creates a path representing the given rectangle.
+ *
+ * If the width or height of the rectangle is negative, the start
+ * point will be on the right or bottom, respectively.
+ *
+ * If the the width or height are 0, the path will be a closed
+ * horizontal or vertical line. If both are 0, it'll be a closed dot.
+ *
+ * Returns: a new #GskPath representing a rectangle
+ **/
+void
+gsk_path_builder_add_rect (GskPathBuilder *builder,
+ const graphene_rect_t *rect)
+{
+ GskContour *contour;
+
+ g_return_if_fail (builder != NULL);
+
+ contour = gsk_rect_contour_new (rect);
+ gsk_path_builder_add_contour (builder, contour);
+}
+
+/**
+ * gsk_path_builder_add_circle:
+ * @builder: a #GskPathBuilder
+ * @center: the center of the circle
+ * @radius: the radius of the circle
+ *
+ * Adds a circle with the @center and @radius.
+ **/
+void
+gsk_path_builder_add_circle (GskPathBuilder *builder,
+ const graphene_point_t *center,
+ float radius)
+{
+ GskContour *contour;
+
+ g_return_if_fail (builder != NULL);
+ g_return_if_fail (center != NULL);
+ g_return_if_fail (radius > 0);
+
+ contour = gsk_circle_contour_new (center, radius, 0, 360);
+ gsk_path_builder_add_contour (builder, contour);
+}
+
+/**
+ * gsk_path_builder_move_to:
+ * @builder: a #GskPathBuilder
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Starts a new contour by placing the pen at @x, @y.
+ *
+ * If gsk_path_builder_move_to() is called twice in succession, the first
+ * call will result in a contour made up of a single point. The second call
+ * will start a new contour.
+ **/
+void
+gsk_path_builder_move_to (GskPathBuilder *builder,
+ float x,
+ float y)
+{
+ g_return_if_fail (builder != NULL);
+
+ gsk_path_builder_end_current (builder);
+
+ builder->flags = GSK_PATH_FLAT;
+ g_array_append_vals (builder->ops, &(GskStandardOperation) { GSK_PATH_MOVE, 0 }, 1);
+ g_array_append_val (builder->points, GRAPHENE_POINT_INIT(x, y));
+}
+
+/**
+ * gsk_path_builder_line_to:
+ * @builder: a #GskPathBuilder
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Draws a line from the current point to @x, @y and makes it the new current
+ * point.
+ **/
+void
+gsk_path_builder_line_to (GskPathBuilder *builder,
+ float x,
+ float y)
+{
+ g_return_if_fail (builder != NULL);
+
+ if (builder->ops->len == 0)
+ {
+ gsk_path_builder_move_to (builder, x, y);
+ return;
+ }
+
+ /* skip the line if it goes to the same point */
+ if (graphene_point_equal (&g_array_index (builder->points, graphene_point_t, builder->points->len - 1),
+ &GRAPHENE_POINT_INIT (x, y)))
+ return;
+
+ gsk_path_builder_append_current (builder,
+ GSK_PATH_LINE,
+ 1, (graphene_point_t[1]) {
+ GRAPHENE_POINT_INIT (x, y)
+ });
+}
+
+/**
+ * gsk_path_builder_curve_to:
+ * @builder: a #GskPathBuilder
+ * @x1: x coordinate of first control point
+ * @y1: y coordinate of first control point
+ * @x2: x coordinate of second control point
+ * @y2: y coordinate of second control point
+ * @x3: x coordinate of the end of the curve
+ * @y3: y coordinate of the end of the curve
+ *
+ * Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
+ * from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
+ * points.
+ **/
+void
+gsk_path_builder_curve_to (GskPathBuilder *builder,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3)
+{
+ g_return_if_fail (builder != NULL);
+
+ if (builder->ops->len == 0)
+ gsk_path_builder_move_to (builder, x1, y1);
+
+ builder->flags ^= ~GSK_PATH_FLAT;
+ gsk_path_builder_append_current (builder,
+ GSK_PATH_CURVE,
+ 3, (graphene_point_t[3]) {
+ GRAPHENE_POINT_INIT (x1, y1),
+ GRAPHENE_POINT_INIT (x2, y2),
+ GRAPHENE_POINT_INIT (x3, y3)
+ });
+}
+
+/**
+ * gsk_path_builder_close:
+ * @builder: a #GskPathBuilder
+ *
+ * Ends the current contour with a line back to the start point.
+ *
+ * Note that this is different from calling gsk_path_builder_line_to()
+ * with the start point in that the contour will be closed. A closed
+ * contour behaves different from an open one when stroking its start
+ * and end point are considered connected, so they will be joined
+ * via the line join, and not ended with line caps.
+ **/
+void
+gsk_path_builder_close (GskPathBuilder *builder)
+{
+ g_return_if_fail (builder != NULL);
+
+ if (builder->ops->len == 0)
+ return;
+
+ builder->flags |= GSK_PATH_CLOSED;
+ gsk_path_builder_append_current (builder,
+ GSK_PATH_CLOSE,
+ 1, (graphene_point_t[1]) {
+ g_array_index (builder->points, graphene_point_t, 0)
+ });
+
+ gsk_path_builder_end_current (builder);
+}
+
diff --git a/gsk/gskpathbuilder.h b/gsk/gskpathbuilder.h
new file mode 100644
index 0000000000..2bc7a3933d
--- /dev/null
+++ b/gsk/gskpathbuilder.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2020 Benjamin Otte
+ *
+ * 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.1 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/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#ifndef __GSK_PATH_BUILDER_H__
+#define __GSK_PATH_BUILDER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+GType gsk_path_builder_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GskPathBuilder * gsk_path_builder_new (void);
+GDK_AVAILABLE_IN_ALL
+GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *builder);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_unref (GskPathBuilder *builder);
+GDK_AVAILABLE_IN_ALL
+GskPath * gsk_path_builder_free_to_path (GskPathBuilder *builder)
G_GNUC_WARN_UNUSED_RESULT;
+GDK_AVAILABLE_IN_ALL
+GskPath * gsk_path_builder_to_path (GskPathBuilder *builder)
G_GNUC_WARN_UNUSED_RESULT;
+
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_add_path (GskPathBuilder *builder,
+ GskPath *path);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_add_rect (GskPathBuilder *builder,
+ const graphene_rect_t *rect);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_add_circle (GskPathBuilder *builder,
+ const graphene_point_t *center,
+ float radius);
+
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_move_to (GskPathBuilder *builder,
+ float x,
+ float y);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_line_to (GskPathBuilder *builder,
+ float x,
+ float y);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_curve_to (GskPathBuilder *builder,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_builder_close (GskPathBuilder *builder);
+
+G_END_DECLS
+
+#endif /* __GSK_PATH_BUILDER_H__ */
diff --git a/gsk/gskpathmeasure.c b/gsk/gskpathmeasure.c
index 3b808cde2d..38cc793faf 100644
--- a/gsk/gskpathmeasure.c
+++ b/gsk/gskpathmeasure.c
@@ -416,12 +416,11 @@ gsk_path_measure_add_segment (GskPathMeasure *self,
else if (start > 0 || end < self->measures[i].length)
{
float len = MIN (end, self->measures[i].length);
- gsk_path_builder_add_contour_segment (builder,
- self->path,
- i,
- self->measures[i].contour_data,
- start,
- len);
+ gsk_contour_add_segment (gsk_path_get_contour (self->path, i),
+ builder,
+ self->measures[i].contour_data,
+ start,
+ len);
end -= len;
start = 0;
if (end <= 0)
@@ -430,7 +429,7 @@ gsk_path_measure_add_segment (GskPathMeasure *self,
else
{
end -= self->measures[i].length;
- gsk_path_builder_add_contour (builder, self->path, i);
+ gsk_path_builder_add_contour (builder, gsk_contour_dup (gsk_path_get_contour (self->path, i)));
}
}
}
diff --git a/gsk/gskpathprivate.h b/gsk/gskpathprivate.h
index ed8db4a49d..d987c90fc1 100644
--- a/gsk/gskpathprivate.h
+++ b/gsk/gskpathprivate.h
@@ -28,12 +28,44 @@ G_BEGIN_DECLS
/* Same as Skia, so looks like a good value. ¯\_(ツ)_/¯ */
#define GSK_PATH_TOLERANCE_DEFAULT (0.5)
+typedef enum
+{
+ GSK_PATH_FLAT,
+ GSK_PATH_CLOSED
+} GskPathFlags;
+
+typedef struct _GskContour GskContour;
+typedef struct _GskContourClass GskContourClass;
+
+typedef struct _GskStandardOperation GskStandardOperation;
+
+struct _GskStandardOperation {
+ GskPathOperation op;
+ gsize point; /* index into points array of the start point (last point of previous op) */
+};
+
+GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
+GskContour * gsk_circle_contour_new (const graphene_point_t *center,
+ float radius,
+ float start_angle,
+ float end_angle);
+GskContour * gsk_standard_contour_new (GskPathFlags flags,
+ const
GskStandardOperation *ops,
+ gsize n_ops,
+ const graphene_point_t
*points,
+ gsize n_points);
+
+GskPath * gsk_path_new_from_contours (const GSList *contours);
+
gsize gsk_path_get_n_contours (GskPath *path);
+const GskContour * gsk_path_get_contour (GskPath *path,
+ gsize i);
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
double tolerance,
GskPathForeachFunc func,
gpointer user_data);
+GskContour * gsk_contour_dup (const GskContour *src);
gpointer gsk_contour_init_measure (GskPath *path,
gsize i,
float tolerance,
@@ -57,16 +89,14 @@ gboolean gsk_contour_get_closest_point (GskPath
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
+void gsk_contour_add_segment (const GskContour *self,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end);
-void gsk_path_builder_add_contour (GskPathBuilder *builder,
- GskPath *path,
- gsize i);
-void gsk_path_builder_add_contour_segment (GskPathBuilder *builder,
- GskPath *path,
- gsize i,
- gpointer measure_data,
- float start,
- float end);
+void gsk_path_builder_add_contour (GskPathBuilder *builder,
+ GskContour *contour);
G_END_DECLS
diff --git a/gsk/gsktypes.h b/gsk/gsktypes.h
index d31e5d9be4..345847a6ed 100644
--- a/gsk/gsktypes.h
+++ b/gsk/gsktypes.h
@@ -27,6 +27,7 @@
#include <gsk/gskenums.h>
typedef struct _GskPath GskPath;
+typedef struct _GskPathBuilder GskPathBuilder;
typedef struct _GskPathMeasure GskPathMeasure;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskStroke GskStroke;
diff --git a/gsk/meson.build b/gsk/meson.build
index a40b7b6e70..f4edb9b49b 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -24,6 +24,7 @@ gsk_public_sources = files([
'gskcairorenderer.c',
'gskglshader.c',
'gskpath.c',
+ 'gskpathbuilder.c',
'gskpathmeasure.c',
'gskrenderer.c',
'gskrendernode.c',
@@ -58,6 +59,7 @@ gsk_public_headers = files([
'gskenums.h',
'gskglshader.h',
'gskpath.h',
+ 'gskpathbuilder.h',
'gskpathmeasure.h',
'gskrenderer.h',
'gskrendernode.h',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]