path: Split GskPathBuilder into its own file

... and add missing API docs.
This commit is contained in:
Benjamin Otte
2020-11-28 05:36:53 +01:00
parent 11dbab714a
commit 42c7490b05
9 changed files with 724 additions and 568 deletions

View File

@@ -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>

View File

@@ -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,
@@ -492,20 +480,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 */
@@ -740,13 +729,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,
@@ -759,12 +741,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 =
@@ -784,14 +764,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);
@@ -800,17 +783,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
{
@@ -1430,12 +1408,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;
@@ -1449,6 +1427,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
@@ -1457,24 +1451,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,
@@ -1543,18 +1519,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;
}
/**
@@ -1649,6 +1685,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
@@ -1880,424 +1923,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);
}

View File

@@ -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__ */

469
gsk/gskpathbuilder.c Normal file
View File

@@ -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
* point.
**/
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);
}

80
gsk/gskpathbuilder.h Normal file
View File

@@ -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__ */

View File

@@ -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)));
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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',