path: Introduce gskpathop

A gskpathop is a pointer to a graphene_point_t* with the low bits used
to encode the GskPathOperation. It's an easy way to introduce API for
operations.

So far it's just used to replace GskStandardOperation.
This commit is contained in:
Benjamin Otte
2020-12-04 17:02:42 +01:00
parent 678405bd22
commit 23c5318de1
4 changed files with 256 additions and 76 deletions

View File

@@ -871,7 +871,7 @@ struct _GskStandardContour
gsize n_ops;
gsize n_points;
graphene_point_t *points;
GskStandardOperation ops[];
gskpathop ops[];
};
static gsize
@@ -879,7 +879,7 @@ gsk_standard_contour_compute_size (gsize n_ops,
gsize n_points)
{
return sizeof (GskStandardContour)
+ sizeof (GskStandardOperation) * n_ops
+ sizeof (gskpathop) * n_ops
+ sizeof (graphene_point_t) * n_points;
}
@@ -899,30 +899,11 @@ gsk_standard_contour_foreach (const GskContour *contour,
{
const GskStandardContour *self = (const GskStandardContour *) contour;
gsize i;
const gsize n_points[] = {
[GSK_PATH_MOVE] = 1,
[GSK_PATH_CLOSE] = 2,
[GSK_PATH_LINE] = 2,
[GSK_PATH_CURVE] = 4
};
for (i = 0; i < self->n_ops; i ++)
{
if (self->ops[i].op == GSK_PATH_CONIC)
{
graphene_point_t pts[3] = { self->points[self->ops[i].point],
self->points[self->ops[i].point + 1],
self->points[self->ops[i].point + 3] };
float weight = self->points[self->ops[i].point + 2].x;
if (!func (GSK_PATH_CONIC, pts, 3, weight, user_data))
return FALSE;
}
else
{
if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], 0, user_data))
return FALSE;
}
if (!gsk_pathop_foreach (self->ops[i], func, user_data))
return FALSE;
}
return TRUE;
@@ -945,9 +926,9 @@ gsk_standard_contour_print (const GskContour *contour,
for (i = 0; i < self->n_ops; i ++)
{
graphene_point_t *pt = &self->points[self->ops[i].point];
const graphene_point_t *pt = gsk_pathop_points (self->ops[i]);
switch (self->ops[i].op)
switch (gsk_pathop_op (self->ops[i]))
{
case GSK_PATH_MOVE:
g_string_append (string, "M ");
@@ -1102,9 +1083,9 @@ gsk_standard_contour_init_measure (const GskContour *contour,
for (i = 1; i < self->n_ops; i ++)
{
graphene_point_t *pt = &self->points[self->ops[i].point];
const graphene_point_t *pt = gsk_pathop_points (self->ops[i]);
switch (self->ops[i].op)
switch (gsk_pathop_op (self->ops[i]))
{
case GSK_PATH_MOVE:
break;
@@ -1184,8 +1165,8 @@ gsk_standard_contour_measure_get_point (GskStandardContour *self,
{
const graphene_point_t *pts;
pts = &self->points[self->ops[op].point];
switch (self->ops[op].op)
pts = gsk_pathop_points (self->ops[op]);
switch (gsk_pathop_op (self->ops[op]))
{
case GSK_PATH_LINE:
case GSK_PATH_CLOSE:
@@ -1229,7 +1210,7 @@ gsk_standard_contour_get_point (const GskContour *contour,
if (array->len == 0)
{
g_assert (distance == 0);
g_assert (self->ops[0].op == GSK_PATH_MOVE);
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
if (pos)
*pos = self->points[0];
if (tangent)
@@ -1266,7 +1247,7 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
gsize i;
gboolean result = FALSE;
g_assert (self->ops[0].op == GSK_PATH_MOVE);
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
last_point = self->points[0];
if (array->len == 0)
@@ -1338,12 +1319,13 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
}
static void
gsk_standard_contour_init (GskContour *contour,
GskPathFlags flags,
const GskStandardOperation *ops,
gsize n_ops,
gsk_standard_contour_init (GskContour *contour,
GskPathFlags flags,
const graphene_point_t *points,
gsize n_points);
gsize n_points,
const gskpathop *ops,
gsize n_ops,
ptrdiff_t offset);
static void
gsk_standard_contour_copy (const GskContour *contour,
@@ -1351,7 +1333,7 @@ gsk_standard_contour_copy (const GskContour *contour,
{
const GskStandardContour *self = (const GskStandardContour *) contour;
gsk_standard_contour_init (dest, self->flags, self->ops, self->n_ops, self->points, self->n_points);
gsk_standard_contour_init (dest, self->flags, self->points, self->n_points, self->ops, self->n_ops, 0);
}
static void
@@ -1400,12 +1382,12 @@ gsk_standard_contour_add_segment (const GskContour *contour,
* taking care that first and last operation might be identical */
if (start_measure)
{
switch (self->ops[start_measure->op].op)
switch (gsk_pathop_op (self->ops[start_measure->op]))
{
case GSK_PATH_CLOSE:
case GSK_PATH_LINE:
{
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
const graphene_point_t *pts = gsk_pathop_points (self->ops[start_measure->op]);
graphene_point_t point;
graphene_point_interpolate (&pts[0], &pts[1], start_progress, &point);
@@ -1422,7 +1404,7 @@ gsk_standard_contour_add_segment (const GskContour *contour,
case GSK_PATH_CURVE:
{
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
const graphene_point_t *pts = gsk_pathop_points (self->ops[start_measure->op]);
graphene_point_t curve[4], discard[4];
gsk_spline_split_cubic (pts, discard, curve, start_progress);
@@ -1441,7 +1423,7 @@ gsk_standard_contour_add_segment (const GskContour *contour,
case GSK_PATH_CONIC:
{
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
const graphene_point_t *pts = gsk_pathop_points (self->ops[start_measure->op]);
graphene_point_t curve[4], discard[4];
gsk_spline_split_conic (pts, discard, curve, start_progress);
@@ -1470,9 +1452,9 @@ gsk_standard_contour_add_segment (const GskContour *contour,
for (; i < (end_measure ? end_measure->op : self->n_ops); i++)
{
graphene_point_t *pt = &self->points[self->ops[i].point];
const graphene_point_t *pt = gsk_pathop_points (self->ops[i]);
switch (self->ops[i].op)
switch (gsk_pathop_op (self->ops[i]))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pt[0].x, pt[0].y);
@@ -1500,12 +1482,12 @@ gsk_standard_contour_add_segment (const GskContour *contour,
/* Add the last partial operation */
if (end_measure)
{
switch (self->ops[end_measure->op].op)
switch (gsk_pathop_op (self->ops[end_measure->op]))
{
case GSK_PATH_CLOSE:
case GSK_PATH_LINE:
{
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
const graphene_point_t *pts = gsk_pathop_points (self->ops[end_measure->op]);
graphene_point_t point;
graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
@@ -1515,7 +1497,7 @@ gsk_standard_contour_add_segment (const GskContour *contour,
case GSK_PATH_CURVE:
{
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
const graphene_point_t *pts = gsk_pathop_points (self->ops[end_measure->op]);
graphene_point_t curve[4], discard[4];
gsk_spline_split_cubic (pts, curve, discard, end_progress);
@@ -1525,7 +1507,7 @@ gsk_standard_contour_add_segment (const GskContour *contour,
case GSK_PATH_CONIC:
{
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
const graphene_point_t *pts = gsk_pathop_points (self->ops[end_measure->op]);
graphene_point_t curve[4], discard[4];
gsk_spline_split_conic (pts, curve, discard, end_progress);
@@ -1637,37 +1619,47 @@ 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 graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset)
{
GskStandardContour *self = (GskStandardContour *) contour;
gsize i;
self->contour.klass = &GSK_STANDARD_CONTOUR_CLASS;
self->flags = flags;
self->n_ops = n_ops;
memcpy (self->ops, ops, sizeof (GskStandardOperation) * n_ops);
self->n_points = n_points;
self->points = (graphene_point_t *) &self->ops[n_ops];
memcpy (self->points, points, sizeof (graphene_point_t) * n_points);
offset += self->points - points;
for (i = 0; i < n_ops; i++)
{
self->ops[i] = gsk_pathop_encode (gsk_pathop_op (ops[i]),
gsk_pathop_points (ops[i]) + offset);
}
}
GskContour *
gsk_standard_contour_new (GskPathFlags flags,
const GskStandardOperation *ops,
gsize n_ops,
const graphene_point_t *points,
gsize n_points)
gsk_standard_contour_new (GskPathFlags flags,
const graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset)
{
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);
gsk_standard_contour_init (contour, flags, points, n_points, ops, n_ops, offset);
return contour;
}

View File

@@ -23,6 +23,8 @@
#include <gskpath.h>
#include "gskpathopprivate.h"
G_BEGIN_DECLS
typedef enum
@@ -33,23 +35,17 @@ typedef enum
typedef struct _GskContour GskContour;
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,
const graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
const graphene_point_t *points,
gsize n_points);
gssize offset);
void gsk_contour_copy (GskContour * dest,
const GskContour *src);

View File

@@ -105,7 +105,7 @@ gsk_path_builder_new (void)
builder = g_slice_new0 (GskPathBuilder);
builder->ref_count = 1;
builder->ops = g_array_new (FALSE, FALSE, sizeof (GskStandardOperation));
builder->ops = g_array_new (FALSE, FALSE, sizeof (gskpathop));
builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
/* Be explicit here */
@@ -137,6 +137,19 @@ gsk_path_builder_ref (GskPathBuilder *builder)
return builder;
}
/* We're cheating here. Out pathops are relative to the NULL pointer,
* so that we can not care about the points GArray reallocating itself
* until we create the contour.
* This does however mean that we need to not use gsk_pathop_get_points()
* without offsetting the returned pointer.
*/
static inline gskpathop
gsk_pathop_encode_index (GskPathOperation op,
gsize index)
{
return gsk_pathop_encode (op, ((graphene_point_t *) NULL) + index);
}
static void
gsk_path_builder_ensure_current (GskPathBuilder *builder)
{
@@ -144,7 +157,7 @@ gsk_path_builder_ensure_current (GskPathBuilder *builder)
return;
builder->flags = GSK_PATH_FLAT;
g_array_append_vals (builder->ops, &(GskStandardOperation) { GSK_PATH_MOVE, 0 }, 1);
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (GSK_PATH_MOVE, 0) }, 1);
g_array_append_val (builder->points, builder->current_point);
}
@@ -156,7 +169,7 @@ gsk_path_builder_append_current (GskPathBuilder *builder,
{
gsk_path_builder_ensure_current (builder);
g_array_append_vals (builder->ops, &(GskStandardOperation) { op, builder->points->len - 1 }, 1);
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (op, builder->points->len - 1) }, 1);
g_array_append_vals (builder->points, points, n_points);
builder->current_point = points[n_points - 1];
@@ -171,10 +184,11 @@ gsk_path_builder_end_current (GskPathBuilder *builder)
return;
contour = gsk_standard_contour_new (builder->flags,
(GskStandardOperation *) builder->ops->data,
builder->ops->len,
(graphene_point_t *) builder->points->data,
builder->points->len);
builder->points->len,
(gskpathop *) builder->ops->data,
builder->ops->len,
(graphene_point_t *) builder->points->data - (graphene_point_t *) NULL);
g_array_set_size (builder->ops, 0);
g_array_set_size (builder->points, 0);

178
gsk/gskpathopprivate.h Normal file
View File

@@ -0,0 +1,178 @@
/*
* 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_PATHOP_PRIVATE_H__
#define __GSK_PATHOP_PRIVATE_H__
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
G_BEGIN_DECLS
typedef gpointer gskpathop;
static inline
gskpathop gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts);
static inline
const graphene_point_t *gsk_pathop_points (gskpathop pop);
static inline
GskPathOperation gsk_pathop_op (gskpathop pop);
static inline
gboolean gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data);
/* included inline so tests can use them */
static inline
void gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op);
static inline
void gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op);
/* IMPLEMENTATION */
#define GSK_PATHOP_OPERATION_MASK (0x7)
static inline gskpathop
gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts)
{
/* g_assert (op & GSK_PATHOP_OPERATION_MASK == op); */
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pts) | op);
}
static inline const graphene_point_t *
gsk_pathop_points (gskpathop pop)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
}
static inline
GskPathOperation gsk_pathop_op (gskpathop pop)
{
return GPOINTER_TO_SIZE (pop) & GSK_PATHOP_OPERATION_MASK;
}
static inline gboolean
gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data)
{
switch (gsk_pathop_op (pop))
{
case GSK_PATH_MOVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 1, 0, user_data);
case GSK_PATH_CLOSE:
case GSK_PATH_LINE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data);
case GSK_PATH_CURVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, 0, user_data);
case GSK_PATH_CONIC:
{
const graphene_point_t *pts = gsk_pathop_points (pop);
return func (gsk_pathop_op (pop), (graphene_point_t[3]) { pts[0], pts[1], pts[3] }, 3, pts[2].x, user_data);
}
default:
g_assert_not_reached ();
return TRUE;
}
}
static inline void
gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (op))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[3].x, pts[3].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
static inline void
gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (op))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_line_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[2].x, pts[2].y, pts[1].x, pts[1].y, pts[0].x, pts[0].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
G_END_DECLS
#endif /* __GSK_PATHOP_PRIVATE_H__ */