summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2020-11-30 19:25:19 +0100
committerBenjamin Otte <otte@redhat.com>2020-12-27 00:31:18 +0100
commit0f332de3d31782e4d5785854d0d11fc256db3693 (patch)
tree7f9c2b517232d4457b2fc82f525d61d9c2b0003c
parent15e8f2bacc45e87c46a7c70972259e71abb6da9f (diff)
downloadgtk+-0f332de3d31782e4d5785854d0d11fc256db3693.tar.gz
path: Split contours into their own file
I'm not sure I want to keep all contours in one file, but for now that's how it is.
-rw-r--r--gsk/gskcontour.c1735
-rw-r--r--gsk/gskcontourprivate.h103
-rw-r--r--gsk/gskpath.c1696
-rw-r--r--gsk/gskpathprivate.h77
-rw-r--r--gsk/meson.build1
5 files changed, 1853 insertions, 1759 deletions
diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c
new file mode 100644
index 0000000000..6c4f2228ff
--- /dev/null
+++ b/gsk/gskcontour.c
@@ -0,0 +1,1735 @@
+/*
+ * 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 "gskcontourprivate.h"
+
+#include "gskpathbuilder.h"
+#include "gskpathprivate.h"
+#include "gsksplineprivate.h"
+
+typedef struct _GskContourClass GskContourClass;
+
+struct _GskContour
+{
+ const GskContourClass *klass;
+};
+
+struct _GskContourClass
+{
+ gsize struct_size;
+ const char *type_name;
+
+ gsize (* get_size) (const GskContour *contour);
+ GskPathFlags (* get_flags) (const GskContour *contour);
+ void (* print) (const GskContour *contour,
+ GString *string);
+ gboolean (* get_bounds) (const GskContour *contour,
+ graphene_rect_t *bounds);
+ void (* get_start_end) (const GskContour *self,
+ graphene_point_t *start,
+ graphene_point_t *end);
+ gboolean (* foreach) (const GskContour *contour,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data);
+ gpointer (* init_measure) (const GskContour *contour,
+ float tolerance,
+ float *out_length);
+ void (* free_measure) (const GskContour *contour,
+ gpointer measure_data);
+ void (* get_point) (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent);
+ gboolean (* get_closest_point) (const GskContour *contour,
+ gpointer measure_data,
+ float tolerance,
+ const graphene_point_t *point,
+ float threshold,
+ float *out_offset,
+ graphene_point_t *out_pos,
+ float *out_distance,
+ graphene_vec2_t *out_tangent);
+ void (* copy) (const GskContour *contour,
+ GskContour *dest);
+ void (* add_segment) (const GskContour *contour,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end);
+ int (* get_winding) (const GskContour *contour,
+ gpointer measure_data,
+ const graphene_point_t *point,
+ gboolean *on_edge);
+};
+
+static gsize
+gsk_contour_get_size_default (const GskContour *contour)
+{
+ return contour->klass->struct_size;
+}
+
+static void
+gsk_find_point_on_line (const graphene_point_t *a,
+ const graphene_point_t *b,
+ const graphene_point_t *p,
+ float *offset,
+ graphene_point_t *pos)
+{
+ graphene_vec2_t n;
+ graphene_vec2_t ap;
+ float t;
+
+ graphene_vec2_init (&n, b->x - a->x, b->y - a->y);
+ graphene_vec2_init (&ap, p->x - a->x, p->y - a->y);
+
+ t = graphene_vec2_dot (&ap, &n) / graphene_vec2_dot (&n, &n);
+
+ if (t <= 0)
+ {
+ *pos = *a;
+ *offset = 0;
+ }
+ else if (t >= 1)
+ {
+ *pos = *b;
+ *offset = 1;
+ }
+ else
+ {
+ graphene_point_interpolate (a, b, t, pos);
+ *offset = t;
+ }
+}
+
+/* RECT CONTOUR */
+
+typedef struct _GskRectContour GskRectContour;
+struct _GskRectContour
+{
+ GskContour contour;
+
+ float x;
+ float y;
+ float width;
+ float height;
+};
+
+static GskPathFlags
+gsk_rect_contour_get_flags (const GskContour *contour)
+{
+ return GSK_PATH_FLAT | GSK_PATH_CLOSED;
+}
+
+static void
+_g_string_append_double (GString *string,
+ double d)
+{
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, d);
+ g_string_append (string, buf);
+}
+
+static void
+_g_string_append_point (GString *string,
+ const graphene_point_t *pt)
+{
+ _g_string_append_double (string, pt->x);
+ g_string_append_c (string, ' ');
+ _g_string_append_double (string, pt->y);
+}
+
+static void
+gsk_rect_contour_print (const GskContour *contour,
+ GString *string)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ g_string_append (string, "M ");
+ _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->x, self->y));
+ g_string_append (string, " h ");
+ _g_string_append_double (string, self->width);
+ g_string_append (string, " v ");
+ _g_string_append_double (string, self->height);
+ g_string_append (string, " h ");
+ _g_string_append_double (string, - self->width);
+ g_string_append (string, " z");
+}
+
+static gboolean
+gsk_rect_contour_get_bounds (const GskContour *contour,
+ graphene_rect_t *rect)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ graphene_rect_init (rect, self->x, self->y, self->width, self->height);
+
+ return TRUE;
+}
+
+static void
+gsk_rect_contour_get_start_end (const GskContour *contour,
+ graphene_point_t *start,
+ graphene_point_t *end)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ if (start)
+ *start = GRAPHENE_POINT_INIT (self->x, self->y);
+
+ if (end)
+ *end = GRAPHENE_POINT_INIT (self->x, self->y);
+}
+
+static gboolean
+gsk_rect_contour_foreach (const GskContour *contour,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ graphene_point_t pts[] = {
+ GRAPHENE_POINT_INIT (self->x, self->y),
+ GRAPHENE_POINT_INIT (self->x + self->width, self->y),
+ GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height),
+ GRAPHENE_POINT_INIT (self->x, self->y + self->height),
+ GRAPHENE_POINT_INIT (self->x, self->y)
+ };
+
+ return func (GSK_PATH_MOVE, &pts[0], 1, user_data)
+ && func (GSK_PATH_LINE, &pts[0], 2, user_data)
+ && func (GSK_PATH_LINE, &pts[1], 2, user_data)
+ && func (GSK_PATH_LINE, &pts[2], 2, user_data)
+ && func (GSK_PATH_CLOSE, &pts[3], 2, user_data);
+}
+
+static gpointer
+gsk_rect_contour_init_measure (const GskContour *contour,
+ float tolerance,
+ float *out_length)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ *out_length = 2 * ABS (self->width) + 2 * ABS (self->height);
+
+ return NULL;
+}
+
+static void
+gsk_rect_contour_free_measure (const GskContour *contour,
+ gpointer data)
+{
+}
+
+static void
+gsk_rect_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ if (distance < fabsf (self->width))
+ {
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x + copysignf (distance, self->width), self->y);
+ if (tangent)
+ graphene_vec2_init (tangent, copysignf (1.0f, self->width), 0.0f);
+ return;
+ }
+ distance -= fabsf (self->width);
+
+ if (distance < fabsf (self->height))
+ {
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + copysignf (distance, self->height));
+ if (tangent)
+ graphene_vec2_init (tangent, 0.0f, copysignf (self->height, 1.0f));
+ return;
+ }
+ distance -= fabs (self->height);
+
+ if (distance < fabsf (self->width))
+ {
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x + self->width - copysignf (distance, self->width), self->y + self->height);
+ if (tangent)
+ graphene_vec2_init (tangent, - copysignf (1.0f, self->width), 0.0f);
+ return;
+ }
+ distance -= fabsf (self->width);
+
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height - copysignf (distance, self->height));
+ if (tangent)
+ graphene_vec2_init (tangent, 0.0f, - copysignf (self->height, 1.0f));
+}
+
+static gboolean
+gsk_rect_contour_get_closest_point (const GskContour *contour,
+ gpointer measure_data,
+ float tolerance,
+ const graphene_point_t *point,
+ float threshold,
+ float *out_distance,
+ graphene_point_t *out_pos,
+ float *out_offset,
+ graphene_vec2_t *out_tangent)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+ graphene_point_t t, p;
+ float distance;
+
+ /* offset coords to be relative to rectangle */
+ t.x = point->x - self->x;
+ t.y = point->y - self->y;
+
+ if (self->width)
+ {
+ /* do unit square math */
+ t.x /= self->width;
+ /* move point onto the square */
+ t.x = CLAMP (t.x, 0.f, 1.f);
+ }
+ else
+ t.x = 0.f;
+
+ if (self->height)
+ {
+ t.y /= self->height;
+ t.y = CLAMP (t.y, 0.f, 1.f);
+ }
+ else
+ t.y = 0.f;
+
+ if (t.x > 0 && t.x < 1 && t.y > 0 && t.y < 1)
+ {
+ float diff = MIN (t.x, 1.f - t.x) * ABS (self->width) - MIN (t.y, 1.f - t.y) * ABS (self->height);
+
+ if (diff < 0.f)
+ t.x = ceilf (t.x - 0.5f); /* round 0.5 down */
+ else if (diff > 0.f)
+ t.y = roundf (t.y); /* round 0.5 up */
+ else
+ {
+ /* at least 2 points match, return the first one in the stroke */
+ if (t.y <= 1.f - t.y)
+ t.y = 0.f;
+ else if (1.f - t.x <= t.x)
+ t.x = 1.f;
+ else
+ t.y = 1.f;
+ }
+ }
+
+ p = GRAPHENE_POINT_INIT (self->x + t.x * self->width,
+ self->y + t.y * self->height);
+
+ distance = graphene_point_distance (point, &p, NULL, NULL);
+ if (distance > threshold)
+ return FALSE;
+
+ if (out_distance)
+ *out_distance = distance;
+
+ if (out_pos)
+ *out_pos = p;
+
+ if (out_offset)
+ *out_offset = (t.x == 0.0 && self->width > 0 ? 2 - t.y : t.y) * ABS (self->height) +
+ (t.y == 1.0 ? 2 - t.x : t.x) * ABS (self->width);
+
+ if (out_tangent)
+ {
+ if (t.y == 0 && t.x < 1)
+ graphene_vec2_init (out_tangent, copysignf(1.0, self->width), 0);
+ else if (t.x == 0)
+ graphene_vec2_init (out_tangent, 0, - copysignf(1.0, self->height));
+ else if (t.y == 1)
+ graphene_vec2_init (out_tangent, - copysignf(1.0, self->width), 0);
+ else if (t.x == 1)
+ graphene_vec2_init (out_tangent, 0, copysignf(1.0, self->height));
+ }
+
+ return TRUE;
+}
+
+static void
+gsk_rect_contour_copy (const GskContour *contour,
+ GskContour *dest)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+ GskRectContour *target = (GskRectContour *) dest;
+
+ *target = *self;
+}
+
+static void
+gsk_rect_contour_add_segment (const GskContour *contour,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+ float w = ABS (self->width);
+ float h = ABS (self->height);
+
+ if (start < w)
+ {
+ gsk_path_builder_move_to (builder, self->x + start * (w / self->width), self->y);
+ if (end <= w)
+ {
+ gsk_path_builder_line_to (builder, self->x + end * (w / self->width), self->y);
+ return;
+ }
+ gsk_path_builder_line_to (builder, self->x + self->width, self->y);
+ }
+ start -= w;
+ end -= w;
+
+ if (start < h)
+ {
+ if (start >= 0)
+ gsk_path_builder_move_to (builder, self->x + self->width, self->y + start * (h / self->height));
+ if (end <= h)
+ {
+ gsk_path_builder_line_to (builder, self->x + self->width, self->y + end * (h / self->height));
+ return;
+ }
+ gsk_path_builder_line_to (builder, self->x + self->width, self->y + self->height);
+ }
+ start -= h;
+ end -= h;
+
+ if (start < w)
+ {
+ if (start >= 0)
+ gsk_path_builder_move_to (builder, self->x + (w - start) * (w / self->width), self->y + self->height);
+ if (end <= w)
+ {
+ gsk_path_builder_line_to (builder, self->x + (w - end) * (w / self->width), self->y + self->height);
+ return;
+ }
+ gsk_path_builder_line_to (builder, self->x, self->y + self->height);
+ }
+ start -= w;
+ end -= w;
+
+ if (start < h)
+ {
+ if (start >= 0)
+ gsk_path_builder_move_to (builder, self->x, self->y + (h - start) * (h / self->height));
+ if (end <= h)
+ {
+ gsk_path_builder_line_to (builder, self->x, self->y + (h - end) * (h / self->height));
+ return;
+ }
+ gsk_path_builder_line_to (builder, self->x, self->y);
+ }
+}
+
+static int
+gsk_rect_contour_get_winding (const GskContour *contour,
+ gpointer measure_data,
+ const graphene_point_t *point,
+ gboolean *on_edge)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+ graphene_rect_t rect;
+
+ graphene_rect_init (&rect, self->x, self->y, self->width, self->height);
+
+ if (graphene_rect_contains_point (&rect, point))
+ return -1;
+
+ return 0;
+}
+
+static const GskContourClass GSK_RECT_CONTOUR_CLASS =
+{
+ sizeof (GskRectContour),
+ "GskRectContour",
+ gsk_contour_get_size_default,
+ gsk_rect_contour_get_flags,
+ gsk_rect_contour_print,
+ gsk_rect_contour_get_bounds,
+ gsk_rect_contour_get_start_end,
+ gsk_rect_contour_foreach,
+ gsk_rect_contour_init_measure,
+ gsk_rect_contour_free_measure,
+ gsk_rect_contour_get_point,
+ gsk_rect_contour_get_closest_point,
+ gsk_rect_contour_copy,
+ gsk_rect_contour_add_segment,
+ gsk_rect_contour_get_winding
+};
+
+GskContour *
+gsk_rect_contour_new (const graphene_rect_t *rect)
+{
+ GskRectContour *self;
+
+ self = g_new0 (GskRectContour, 1);
+
+ self->contour.klass = &GSK_RECT_CONTOUR_CLASS;
+
+ 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 */
+
+#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
+#define RAD_TO_DEG(x) ((x) / (G_PI / 180.f))
+
+typedef struct _GskCircleContour GskCircleContour;
+struct _GskCircleContour
+{
+ GskContour contour;
+
+ graphene_point_t center;
+ float radius;
+ float start_angle; /* in degrees */
+ float end_angle; /* start_angle +/- 360 */
+};
+
+static GskPathFlags
+gsk_circle_contour_get_flags (const GskContour *contour)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+
+ /* XXX: should we explicitly close paths? */
+ if (fabs (self->start_angle - self->end_angle) >= 360)
+ return GSK_PATH_CLOSED;
+ else
+ return 0;
+}
+
+#define GSK_CIRCLE_POINT_INIT(self, angle) \
+ GRAPHENE_POINT_INIT ((self)->center.x + cos (DEG_TO_RAD (angle)) * self->radius, \
+ (self)->center.y + sin (DEG_TO_RAD (angle)) * self->radius)
+
+static void
+gsk_circle_contour_print (const GskContour *contour,
+ GString *string)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ float mid_angle = (self->end_angle - self->start_angle) / 2;
+
+ g_string_append (string, "M ");
+ _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, self->start_angle));
+ g_string_append (string, " A ");
+ _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->radius, self->radius));
+ g_string_append_printf (string, " 0 0 %u ",
+ self->start_angle < self->end_angle ? 0 : 1);
+ _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, mid_angle));
+ g_string_append (string, " A ");
+ _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->radius, self->radius));
+ g_string_append_printf (string, " 0 0 %u ",
+ self->start_angle < self->end_angle ? 0 : 1);
+ _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, self->end_angle));
+ if (fabs (self->start_angle - self->end_angle) >= 360)
+ g_string_append (string, " z");
+}
+
+static gboolean
+gsk_circle_contour_get_bounds (const GskContour *contour,
+ graphene_rect_t *rect)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+
+ /* XXX: handle partial circles */
+ graphene_rect_init (rect,
+ self->center.x - self->radius,
+ self->center.y - self->radius,
+ 2 * self->radius,
+ 2 * self->radius);
+
+ return TRUE;
+}
+
+static void
+gsk_circle_contour_get_start_end (const GskContour *contour,
+ graphene_point_t *start,
+ graphene_point_t *end)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+
+ if (start)
+ *start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
+
+ if (end)
+ *end = GSK_CIRCLE_POINT_INIT (self, self->end_angle);
+}
+
+typedef struct
+{
+ GskPathForeachFunc func;
+ gpointer user_data;
+} ForeachWrapper;
+
+static gboolean
+gsk_circle_contour_curve (const graphene_point_t curve[4],
+ gpointer data)
+{
+ ForeachWrapper *wrapper = data;
+
+ return wrapper->func (GSK_PATH_CURVE, curve, 4, wrapper->user_data);
+}
+
+static gboolean
+gsk_circle_contour_foreach (const GskContour *contour,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ graphene_point_t start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
+
+ if (!func (GSK_PATH_MOVE, &start, 1, user_data))
+ return FALSE;
+
+ if (!gsk_spline_decompose_arc (&self->center,
+ self->radius,
+ tolerance,
+ DEG_TO_RAD (self->start_angle),
+ DEG_TO_RAD (self->end_angle),
+ gsk_circle_contour_curve,
+ &(ForeachWrapper) { func, user_data }))
+ return FALSE;
+
+ if (fabs (self->start_angle - self->end_angle) >= 360)
+ {
+ if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, user_data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gpointer
+gsk_circle_contour_init_measure (const GskContour *contour,
+ float tolerance,
+ float *out_length)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+
+ *out_length = DEG_TO_RAD (fabs (self->start_angle - self->end_angle)) * self->radius;
+
+ return NULL;
+}
+
+static void
+gsk_circle_contour_free_measure (const GskContour *contour,
+ gpointer data)
+{
+}
+
+static void
+gsk_circle_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ float delta = self->end_angle - self->start_angle;
+ float length = self->radius * DEG_TO_RAD (delta);
+ float angle = self->start_angle + distance/length * delta;
+ graphene_point_t p;
+
+ p = GSK_CIRCLE_POINT_INIT (self, angle);
+
+ if (pos)
+ *pos = p;
+
+ if (tangent)
+ {
+ graphene_vec2_init (tangent,
+ p.y - self->center.y,
+ - p.x + self->center.x);
+ graphene_vec2_normalize (tangent, tangent);
+ }
+}
+
+static gboolean
+gsk_circle_contour_get_closest_point (const GskContour *contour,
+ gpointer measure_data,
+ float tolerance,
+ const graphene_point_t *point,
+ float threshold,
+ float *out_distance,
+ graphene_point_t *out_pos,
+ float *out_offset,
+ graphene_vec2_t *out_tangent)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ float angle;
+ float closest_angle;
+ float offset;
+ graphene_point_t pos;
+ graphene_vec2_t tangent;
+ float distance;
+
+ if (graphene_point_distance (point, &self->center, NULL, NULL) > threshold + self->radius)
+ return FALSE;
+
+ angle = atan2f (point->y - self->center.y, point->x - self->center.x);
+ angle = RAD_TO_DEG (angle);
+ if (angle < 0)
+ angle += 360;
+
+ if ((self->start_angle <= angle && angle <= self->end_angle) ||
+ (self->end_angle <= angle && angle <= self->start_angle))
+ {
+ closest_angle = angle;
+ }
+ else
+ {
+ float d1, d2;
+
+ d1 = fabs (self->start_angle - angle);
+ d1 = MIN (d1, 360 - d1);
+ d2 = fabs (self->end_angle - angle);
+ d2 = MIN (d2, 360 - d2);
+ if (d1 < d2)
+ closest_angle = self->start_angle;
+ else
+ closest_angle = self->end_angle;
+ }
+
+ offset = self->radius * 2 * M_PI * (closest_angle - self->start_angle) / (self->end_angle - self->start_angle);
+
+ gsk_circle_contour_get_point (contour, NULL, offset, &pos, out_tangent ? &tangent : NULL);
+
+ distance = graphene_point_distance (&pos, point, NULL, NULL);
+ if (threshold < distance)
+ return FALSE;
+
+ if (out_offset)
+ *out_offset = offset;
+ if (out_pos)
+ *out_pos = pos;
+ if (out_distance)
+ *out_distance = distance;
+ if (out_tangent)
+ *out_tangent = tangent;
+
+ return TRUE;
+}
+
+static void
+gsk_circle_contour_copy (const GskContour *contour,
+ GskContour *dest)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ GskCircleContour *target = (GskCircleContour *) dest;
+
+ *target = *self;
+}
+
+static void
+gsk_circle_contour_add_segment (const GskContour *contour,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ float delta = self->end_angle - self->start_angle;
+ float length = self->radius * DEG_TO_RAD (delta);
+ GskContour *segment;
+
+ 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 int
+gsk_circle_contour_get_winding (const GskContour *contour,
+ gpointer measure_data,
+ const graphene_point_t *point,
+ gboolean *on_edge)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+
+ if (graphene_point_distance (point, &self->center, NULL, NULL) >= self->radius)
+ return 0;
+
+ if (fabs (self->start_angle - self->end_angle) >= 360)
+ {
+ return -1;
+ }
+ else
+ {
+ /* Check if the point and the midpoint are on the same side
+ * of the chord through start and end.
+ */
+ double mid_angle = (self->end_angle - self->start_angle) / 2;
+ graphene_point_t start = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (self->start_angle)) * self->radius,
+ self->center.y + sin (DEG_TO_RAD (self->start_angle)) * self->radius);
+ graphene_point_t mid = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (mid_angle)) * self->radius,
+ self->center.y + sin (DEG_TO_RAD (mid_angle)) * self->radius);
+ graphene_point_t end = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (self->end_angle)) * self->radius,
+ self->center.y + sin (DEG_TO_RAD (self->end_angle)) * self->radius);
+
+ graphene_vec2_t n, m;
+ float a, b;
+
+ graphene_vec2_init (&n, start.y - end.y, end.x - start.x);
+ graphene_vec2_init (&m, mid.x, mid.y);
+ a = graphene_vec2_dot (&m, &n);
+ graphene_vec2_init (&m, point->x, point->y);
+ b = graphene_vec2_dot (&m, &n);
+
+ if ((a < 0) != (b < 0))
+ return -1;
+ }
+
+ return 0;
+}
+
+static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
+{
+ sizeof (GskCircleContour),
+ "GskCircleContour",
+ gsk_contour_get_size_default,
+ gsk_circle_contour_get_flags,
+ gsk_circle_contour_print,
+ gsk_circle_contour_get_bounds,
+ gsk_circle_contour_get_start_end,
+ gsk_circle_contour_foreach,
+ gsk_circle_contour_init_measure,
+ gsk_circle_contour_free_measure,
+ gsk_circle_contour_get_point,
+ gsk_circle_contour_get_closest_point,
+ gsk_circle_contour_copy,
+ gsk_circle_contour_add_segment,
+ gsk_circle_contour_get_winding
+};
+
+GskContour *
+gsk_circle_contour_new (const graphene_point_t *center,
+ float radius,
+ float start_angle,
+ float end_angle)
+{
+ GskCircleContour *self;
+
+ self = g_new0 (GskCircleContour, 1);
+
+ self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
+
+ g_assert (fabs (start_angle - end_angle) <= 360);
+
+ self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
+ self->center = *center;
+ self->radius = radius;
+ self->start_angle = start_angle;
+ self->end_angle = end_angle;
+
+ return (GskContour *) self;
+}
+
+/* STANDARD CONTOUR */
+
+typedef struct _GskStandardContour GskStandardContour;
+struct _GskStandardContour
+{
+ GskContour contour;
+
+ GskPathFlags flags;
+
+ gsize n_ops;
+ gsize n_points;
+ graphene_point_t *points;
+ GskStandardOperation ops[];
+};
+
+static gsize
+gsk_standard_contour_compute_size (gsize n_ops,
+ gsize n_points)
+{
+ return sizeof (GskStandardContour)
+ + sizeof (GskStandardOperation) * n_ops
+ + sizeof (graphene_point_t) * n_points;
+}
+
+static gsize
+gsk_standard_contour_get_size (const GskContour *contour)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+
+ return gsk_standard_contour_compute_size (self->n_ops, self->n_points);
+}
+
+static gboolean
+gsk_standard_contour_foreach (const GskContour *contour,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data)
+{
+ 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 (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GskPathFlags
+gsk_standard_contour_get_flags (const GskContour *contour)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+
+ return self->flags;
+}
+
+static void
+gsk_standard_contour_print (const GskContour *contour,
+ GString *string)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+ gsize i;
+
+ for (i = 0; i < self->n_ops; i ++)
+ {
+ graphene_point_t *pt = &self->points[self->ops[i].point];
+
+ switch (self->ops[i].op)
+ {
+ case GSK_PATH_MOVE:
+ g_string_append (string, "M ");
+ _g_string_append_point (string, &pt[0]);
+ break;
+
+ case GSK_PATH_CLOSE:
+ g_string_append (string, " Z");
+ break;
+
+ case GSK_PATH_LINE:
+ g_string_append (string, " L ");
+ _g_string_append_point (string, &pt[1]);
+ break;
+
+ case GSK_PATH_CURVE:
+ g_string_append (string, " C ");
+ _g_string_append_point (string, &pt[1]);
+ g_string_append (string, ", ");
+ _g_string_append_point (string, &pt[2]);
+ g_string_append (string, ", ");
+ _g_string_append_point (string, &pt[3]);
+ break;
+
+ default:
+ g_assert_not_reached();
+ return;
+ }
+ }
+}
+
+static void
+rect_add_point (graphene_rect_t *rect,
+ const graphene_point_t *point)
+{
+ if (point->x < rect->origin.x)
+ {
+ rect->size.width += rect->origin.x - point->x;
+ rect->origin.x = point->x;
+ }
+ else if (point->x > rect->origin.x + rect->size.width)
+ {
+ rect->size.width = point->x - rect->origin.x;
+ }
+
+ if (point->y < rect->origin.y)
+ {
+ rect->size.height += rect->origin.y - point->y;
+ rect->origin.y = point->y;
+ }
+ else if (point->y > rect->origin.y + rect->size.height)
+ {
+ rect->size.height = point->y - rect->origin.y;
+ }
+}
+
+static gboolean
+gsk_standard_contour_get_bounds (const GskContour *contour,
+ graphene_rect_t *bounds)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+ gsize i;
+
+ if (self->n_points == 0)
+ return FALSE;
+
+ graphene_rect_init (bounds,
+ self->points[0].x, self->points[0].y,
+ 0, 0);
+
+ for (i = 1; i < self->n_points; i ++)
+ {
+ rect_add_point (bounds, &self->points[i]);
+ }
+
+ return bounds->size.width > 0 && bounds->size.height > 0;
+}
+
+static void
+gsk_standard_contour_get_start_end (const GskContour *contour,
+ graphene_point_t *start,
+ graphene_point_t *end)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+
+ if (start)
+ *start = self->points[0];
+
+ if (end)
+ *end = self->points[self->n_points - 1];
+}
+
+typedef struct
+{
+ float start;
+ float end;
+ float start_progress;
+ float end_progress;
+ graphene_point_t end_point;
+ gsize op;
+} GskStandardContourMeasure;
+
+typedef struct
+{
+ GArray *array;
+ GskStandardContourMeasure measure;
+} LengthDecompose;
+
+static void
+gsk_standard_contour_measure_add_point (const graphene_point_t *from,
+ const graphene_point_t *to,
+ float from_progress,
+ float to_progress,
+ gpointer user_data)
+{
+ LengthDecompose *decomp = user_data;
+ float seg_length;
+
+ seg_length = graphene_point_distance (from, to, NULL, NULL);
+ decomp->measure.end += seg_length;
+ decomp->measure.start_progress = from_progress;
+ decomp->measure.end_progress = to_progress;
+ decomp->measure.end_point = *to;
+
+ g_array_append_val (decomp->array, decomp->measure);
+
+ decomp->measure.start += seg_length;
+}
+
+static gpointer
+gsk_standard_contour_init_measure (const GskContour *contour,
+ float tolerance,
+ float *out_length)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+ gsize i;
+ float length, seg_length;
+ GArray *array;
+
+ array = g_array_new (FALSE, FALSE, sizeof (GskStandardContourMeasure));
+ length = 0;
+
+ for (i = 1; i < self->n_ops; i ++)
+ {
+ graphene_point_t *pt = &self->points[self->ops[i].point];
+
+ switch (self->ops[i].op)
+ {
+ case GSK_PATH_MOVE:
+ break;
+
+ case GSK_PATH_CLOSE:
+ case GSK_PATH_LINE:
+ seg_length = graphene_point_distance (&pt[0], &pt[1], NULL, NULL);
+ if (seg_length > 0)
+ {
+ g_array_append_vals (array,
+ &(GskStandardContourMeasure) {
+ length,
+ length + seg_length,
+ 0, 1,
+ pt[1],
+ i,
+ }, 1);
+ length += seg_length;
+ }
+ break;
+
+ case GSK_PATH_CURVE:
+ {
+ LengthDecompose decomp = { array, { length, length, 0, 0, pt[0], i } };
+ gsk_spline_decompose_cubic (pt, tolerance, gsk_standard_contour_measure_add_point, &decomp);
+ length = decomp.measure.start;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+ }
+
+ *out_length = length;
+
+ return array;
+}
+
+static void
+gsk_standard_contour_free_measure (const GskContour *contour,
+ gpointer data)
+{
+ g_array_free (data, TRUE);
+}
+
+static int
+gsk_standard_contour_find_measure (gconstpointer m,
+ gconstpointer l)
+{
+ const GskStandardContourMeasure *measure = m;
+ float length = *(const float *) l;
+
+ if (measure->start > length)
+ return 1;
+ else if (measure->end <= length)
+ return -1;
+ else
+ return 0;
+}
+
+static void
+gsk_standard_contour_measure_get_point (GskStandardContour *self,
+ gsize op,
+ float progress,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ const graphene_point_t *pts;
+
+ pts = &self->points[self->ops[op].point];
+ switch (self->ops[op].op)
+ {
+ case GSK_PATH_LINE:
+ case GSK_PATH_CLOSE:
+ if (pos)
+ graphene_point_interpolate (&pts[0], &pts[1], progress, pos);
+ if (tangent)
+ {
+ graphene_vec2_init (tangent, pts[1].x - pts[0].x, pts[1].y - pts[0].y);
+ graphene_vec2_normalize (tangent, tangent);
+ }
+ break;
+
+ case GSK_PATH_CURVE:
+ gsk_spline_get_point_cubic (pts, progress, pos, tangent);
+ break;
+
+ case GSK_PATH_MOVE:
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+}
+
+static void
+gsk_standard_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ GskStandardContour *self = (GskStandardContour *) contour;
+ GArray *array = measure_data;
+ guint index;
+ float progress;
+ GskStandardContourMeasure *measure;
+
+ if (array->len == 0)
+ {
+ g_assert (distance == 0);
+ g_assert (self->ops[0].op == GSK_PATH_MOVE);
+ if (pos)
+ *pos = self->points[0];
+ if (tangent)
+ graphene_vec2_init (tangent, 1.f, 0.f);
+ return;
+ }
+
+ if (!g_array_binary_search (array, &distance, gsk_standard_contour_find_measure, &index))
+ index = array->len - 1;
+ measure = &g_array_index (array, GskStandardContourMeasure, index);
+ progress = (distance - measure->start) / (measure->end - measure->start);
+ progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
+ g_assert (progress >= 0 && progress <= 1);
+
+ gsk_standard_contour_measure_get_point (self, measure->op, progress, pos, tangent);
+}
+
+static gboolean
+gsk_standard_contour_get_closest_point (const GskContour *contour,
+ gpointer measure_data,
+ float tolerance,
+ const graphene_point_t *point,
+ float threshold,
+ float *out_distance,
+ graphene_point_t *out_pos,
+ float *out_offset,
+ graphene_vec2_t *out_tangent)
+{
+ GskStandardContour *self = (GskStandardContour *) contour;
+ GskStandardContourMeasure *measure;
+ float progress, dist;
+ GArray *array = measure_data;
+ graphene_point_t p, last_point;
+ gsize i;
+ gboolean result = FALSE;
+
+ g_assert (self->ops[0].op == GSK_PATH_MOVE);
+ last_point = self->points[0];
+
+ if (array->len == 0)
+ {
+ /* This is the special case for point-only */
+ dist = graphene_point_distance (&last_point, point, NULL, NULL);
+
+ if (dist > threshold)
+ return FALSE;
+
+ if (out_offset)
+ *out_offset = 0;
+
+ if (out_distance)
+ *out_distance = dist;
+
+ if (out_pos)
+ *out_pos = last_point;
+
+ if (out_tangent)
+ *out_tangent = *graphene_vec2_x_axis ();
+
+ return TRUE;
+ }
+
+ for (i = 0; i < array->len; i++)
+ {
+ measure = &g_array_index (array, GskStandardContourMeasure, i);
+
+ gsk_find_point_on_line (&last_point,
+ &measure->end_point,
+ point,
+ &progress,
+ &p);
+ last_point = measure->end_point;
+ dist = graphene_point_distance (point, &p, NULL, NULL);
+ /* add some wiggleroom for the accurate check below */
+ //g_print ("%zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
+ if (dist <= threshold + 1.0f)
+ {
+ graphene_vec2_t t;
+ float found_progress;
+
+ found_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
+
+ gsk_standard_contour_measure_get_point (self, measure->op, found_progress, &p, out_tangent ? &t : NULL);
+ dist = graphene_point_distance (point, &p, NULL, NULL);
+ /* double check that the point actually is closer */
+ //g_print ("!!! %zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
+ if (dist <= threshold)
+ {
+ if (out_distance)
+ *out_distance = dist;
+ if (out_pos)
+ *out_pos = p;
+ if (out_offset)
+ *out_offset = measure->start + (measure->end - measure->start) * progress;
+ if (out_tangent)
+ *out_tangent = t;
+ result = TRUE;
+ if (tolerance >= dist)
+ return TRUE;
+ threshold = dist - tolerance;
+ }
+ }
+ }
+
+ return result;
+}
+
+static void
+gsk_standard_contour_init (GskContour *contour,
+ GskPathFlags flags,
+ const GskStandardOperation *ops,
+ gsize n_ops,
+ const graphene_point_t *points,
+ gsize n_points);
+
+static void
+gsk_standard_contour_copy (const GskContour *contour,
+ GskContour *dest)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+
+ gsk_standard_contour_init (dest, self->flags, self->ops, self->n_ops, self->points, self->n_points);
+}
+
+static void
+gsk_standard_contour_add_segment (const GskContour *contour,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end)
+{
+ GskStandardContour *self = (GskStandardContour *) contour;
+ GArray *array = measure_data;
+ guint start_index, end_index;
+ float start_progress, end_progress;
+ GskStandardContourMeasure *start_measure, *end_measure;
+ gsize i;
+
+ if (start > 0)
+ {
+ if (!g_array_binary_search (array, (float[1]) { start }, gsk_standard_contour_find_measure, &start_index))
+ start_index = array->len - 1;
+ start_measure = &g_array_index (array, GskStandardContourMeasure, start_index);
+ start_progress = (start - start_measure->start) / (start_measure->end - start_measure->start);
+ start_progress = start_measure->start_progress + (start_measure->end_progress - start_measure->start_progress) * start_progress;
+ g_assert (start_progress >= 0 && start_progress <= 1);
+ }
+ else
+ {
+ start_measure = NULL;
+ start_progress = 0.0;
+ }
+
+ if (g_array_binary_search (array, (float[1]) { end }, gsk_standard_contour_find_measure, &end_index))
+ {
+ end_measure = &g_array_index (array, GskStandardContourMeasure, end_index);
+ end_progress = (end - end_measure->start) / (end_measure->end - end_measure->start);
+ end_progress = end_measure->start_progress + (end_measure->end_progress - end_measure->start_progress) * end_progress;
+ g_assert (end_progress >= 0 && end_progress <= 1);
+ }
+ else
+ {
+ end_measure = NULL;
+ end_progress = 1.0;
+ }
+
+ /* Add the first partial operation,
+ * taking care that first and last operation might be identical */
+ if (start_measure)
+ {
+ switch (self->ops[start_measure->op].op)
+ {
+ case GSK_PATH_CLOSE:
+ case GSK_PATH_LINE:
+ {
+ graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
+ graphene_point_t point;
+
+ graphene_point_interpolate (&pts[0], &pts[1], start_progress, &point);
+ gsk_path_builder_move_to (builder, point.x, point.y);
+ if (end_measure && end_measure->op == start_measure->op)
+ {
+ graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
+ gsk_path_builder_line_to (builder, point.x, point.y);
+ return;
+ }
+ gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
+ }
+ break;
+
+ case GSK_PATH_CURVE:
+ {
+ graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
+ graphene_point_t curve[4], discard[4];
+
+ gsk_spline_split_cubic (pts, discard, curve, start_progress);
+ if (end_measure && end_measure->op == start_measure->op)
+ {
+ graphene_point_t tiny[4];
+ gsk_spline_split_cubic (curve, tiny, discard, (end_progress - start_progress) / (1 - start_progress));
+ gsk_path_builder_move_to (builder, tiny[0].x, tiny[0].y);
+ gsk_path_builder_curve_to (builder, tiny[1].x, tiny[1].y, tiny[2].x, tiny[2].y, tiny[3].x, tiny[3].y);
+ return;
+ }
+ gsk_path_builder_move_to (builder, curve[0].x, curve[0].y);
+ gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
+ }
+ break;
+
+ case GSK_PATH_MOVE:
+ default:
+ g_assert_not_reached();
+ return;
+ }
+ i = start_measure->op + 1;
+ }
+ else
+ i = 0;
+
+ for (; i < (end_measure ? end_measure->op : self->n_ops); i++)
+ {
+ graphene_point_t *pt = &self->points[self->ops[i].point];
+
+ switch (self->ops[i].op)
+ {
+ case GSK_PATH_MOVE:
+ gsk_path_builder_move_to (builder, pt[0].x, pt[0].y);
+ break;
+
+ case GSK_PATH_LINE:
+ case GSK_PATH_CLOSE:
+ gsk_path_builder_line_to (builder, pt[1].x, pt[1].y);
+ break;
+
+ case GSK_PATH_CURVE:
+ gsk_path_builder_curve_to (builder, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
+ break;
+
+ default:
+ g_assert_not_reached();
+ return;
+ }
+ }
+
+ /* Add the last partial operation */
+ if (end_measure)
+ {
+ switch (self->ops[end_measure->op].op)
+ {
+ case GSK_PATH_CLOSE:
+ case GSK_PATH_LINE:
+ {
+ graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
+ graphene_point_t point;
+
+ graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
+ gsk_path_builder_line_to (builder, point.x, point.y);
+ }
+ break;
+
+ case GSK_PATH_CURVE:
+ {
+ graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
+ graphene_point_t curve[4], discard[4];
+
+ gsk_spline_split_cubic (pts, curve, discard, end_progress);
+ gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
+ }
+ break;
+
+ case GSK_PATH_MOVE:
+ default:
+ g_assert_not_reached();
+ return;
+ }
+ }
+}
+
+static inline int
+line_get_crossing (const graphene_point_t *p,
+ const graphene_point_t *p1,
+ const graphene_point_t *p2,
+ gboolean *on_edge)
+{
+ int dir = 1;
+
+ if (p1->x >= p->x && p2->x >= p->x)
+ return 0;
+
+ if (p2->y < p1->y)
+ {
+ const graphene_point_t *tmp;
+ tmp = p1;
+ p1 = p2;
+ p2 = tmp;
+ dir = -1;
+ }
+
+ if ((p1->x >= p->x && p1->y == p->y) ||
+ (p2->x >= p->x && p2->y == p->y))
+ {
+ *on_edge = TRUE;
+ return 0;
+ }
+
+ if (p2->y <= p->y || p1->y > p->y)
+ return 0;
+
+ if (p1->x <= p->x && p2->x <= p->x)
+ return dir;
+
+ if (p->x > p1->x + (p->y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y))
+ return dir;
+
+ return 0;
+}
+
+static int
+gsk_standard_contour_get_winding (const GskContour *contour,
+ gpointer measure_data,
+ const graphene_point_t *point,
+ gboolean *on_edge)
+{
+ GskStandardContour *self = (GskStandardContour *) contour;
+ GArray *array = measure_data;
+ graphene_point_t last_point;
+ int winding;
+ int i;
+
+ if (array->len == 0)
+ return 0;
+
+ winding = 0;
+ last_point = self->points[0];
+ for (i = 0; i < array->len; i++)
+ {
+ GskStandardContourMeasure *measure;
+
+ measure = &g_array_index (array, GskStandardContourMeasure, i);
+ winding += line_get_crossing (point, &last_point, &measure->end_point, on_edge);
+ if (*on_edge)
+ return 0;
+
+ last_point = measure->end_point;
+ }
+
+ winding += line_get_crossing (point, &last_point, &self->points[0], on_edge);
+
+ return winding;
+}
+
+static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
+{
+ sizeof (GskStandardContour),
+ "GskStandardContour",
+ gsk_standard_contour_get_size,
+ gsk_standard_contour_get_flags,
+ gsk_standard_contour_print,
+ gsk_standard_contour_get_bounds,
+ gsk_standard_contour_get_start_end,
+ gsk_standard_contour_foreach,
+ gsk_standard_contour_init_measure,
+ gsk_standard_contour_free_measure,
+ gsk_standard_contour_get_point,
+ gsk_standard_contour_get_closest_point,
+ gsk_standard_contour_copy,
+ gsk_standard_contour_add_segment,
+ gsk_standard_contour_get_winding
+};
+
+/* You must ensure the contour has enough size allocated,
+ * 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)
+{
+ GskStandardContour *self = (GskStandardContour *) contour;
+
+ 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);
+}
+
+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 */
+
+gsize
+gsk_contour_get_size (const GskContour *self)
+{
+ return self->klass->get_size (self);
+}
+
+GskPathFlags
+gsk_contour_get_flags (const GskContour *self)
+{
+ return self->klass->get_flags (self);
+}
+
+void
+gsk_contour_print (const GskContour *self,
+ GString *string)
+{
+ self->klass->print (self, string);
+}
+
+gboolean
+gsk_contour_get_bounds (const GskContour *self,
+ graphene_rect_t *bounds)
+{
+ return self->klass->get_bounds (self, bounds);
+}
+
+gboolean
+gsk_contour_foreach (const GskContour *self,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data)
+{
+ return self->klass->foreach (self, tolerance, func, user_data);
+}
+
+gpointer
+gsk_contour_init_measure (const GskContour *self,
+ float tolerance,
+ float *out_length)
+{
+ return self->klass->init_measure (self, tolerance, out_length);
+}
+
+void
+gsk_contour_free_measure (const GskContour *self,
+ gpointer data)
+{
+ self->klass->free_measure (self, data);
+}
+
+void
+gsk_contour_get_start_end (const GskContour *self,
+ graphene_point_t *start,
+ graphene_point_t *end)
+{
+ self->klass->get_start_end (self, start, end);
+}
+
+void
+gsk_contour_get_point (const GskContour *self,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ self->klass->get_point (self, measure_data, distance, pos, tangent);
+}
+
+gboolean
+gsk_contour_get_closest_point (const GskContour *self,
+ gpointer measure_data,
+ float tolerance,
+ const graphene_point_t *point,
+ float threshold,
+ float *out_distance,
+ graphene_point_t *out_pos,
+ float *out_offset,
+ graphene_vec2_t *out_tangent)
+{
+ return self->klass->get_closest_point (self,
+ measure_data,
+ tolerance,
+ point,
+ threshold,
+ out_distance,
+ out_pos,
+ out_offset,
+ 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);
+}
+
+int
+gsk_contour_get_winding (const GskContour *self,
+ gpointer measure_data,
+ const graphene_point_t *point,
+ gboolean *on_edge)
+{
+ return self->klass->get_winding (self, measure_data, point, on_edge);
+}
+
+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;
+}
+
diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h
new file mode 100644
index 0000000000..13e54a8e5d
--- /dev/null
+++ b/gsk/gskcontourprivate.h
@@ -0,0 +1,103 @@
+/*
+ * 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_CONTOUR_PRIVATE_H__
+#define __GSK_CONTOUR_PRIVATE_H__
+
+#include <gskpath.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GSK_PATH_FLAT,
+ GSK_PATH_CLOSED
+} GskPathFlags;
+
+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,
+ gsize n_ops,
+ const graphene_point_t *points,
+ gsize n_points);
+
+void gsk_contour_copy (GskContour * dest,
+ const GskContour *src);
+GskContour * gsk_contour_dup (const GskContour *src);
+
+gsize gsk_contour_get_size (const GskContour *self);
+GskPathFlags gsk_contour_get_flags (const GskContour *self);
+void gsk_contour_print (const GskContour *self,
+ GString *string);
+gboolean gsk_contour_get_bounds (const GskContour *self,
+ graphene_rect_t *bounds);
+gpointer gsk_contour_init_measure (const GskContour *self,
+ float tolerance,
+ float *out_length);
+void gsk_contour_free_measure (const GskContour *self,
+ gpointer data);
+gboolean gsk_contour_foreach (const GskContour *self,
+ float tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data);
+void gsk_contour_get_start_end (const GskContour *self,
+ graphene_point_t *start,
+ graphene_point_t *end);
+void gsk_contour_get_point (const GskContour *self,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent);
+gboolean gsk_contour_get_closest_point (const GskContour *self,
+ gpointer measure_data,
+ float tolerance,
+ const graphene_point_t *point,
+ float threshold,
+ float *out_distance,
+ graphene_point_t *out_pos,
+ float *out_offset,
+ graphene_vec2_t *out_tangent);
+int gsk_contour_get_winding (const GskContour *self,
+ gpointer measure_data,
+ const graphene_point_t *point,
+ gboolean *on_edge);
+void gsk_contour_add_segment (const GskContour *self,
+ GskPathBuilder *builder,
+ gpointer measure_data,
+ float start,
+ float end);
+
+G_END_DECLS
+
+#endif /* __GSK_CONTOUR_PRIVATE_H__ */
+
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 85096fc757..8808f17742 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -38,61 +38,6 @@
* The #GskPathBuilder structure is meant to help in this endeavor.
*/
-struct _GskContour
-{
- const GskContourClass *klass;
-};
-
-struct _GskContourClass
-{
- gsize struct_size;
- const char *type_name;
-
- gsize (* get_size) (const GskContour *contour);
- GskPathFlags (* get_flags) (const GskContour *contour);
- void (* print) (const GskContour *contour,
- GString *string);
- gboolean (* get_bounds) (const GskContour *contour,
- graphene_rect_t *bounds);
- void (* get_start_end) (const GskContour *self,
- graphene_point_t *start,
- graphene_point_t *end);
- gboolean (* foreach) (const GskContour *contour,
- float tolerance,
- GskPathForeachFunc func,
- gpointer user_data);
- gpointer (* init_measure) (const GskContour *contour,
- float tolerance,
- float *out_length);
- void (* free_measure) (const GskContour *contour,
- gpointer measure_data);
- void (* get_point) (const GskContour *contour,
- gpointer measure_data,
- float distance,
- graphene_point_t *pos,
- graphene_vec2_t *tangent);
- gboolean (* get_closest_point) (const GskContour *contour,
- gpointer measure_data,
- float tolerance,
- const graphene_point_t *point,
- float threshold,
- float *out_offset,
- graphene_point_t *out_pos,
- float *out_distance,
- graphene_vec2_t *out_tangent);
- void (* copy) (const GskContour *contour,
- GskContour *dest);
- void (* add_segment) (const GskContour *contour,
- GskPathBuilder *builder,
- gpointer measure_data,
- float start,
- float end);
- int (* get_winding) (const GskContour *contour,
- gpointer measure_data,
- const graphene_point_t *point,
- gboolean *on_edge);
-};
-
struct _GskPath
{
/*< private >*/
@@ -116,1639 +61,6 @@ G_DEFINE_BOXED_TYPE (GskPath, gsk_path,
gsk_path_ref,
gsk_path_unref)
-static gsize
-gsk_contour_get_size_default (const GskContour *contour)
-{
- return contour->klass->struct_size;
-}
-
-static void
-gsk_find_point_on_line (const graphene_point_t *a,
- const graphene_point_t *b,
- const graphene_point_t *p,
- float *offset,
- graphene_point_t *pos)
-{
- graphene_vec2_t n;
- graphene_vec2_t ap;
- float t;
-
- graphene_vec2_init (&n, b->x - a->x, b->y - a->y);
- graphene_vec2_init (&ap, p->x - a->x, p->y - a->y);
-
- t = graphene_vec2_dot (&ap, &n) / graphene_vec2_dot (&n, &n);
-
- if (t <= 0)
- {
- *pos = *a;
- *offset = 0;
- }
- else if (t >= 1)
- {
- *pos = *b;
- *offset = 1;
- }
- else
- {
- graphene_point_interpolate (a, b, t, pos);
- *offset = t;
- }
-}
-
-/* RECT CONTOUR */
-
-typedef struct _GskRectContour GskRectContour;
-struct _GskRectContour
-{
- GskContour contour;
-
- float x;
- float y;
- float width;
- float height;
-};
-
-static GskPathFlags
-gsk_rect_contour_get_flags (const GskContour *contour)
-{
- return GSK_PATH_FLAT | GSK_PATH_CLOSED;
-}
-
-static void
-_g_string_append_double (GString *string,
- double d)
-{
- char buf[G_ASCII_DTOSTR_BUF_SIZE];
-
- g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, d);
- g_string_append (string, buf);
-}
-
-static void
-_g_string_append_point (GString *string,
- const graphene_point_t *pt)
-{
- _g_string_append_double (string, pt->x);
- g_string_append_c (string, ' ');
- _g_string_append_double (string, pt->y);
-}
-
-static void
-gsk_rect_contour_print (const GskContour *contour,
- GString *string)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
-
- g_string_append (string, "M ");
- _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->x, self->y));
- g_string_append (string, " h ");
- _g_string_append_double (string, self->width);
- g_string_append (string, " v ");
- _g_string_append_double (string, self->height);
- g_string_append (string, " h ");
- _g_string_append_double (string, - self->width);
- g_string_append (string, " z");
-}
-
-static gboolean
-gsk_rect_contour_get_bounds (const GskContour *contour,
- graphene_rect_t *rect)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
-
- graphene_rect_init (rect, self->x, self->y, self->width, self->height);
-
- return TRUE;
-}
-
-static void
-gsk_rect_contour_get_start_end (const GskContour *contour,
- graphene_point_t *start,
- graphene_point_t *end)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
-
- if (start)
- *start = GRAPHENE_POINT_INIT (self->x, self->y);
-
- if (end)
- *end = GRAPHENE_POINT_INIT (self->x, self->y);
-}
-
-static gboolean
-gsk_rect_contour_foreach (const GskContour *contour,
- float tolerance,
- GskPathForeachFunc func,
- gpointer user_data)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
-
- graphene_point_t pts[] = {
- GRAPHENE_POINT_INIT (self->x, self->y),
- GRAPHENE_POINT_INIT (self->x + self->width, self->y),
- GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height),
- GRAPHENE_POINT_INIT (self->x, self->y + self->height),
- GRAPHENE_POINT_INIT (self->x, self->y)
- };
-
- return func (GSK_PATH_MOVE, &pts[0], 1, user_data)
- && func (GSK_PATH_LINE, &pts[0], 2, user_data)
- && func (GSK_PATH_LINE, &pts[1], 2, user_data)
- && func (GSK_PATH_LINE, &pts[2], 2, user_data)
- && func (GSK_PATH_CLOSE, &pts[3], 2, user_data);
-}
-
-static gpointer
-gsk_rect_contour_init_measure (const GskContour *contour,
- float tolerance,
- float *out_length)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
-
- *out_length = 2 * ABS (self->width) + 2 * ABS (self->height);
-
- return NULL;
-}
-
-static void
-gsk_rect_contour_free_measure (const GskContour *contour,
- gpointer data)
-{
-}
-
-static void
-gsk_rect_contour_get_point (const GskContour *contour,
- gpointer measure_data,
- float distance,
- graphene_point_t *pos,
- graphene_vec2_t *tangent)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
-
- if (distance < fabsf (self->width))
- {
- if (pos)
- *pos = GRAPHENE_POINT_INIT (self->x + copysignf (distance, self->width), self->y);
- if (tangent)
- graphene_vec2_init (tangent, copysignf (1.0f, self->width), 0.0f);
- return;
- }
- distance -= fabsf (self->width);
-
- if (distance < fabsf (self->height))
- {
- if (pos)
- *pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + copysignf (distance, self->height));
- if (tangent)
- graphene_vec2_init (tangent, 0.0f, copysignf (self->height, 1.0f));
- return;
- }
- distance -= fabs (self->height);
-
- if (distance < fabsf (self->width))
- {
- if (pos)
- *pos = GRAPHENE_POINT_INIT (self->x + self->width - copysignf (distance, self->width), self->y + self->height);
- if (tangent)
- graphene_vec2_init (tangent, - copysignf (1.0f, self->width), 0.0f);
- return;
- }
- distance -= fabsf (self->width);
-
- if (pos)
- *pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height - copysignf (distance, self->height));
- if (tangent)
- graphene_vec2_init (tangent, 0.0f, - copysignf (self->height, 1.0f));
-}
-
-static gboolean
-gsk_rect_contour_get_closest_point (const GskContour *contour,
- gpointer measure_data,
- float tolerance,
- const graphene_point_t *point,
- float threshold,
- float *out_distance,
- graphene_point_t *out_pos,
- float *out_offset,
- graphene_vec2_t *out_tangent)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
- graphene_point_t t, p;
- float distance;
-
- /* offset coords to be relative to rectangle */
- t.x = point->x - self->x;
- t.y = point->y - self->y;
-
- if (self->width)
- {
- /* do unit square math */
- t.x /= self->width;
- /* move point onto the square */
- t.x = CLAMP (t.x, 0.f, 1.f);
- }
- else
- t.x = 0.f;
-
- if (self->height)
- {
- t.y /= self->height;
- t.y = CLAMP (t.y, 0.f, 1.f);
- }
- else
- t.y = 0.f;
-
- if (t.x > 0 && t.x < 1 && t.y > 0 && t.y < 1)
- {
- float diff = MIN (t.x, 1.f - t.x) * ABS (self->width) - MIN (t.y, 1.f - t.y) * ABS (self->height);
-
- if (diff < 0.f)
- t.x = ceilf (t.x - 0.5f); /* round 0.5 down */
- else if (diff > 0.f)
- t.y = roundf (t.y); /* round 0.5 up */
- else
- {
- /* at least 2 points match, return the first one in the stroke */
- if (t.y <= 1.f - t.y)
- t.y = 0.f;
- else if (1.f - t.x <= t.x)
- t.x = 1.f;
- else
- t.y = 1.f;
- }
- }
-
- p = GRAPHENE_POINT_INIT (self->x + t.x * self->width,
- self->y + t.y * self->height);
-
- distance = graphene_point_distance (point, &p, NULL, NULL);
- if (distance > threshold)
- return FALSE;
-
- if (out_distance)
- *out_distance = distance;
-
- if (out_pos)
- *out_pos = p;
-
- if (out_offset)
- *out_offset = (t.x == 0.0 && self->width > 0 ? 2 - t.y : t.y) * ABS (self->height) +
- (t.y == 1.0 ? 2 - t.x : t.x) * ABS (self->width);
-
- if (out_tangent)
- {
- if (t.y == 0 && t.x < 1)
- graphene_vec2_init (out_tangent, copysignf(1.0, self->width), 0);
- else if (t.x == 0)
- graphene_vec2_init (out_tangent, 0, - copysignf(1.0, self->height));
- else if (t.y == 1)
- graphene_vec2_init (out_tangent, - copysignf(1.0, self->width), 0);
- else if (t.x == 1)
- graphene_vec2_init (out_tangent, 0, copysignf(1.0, self->height));
- }
-
- return TRUE;
-}
-
-static void
-gsk_rect_contour_copy (const GskContour *contour,
- GskContour *dest)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
- GskRectContour *target = (GskRectContour *) dest;
-
- *target = *self;
-}
-
-static void
-gsk_rect_contour_add_segment (const GskContour *contour,
- GskPathBuilder *builder,
- gpointer measure_data,
- float start,
- float end)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
- float w = ABS (self->width);
- float h = ABS (self->height);
-
- if (start < w)
- {
- gsk_path_builder_move_to (builder, self->x + start * (w / self->width), self->y);
- if (end <= w)
- {
- gsk_path_builder_line_to (builder, self->x + end * (w / self->width), self->y);
- return;
- }
- gsk_path_builder_line_to (builder, self->x + self->width, self->y);
- }
- start -= w;
- end -= w;
-
- if (start < h)
- {
- if (start >= 0)
- gsk_path_builder_move_to (builder, self->x + self->width, self->y + start * (h / self->height));
- if (end <= h)
- {
- gsk_path_builder_line_to (builder, self->x + self->width, self->y + end * (h / self->height));
- return;
- }
- gsk_path_builder_line_to (builder, self->x + self->width, self->y + self->height);
- }
- start -= h;
- end -= h;
-
- if (start < w)
- {
- if (start >= 0)
- gsk_path_builder_move_to (builder, self->x + (w - start) * (w / self->width), self->y + self->height);
- if (end <= w)
- {
- gsk_path_builder_line_to (builder, self->x + (w - end) * (w / self->width), self->y + self->height);
- return;
- }
- gsk_path_builder_line_to (builder, self->x, self->y + self->height);
- }
- start -= w;
- end -= w;
-
- if (start < h)
- {
- if (start >= 0)
- gsk_path_builder_move_to (builder, self->x, self->y + (h - start) * (h / self->height));
- if (end <= h)
- {
- gsk_path_builder_line_to (builder, self->x, self->y + (h - end) * (h / self->height));
- return;
- }
- gsk_path_builder_line_to (builder, self->x, self->y);
- }
-}
-
-static int
-gsk_rect_contour_get_winding (const GskContour *contour,
- gpointer measure_data,
- const graphene_point_t *point,
- gboolean *on_edge)
-{
- const GskRectContour *self = (const GskRectContour *) contour;
- graphene_rect_t rect;
-
- graphene_rect_init (&rect, self->x, self->y, self->width, self->height);
-
- if (graphene_rect_contains_point (&rect, point))
- return -1;
-
- return 0;
-}
-
-static const GskContourClass GSK_RECT_CONTOUR_CLASS =
-{
- sizeof (GskRectContour),
- "GskRectContour",
- gsk_contour_get_size_default,
- gsk_rect_contour_get_flags,
- gsk_rect_contour_print,
- gsk_rect_contour_get_bounds,
- gsk_rect_contour_get_start_end,
- gsk_rect_contour_foreach,
- gsk_rect_contour_init_measure,
- gsk_rect_contour_free_measure,
- gsk_rect_contour_get_point,
- gsk_rect_contour_get_closest_point,
- gsk_rect_contour_copy,
- gsk_rect_contour_add_segment,
- gsk_rect_contour_get_winding
-};
-
-GskContour *
-gsk_rect_contour_new (const graphene_rect_t *rect)
-{
- GskRectContour *self;
-
- self = g_new0 (GskRectContour, 1);
-
- self->contour.klass = &GSK_RECT_CONTOUR_CLASS;
-
- 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 */
-
-#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
-#define RAD_TO_DEG(x) ((x) / (G_PI / 180.f))
-
-typedef struct _GskCircleContour GskCircleContour;
-struct _GskCircleContour
-{
- GskContour contour;
-
- graphene_point_t center;
- float radius;
- float start_angle; /* in degrees */
- float end_angle; /* start_angle +/- 360 */
-};
-
-static GskPathFlags
-gsk_circle_contour_get_flags (const GskContour *contour)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
-
- /* XXX: should we explicitly close paths? */
- if (fabs (self->start_angle - self->end_angle) >= 360)
- return GSK_PATH_CLOSED;
- else
- return 0;
-}
-
-#define GSK_CIRCLE_POINT_INIT(self, angle) \
- GRAPHENE_POINT_INIT ((self)->center.x + cos (DEG_TO_RAD (angle)) * self->radius, \
- (self)->center.y + sin (DEG_TO_RAD (angle)) * self->radius)
-
-static void
-gsk_circle_contour_print (const GskContour *contour,
- GString *string)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
- float mid_angle = (self->end_angle - self->start_angle) / 2;
-
- g_string_append (string, "M ");
- _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, self->start_angle));
- g_string_append (string, " A ");
- _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->radius, self->radius));
- g_string_append_printf (string, " 0 0 %u ",
- self->start_angle < self->end_angle ? 0 : 1);
- _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, mid_angle));
- g_string_append (string, " A ");
- _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->radius, self->radius));
- g_string_append_printf (string, " 0 0 %u ",
- self->start_angle < self->end_angle ? 0 : 1);
- _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, self->end_angle));
- if (fabs (self->start_angle - self->end_angle) >= 360)
- g_string_append (string, " z");
-}
-
-static gboolean
-gsk_circle_contour_get_bounds (const GskContour *contour,
- graphene_rect_t *rect)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
-
- /* XXX: handle partial circles */
- graphene_rect_init (rect,
- self->center.x - self->radius,
- self->center.y - self->radius,
- 2 * self->radius,
- 2 * self->radius);
-
- return TRUE;
-}
-
-static void
-gsk_circle_contour_get_start_end (const GskContour *contour,
- graphene_point_t *start,
- graphene_point_t *end)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
-
- if (start)
- *start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
-
- if (end)
- *end = GSK_CIRCLE_POINT_INIT (self, self->end_angle);
-}
-
-typedef struct
-{
- GskPathForeachFunc func;
- gpointer user_data;
-} ForeachWrapper;
-
-static gboolean
-gsk_circle_contour_curve (const graphene_point_t curve[4],
- gpointer data)
-{
- ForeachWrapper *wrapper = data;
-
- return wrapper->func (GSK_PATH_CURVE, curve, 4, wrapper->user_data);
-}
-
-static gboolean
-gsk_circle_contour_foreach (const GskContour *contour,
- float tolerance,
- GskPathForeachFunc func,
- gpointer user_data)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
- graphene_point_t start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
-
- if (!func (GSK_PATH_MOVE, &start, 1, user_data))
- return FALSE;
-
- if (!gsk_spline_decompose_arc (&self->center,
- self->radius,
- tolerance,
- DEG_TO_RAD (self->start_angle),
- DEG_TO_RAD (self->end_angle),
- gsk_circle_contour_curve,
- &(ForeachWrapper) { func, user_data }))
- return FALSE;
-
- if (fabs (self->start_angle - self->end_angle) >= 360)
- {
- if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, user_data))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gpointer
-gsk_circle_contour_init_measure (const GskContour *contour,
- float tolerance,
- float *out_length)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
-
- *out_length = DEG_TO_RAD (fabs (self->start_angle - self->end_angle)) * self->radius;
-
- return NULL;
-}
-
-static void
-gsk_circle_contour_free_measure (const GskContour *contour,
- gpointer data)
-{
-}
-
-static void
-gsk_circle_contour_get_point (const GskContour *contour,
- gpointer measure_data,
- float distance,
- graphene_point_t *pos,
- graphene_vec2_t *tangent)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
- float delta = self->end_angle - self->start_angle;
- float length = self->radius * DEG_TO_RAD (delta);
- float angle = self->start_angle + distance/length * delta;
- graphene_point_t p;
-
- p = GSK_CIRCLE_POINT_INIT (self, angle);
-
- if (pos)
- *pos = p;
-
- if (tangent)
- {
- graphene_vec2_init (tangent,
- p.y - self->center.y,
- - p.x + self->center.x);
- graphene_vec2_normalize (tangent, tangent);
- }
-}
-
-static gboolean
-gsk_circle_contour_get_closest_point (const GskContour *contour,
- gpointer measure_data,
- float tolerance,
- const graphene_point_t *point,
- float threshold,
- float *out_distance,
- graphene_point_t *out_pos,
- float *out_offset,
- graphene_vec2_t *out_tangent)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
- float angle;
- float closest_angle;
- float offset;
- graphene_point_t pos;
- graphene_vec2_t tangent;
- float distance;
-
- if (graphene_point_distance (point, &self->center, NULL, NULL) > threshold + self->radius)
- return FALSE;
-
- angle = atan2f (point->y - self->center.y, point->x - self->center.x);
- angle = RAD_TO_DEG (angle);
- if (angle < 0)
- angle += 360;
-
- if ((self->start_angle <= angle && angle <= self->end_angle) ||
- (self->end_angle <= angle && angle <= self->start_angle))
- {
- closest_angle = angle;
- }
- else
- {
- float d1, d2;
-
- d1 = fabs (self->start_angle - angle);
- d1 = MIN (d1, 360 - d1);
- d2 = fabs (self->end_angle - angle);
- d2 = MIN (d2, 360 - d2);
- if (d1 < d2)
- closest_angle = self->start_angle;
- else
- closest_angle = self->end_angle;
- }
-
- offset = self->radius * 2 * M_PI * (closest_angle - self->start_angle) / (self->end_angle - self->start_angle);
-
- gsk_circle_contour_get_point (contour, NULL, offset, &pos, out_tangent ? &tangent : NULL);
-
- distance = graphene_point_distance (&pos, point, NULL, NULL);
- if (threshold < distance)
- return FALSE;
-
- if (out_offset)
- *out_offset = offset;
- if (out_pos)
- *out_pos = pos;
- if (out_distance)
- *out_distance = distance;
- if (out_tangent)
- *out_tangent = tangent;
-
- return TRUE;
-}
-
-static void
-gsk_circle_contour_copy (const GskContour *contour,
- GskContour *dest)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
- GskCircleContour *target = (GskCircleContour *) dest;
-
- *target = *self;
-}
-
-static void
-gsk_circle_contour_add_segment (const GskContour *contour,
- GskPathBuilder *builder,
- gpointer measure_data,
- float start,
- float end)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
- float delta = self->end_angle - self->start_angle;
- float length = self->radius * DEG_TO_RAD (delta);
- GskContour *segment;
-
- 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 int
-gsk_circle_contour_get_winding (const GskContour *contour,
- gpointer measure_data,
- const graphene_point_t *point,
- gboolean *on_edge)
-{
- const GskCircleContour *self = (const GskCircleContour *) contour;
-
- if (graphene_point_distance (point, &self->center, NULL, NULL) >= self->radius)
- return 0;
-
- if (fabs (self->start_angle - self->end_angle) >= 360)
- {
- return -1;
- }
- else
- {
- /* Check if the point and the midpoint are on the same side
- * of the chord through start and end.
- */
- double mid_angle = (self->end_angle - self->start_angle) / 2;
- graphene_point_t start = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (self->start_angle)) * self->radius,
- self->center.y + sin (DEG_TO_RAD (self->start_angle)) * self->radius);
- graphene_point_t mid = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (mid_angle)) * self->radius,
- self->center.y + sin (DEG_TO_RAD (mid_angle)) * self->radius);
- graphene_point_t end = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (self->end_angle)) * self->radius,
- self->center.y + sin (DEG_TO_RAD (self->end_angle)) * self->radius);
-
- graphene_vec2_t n, m;
- float a, b;
-
- graphene_vec2_init (&n, start.y - end.y, end.x - start.x);
- graphene_vec2_init (&m, mid.x, mid.y);
- a = graphene_vec2_dot (&m, &n);
- graphene_vec2_init (&m, point->x, point->y);
- b = graphene_vec2_dot (&m, &n);
-
- if ((a < 0) != (b < 0))
- return -1;
- }
-
- return 0;
-}
-
-static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
-{
- sizeof (GskCircleContour),
- "GskCircleContour",
- gsk_contour_get_size_default,
- gsk_circle_contour_get_flags,
- gsk_circle_contour_print,
- gsk_circle_contour_get_bounds,
- gsk_circle_contour_get_start_end,
- gsk_circle_contour_foreach,
- gsk_circle_contour_init_measure,
- gsk_circle_contour_free_measure,
- gsk_circle_contour_get_point,
- gsk_circle_contour_get_closest_point,
- gsk_circle_contour_copy,
- gsk_circle_contour_add_segment,
- gsk_circle_contour_get_winding
-};
-
-GskContour *
-gsk_circle_contour_new (const graphene_point_t *center,
- float radius,
- float start_angle,
- float end_angle)
-{
- GskCircleContour *self;
-
- self = g_new0 (GskCircleContour, 1);
-
- self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
-
- g_assert (fabs (start_angle - end_angle) <= 360);
-
- self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS;
- self->center = *center;
- self->radius = radius;
- self->start_angle = start_angle;
- self->end_angle = end_angle;
-
- return (GskContour *) self;
-}
-
-/* STANDARD CONTOUR */
-
-typedef struct _GskStandardContour GskStandardContour;
-struct _GskStandardContour
-{
- GskContour contour;
-
- GskPathFlags flags;
-
- gsize n_ops;
- gsize n_points;
- graphene_point_t *points;
- GskStandardOperation ops[];
-};
-
-static gsize
-gsk_standard_contour_compute_size (gsize n_ops,
- gsize n_points)
-{
- return sizeof (GskStandardContour)
- + sizeof (GskStandardOperation) * n_ops
- + sizeof (graphene_point_t) * n_points;
-}
-
-static gsize
-gsk_standard_contour_get_size (const GskContour *contour)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
-
- return gsk_standard_contour_compute_size (self->n_ops, self->n_points);
-}
-
-static gboolean
-gsk_standard_contour_foreach (const GskContour *contour,
- float tolerance,
- GskPathForeachFunc func,
- gpointer user_data)
-{
- 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 (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static GskPathFlags
-gsk_standard_contour_get_flags (const GskContour *contour)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
-
- return self->flags;
-}
-
-static void
-gsk_standard_contour_print (const GskContour *contour,
- GString *string)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
- gsize i;
-
- for (i = 0; i < self->n_ops; i ++)
- {
- graphene_point_t *pt = &self->points[self->ops[i].point];
-
- switch (self->ops[i].op)
- {
- case GSK_PATH_MOVE:
- g_string_append (string, "M ");
- _g_string_append_point (string, &pt[0]);
- break;
-
- case GSK_PATH_CLOSE:
- g_string_append (string, " Z");
- break;
-
- case GSK_PATH_LINE:
- g_string_append (string, " L ");
- _g_string_append_point (string, &pt[1]);
- break;
-
- case GSK_PATH_CURVE:
- g_string_append (string, " C ");
- _g_string_append_point (string, &pt[1]);
- g_string_append (string, ", ");
- _g_string_append_point (string, &pt[2]);
- g_string_append (string, ", ");
- _g_string_append_point (string, &pt[3]);
- break;
-
- default:
- g_assert_not_reached();
- return;
- }
- }
-}
-
-static void
-rect_add_point (graphene_rect_t *rect,
- const graphene_point_t *point)
-{
- if (point->x < rect->origin.x)
- {
- rect->size.width += rect->origin.x - point->x;
- rect->origin.x = point->x;
- }
- else if (point->x > rect->origin.x + rect->size.width)
- {
- rect->size.width = point->x - rect->origin.x;
- }
-
- if (point->y < rect->origin.y)
- {
- rect->size.height += rect->origin.y - point->y;
- rect->origin.y = point->y;
- }
- else if (point->y > rect->origin.y + rect->size.height)
- {
- rect->size.height = point->y - rect->origin.y;
- }
-}
-
-static gboolean
-gsk_standard_contour_get_bounds (const GskContour *contour,
- graphene_rect_t *bounds)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
- gsize i;
-
- if (self->n_points == 0)
- return FALSE;
-
- graphene_rect_init (bounds,
- self->points[0].x, self->points[0].y,
- 0, 0);
-
- for (i = 1; i < self->n_points; i ++)
- {
- rect_add_point (bounds, &self->points[i]);
- }
-
- return bounds->size.width > 0 && bounds->size.height > 0;
-}
-
-static void
-gsk_standard_contour_get_start_end (const GskContour *contour,
- graphene_point_t *start,
- graphene_point_t *end)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
-
- if (start)
- *start = self->points[0];
-
- if (end)
- *end = self->points[self->n_points - 1];
-}
-
-typedef struct
-{
- float start;
- float end;
- float start_progress;
- float end_progress;
- graphene_point_t end_point;
- gsize op;
-} GskStandardContourMeasure;
-
-typedef struct
-{
- GArray *array;
- GskStandardContourMeasure measure;
-} LengthDecompose;
-
-static void
-gsk_standard_contour_measure_add_point (const graphene_point_t *from,
- const graphene_point_t *to,
- float from_progress,
- float to_progress,
- gpointer user_data)
-{
- LengthDecompose *decomp = user_data;
- float seg_length;
-
- seg_length = graphene_point_distance (from, to, NULL, NULL);
- decomp->measure.end += seg_length;
- decomp->measure.start_progress = from_progress;
- decomp->measure.end_progress = to_progress;
- decomp->measure.end_point = *to;
-
- g_array_append_val (decomp->array, decomp->measure);
-
- decomp->measure.start += seg_length;
-}
-
-static gpointer
-gsk_standard_contour_init_measure (const GskContour *contour,
- float tolerance,
- float *out_length)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
- gsize i;
- float length, seg_length;
- GArray *array;
-
- array = g_array_new (FALSE, FALSE, sizeof (GskStandardContourMeasure));
- length = 0;
-
- for (i = 1; i < self->n_ops; i ++)
- {
- graphene_point_t *pt = &self->points[self->ops[i].point];
-
- switch (self->ops[i].op)
- {
- case GSK_PATH_MOVE:
- break;
-
- case GSK_PATH_CLOSE:
- case GSK_PATH_LINE:
- seg_length = graphene_point_distance (&pt[0], &pt[1], NULL, NULL);
- if (seg_length > 0)
- {
- g_array_append_vals (array,
- &(GskStandardContourMeasure) {
- length,
- length + seg_length,
- 0, 1,
- pt[1],
- i,
- }, 1);
- length += seg_length;
- }
- break;
-
- case GSK_PATH_CURVE:
- {
- LengthDecompose decomp = { array, { length, length, 0, 0, pt[0], i } };
- gsk_spline_decompose_cubic (pt, tolerance, gsk_standard_contour_measure_add_point, &decomp);
- length = decomp.measure.start;
- }
- break;
-
- default:
- g_assert_not_reached();
- return NULL;
- }
- }
-
- *out_length = length;
-
- return array;
-}
-
-static void
-gsk_standard_contour_free_measure (const GskContour *contour,
- gpointer data)
-{
- g_array_free (data, TRUE);
-}
-
-static int
-gsk_standard_contour_find_measure (gconstpointer m,
- gconstpointer l)
-{
- const GskStandardContourMeasure *measure = m;
- float length = *(const float *) l;
-
- if (measure->start > length)
- return 1;
- else if (measure->end <= length)
- return -1;
- else
- return 0;
-}
-
-static void
-gsk_standard_contour_measure_get_point (GskStandardContour *self,
- gsize op,
- float progress,
- graphene_point_t *pos,
- graphene_vec2_t *tangent)
-{
- const graphene_point_t *pts;
-
- pts = &self->points[self->ops[op].point];
- switch (self->ops[op].op)
- {
- case GSK_PATH_LINE:
- case GSK_PATH_CLOSE:
- if (pos)
- graphene_point_interpolate (&pts[0], &pts[1], progress, pos);
- if (tangent)
- {
- graphene_vec2_init (tangent, pts[1].x - pts[0].x, pts[1].y - pts[0].y);
- graphene_vec2_normalize (tangent, tangent);
- }
- break;
-
- case GSK_PATH_CURVE:
- gsk_spline_get_point_cubic (pts, progress, pos, tangent);
- break;
-
- case GSK_PATH_MOVE:
- default:
- g_assert_not_reached ();
- return;
- }
-}
-
-static void
-gsk_standard_contour_get_point (const GskContour *contour,
- gpointer measure_data,
- float distance,
- graphene_point_t *pos,
- graphene_vec2_t *tangent)
-{
- GskStandardContour *self = (GskStandardContour *) contour;
- GArray *array = measure_data;
- guint index;
- float progress;
- GskStandardContourMeasure *measure;
-
- if (array->len == 0)
- {
- g_assert (distance == 0);
- g_assert (self->ops[0].op == GSK_PATH_MOVE);
- if (pos)
- *pos = self->points[0];
- if (tangent)
- graphene_vec2_init (tangent, 1.f, 0.f);
- return;
- }
-
- if (!g_array_binary_search (array, &distance, gsk_standard_contour_find_measure, &index))
- index = array->len - 1;
- measure = &g_array_index (array, GskStandardContourMeasure, index);
- progress = (distance - measure->start) / (measure->end - measure->start);
- progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
- g_assert (progress >= 0 && progress <= 1);
-
- gsk_standard_contour_measure_get_point (self, measure->op, progress, pos, tangent);
-}
-
-static gboolean
-gsk_standard_contour_get_closest_point (const GskContour *contour,
- gpointer measure_data,
- float tolerance,
- const graphene_point_t *point,
- float threshold,
- float *out_distance,
- graphene_point_t *out_pos,
- float *out_offset,
- graphene_vec2_t *out_tangent)
-{
- GskStandardContour *self = (GskStandardContour *) contour;
- GskStandardContourMeasure *measure;
- float progress, dist;
- GArray *array = measure_data;
- graphene_point_t p, last_point;
- gsize i;
- gboolean result = FALSE;
-
- g_assert (self->ops[0].op == GSK_PATH_MOVE);
- last_point = self->points[0];
-
- if (array->len == 0)
- {
- /* This is the special case for point-only */
- dist = graphene_point_distance (&last_point, point, NULL, NULL);
-
- if (dist > threshold)
- return FALSE;
-
- if (out_offset)
- *out_offset = 0;
-
- if (out_distance)
- *out_distance = dist;
-
- if (out_pos)
- *out_pos = last_point;
-
- if (out_tangent)
- *out_tangent = *graphene_vec2_x_axis ();
-
- return TRUE;
- }
-
- for (i = 0; i < array->len; i++)
- {
- measure = &g_array_index (array, GskStandardContourMeasure, i);
-
- gsk_find_point_on_line (&last_point,
- &measure->end_point,
- point,
- &progress,
- &p);
- last_point = measure->end_point;
- dist = graphene_point_distance (point, &p, NULL, NULL);
- /* add some wiggleroom for the accurate check below */
- //g_print ("%zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
- if (dist <= threshold + 1.0f)
- {
- graphene_vec2_t t;
- float found_progress;
-
- found_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
-
- gsk_standard_contour_measure_get_point (self, measure->op, found_progress, &p, out_tangent ? &t : NULL);
- dist = graphene_point_distance (point, &p, NULL, NULL);
- /* double check that the point actually is closer */
- //g_print ("!!! %zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
- if (dist <= threshold)
- {
- if (out_distance)
- *out_distance = dist;
- if (out_pos)
- *out_pos = p;
- if (out_offset)
- *out_offset = measure->start + (measure->end - measure->start) * progress;
- if (out_tangent)
- *out_tangent = t;
- result = TRUE;
- if (tolerance >= dist)
- return TRUE;
- threshold = dist - tolerance;
- }
- }
- }
-
- return result;
-}
-
-static void
-gsk_standard_contour_init (GskContour *contour,
- GskPathFlags flags,
- const GskStandardOperation *ops,
- gsize n_ops,
- const graphene_point_t *points,
- gsize n_points);
-
-static void
-gsk_standard_contour_copy (const GskContour *contour,
- GskContour *dest)
-{
- const GskStandardContour *self = (const GskStandardContour *) contour;
-
- gsk_standard_contour_init (dest, self->flags, self->ops, self->n_ops, self->points, self->n_points);
-}
-
-static void
-gsk_standard_contour_add_segment (const GskContour *contour,
- GskPathBuilder *builder,
- gpointer measure_data,
- float start,
- float end)
-{
- GskStandardContour *self = (GskStandardContour *) contour;
- GArray *array = measure_data;
- guint start_index, end_index;
- float start_progress, end_progress;
- GskStandardContourMeasure *start_measure, *end_measure;
- gsize i;
-
- if (start > 0)
- {
- if (!g_array_binary_search (array, (float[1]) { start }, gsk_standard_contour_find_measure, &start_index))
- start_index = array->len - 1;
- start_measure = &g_array_index (array, GskStandardContourMeasure, start_index);
- start_progress = (start - start_measure->start) / (start_measure->end - start_measure->start);
- start_progress = start_measure->start_progress + (start_measure->end_progress - start_measure->start_progress) * start_progress;
- g_assert (start_progress >= 0 && start_progress <= 1);
- }
- else
- {
- start_measure = NULL;
- start_progress = 0.0;
- }
-
- if (g_array_binary_search (array, (float[1]) { end }, gsk_standard_contour_find_measure, &end_index))
- {
- end_measure = &g_array_index (array, GskStandardContourMeasure, end_index);
- end_progress = (end - end_measure->start) / (end_measure->end - end_measure->start);
- end_progress = end_measure->start_progress + (end_measure->end_progress - end_measure->start_progress) * end_progress;
- g_assert (end_progress >= 0 && end_progress <= 1);
- }
- else
- {
- end_measure = NULL;
- end_progress = 1.0;
- }
-
- /* Add the first partial operation,
- * taking care that first and last operation might be identical */
- if (start_measure)
- {
- switch (self->ops[start_measure->op].op)
- {
- case GSK_PATH_CLOSE:
- case GSK_PATH_LINE:
- {
- graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
- graphene_point_t point;
-
- graphene_point_interpolate (&pts[0], &pts[1], start_progress, &point);
- gsk_path_builder_move_to (builder, point.x, point.y);
- if (end_measure && end_measure->op == start_measure->op)
- {
- graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
- gsk_path_builder_line_to (builder, point.x, point.y);
- return;
- }
- gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
- }
- break;
-
- case GSK_PATH_CURVE:
- {
- graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
- graphene_point_t curve[4], discard[4];
-
- gsk_spline_split_cubic (pts, discard, curve, start_progress);
- if (end_measure && end_measure->op == start_measure->op)
- {
- graphene_point_t tiny[4];
- gsk_spline_split_cubic (curve, tiny, discard, (end_progress - start_progress) / (1 - start_progress));
- gsk_path_builder_move_to (builder, tiny[0].x, tiny[0].y);
- gsk_path_builder_curve_to (builder, tiny[1].x, tiny[1].y, tiny[2].x, tiny[2].y, tiny[3].x, tiny[3].y);
- return;
- }
- gsk_path_builder_move_to (builder, curve[0].x, curve[0].y);
- gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
- }
- break;
-
- case GSK_PATH_MOVE:
- default:
- g_assert_not_reached();
- return;
- }
- i = start_measure->op + 1;
- }
- else
- i = 0;
-
- for (; i < (end_measure ? end_measure->op : self->n_ops); i++)
- {
- graphene_point_t *pt = &self->points[self->ops[i].point];
-
- switch (self->ops[i].op)
- {
- case GSK_PATH_MOVE:
- gsk_path_builder_move_to (builder, pt[0].x, pt[0].y);
- break;
-
- case GSK_PATH_LINE:
- case GSK_PATH_CLOSE:
- gsk_path_builder_line_to (builder, pt[1].x, pt[1].y);
- break;
-
- case GSK_PATH_CURVE:
- gsk_path_builder_curve_to (builder, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
- break;
-
- default:
- g_assert_not_reached();
- return;
- }
- }
-
- /* Add the last partial operation */
- if (end_measure)
- {
- switch (self->ops[end_measure->op].op)
- {
- case GSK_PATH_CLOSE:
- case GSK_PATH_LINE:
- {
- graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
- graphene_point_t point;
-
- graphene_point_interpolate (&pts[0], &pts[1], end_progress, &point);
- gsk_path_builder_line_to (builder, point.x, point.y);
- }
- break;
-
- case GSK_PATH_CURVE:
- {
- graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
- graphene_point_t curve[4], discard[4];
-
- gsk_spline_split_cubic (pts, curve, discard, end_progress);
- gsk_path_builder_curve_to (builder, curve[1].x, curve[1].y, curve[2].x, curve[2].y, curve[3].x, curve[3].y);
- }
- break;
-
- case GSK_PATH_MOVE:
- default:
- g_assert_not_reached();
- return;
- }
- }
-}
-
-static inline int
-line_get_crossing (const graphene_point_t *p,
- const graphene_point_t *p1,
- const graphene_point_t *p2,
- gboolean *on_edge)
-{
- int dir = 1;
-
- if (p1->x >= p->x && p2->x >= p->x)
- return 0;
-
- if (p2->y < p1->y)
- {
- const graphene_point_t *tmp;
- tmp = p1;
- p1 = p2;
- p2 = tmp;
- dir = -1;
- }
-
- if ((p1->x >= p->x && p1->y == p->y) ||
- (p2->x >= p->x && p2->y == p->y))
- {
- *on_edge = TRUE;
- return 0;
- }
-
- if (p2->y <= p->y || p1->y > p->y)
- return 0;
-
- if (p1->x <= p->x && p2->x <= p->x)
- return dir;
-
- if (p->x > p1->x + (p->y - p1->y) * (p2->x - p1->x) / (p2->y - p1->y))
- return dir;
-
- return 0;
-}
-
-static int
-gsk_standard_contour_get_winding (const GskContour *contour,
- gpointer measure_data,
- const graphene_point_t *point,
- gboolean *on_edge)
-{
- GskStandardContour *self = (GskStandardContour *) contour;
- GArray *array = measure_data;
- graphene_point_t last_point;
- int winding;
- int i;
-
- if (array->len == 0)
- return 0;
-
- winding = 0;
- last_point = self->points[0];
- for (i = 0; i < array->len; i++)
- {
- GskStandardContourMeasure *measure;
-
- measure = &g_array_index (array, GskStandardContourMeasure, i);
- winding += line_get_crossing (point, &last_point, &measure->end_point, on_edge);
- if (*on_edge)
- return 0;
-
- last_point = measure->end_point;
- }
-
- winding += line_get_crossing (point, &last_point, &self->points[0], on_edge);
-
- return winding;
-}
-
-static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
-{
- sizeof (GskStandardContour),
- "GskStandardContour",
- gsk_standard_contour_get_size,
- gsk_standard_contour_get_flags,
- gsk_standard_contour_print,
- gsk_standard_contour_get_bounds,
- gsk_standard_contour_get_start_end,
- gsk_standard_contour_foreach,
- gsk_standard_contour_init_measure,
- gsk_standard_contour_free_measure,
- gsk_standard_contour_get_point,
- gsk_standard_contour_get_closest_point,
- gsk_standard_contour_copy,
- gsk_standard_contour_add_segment,
- gsk_standard_contour_get_winding
-};
-
-/* You must ensure the contour has enough size allocated,
- * 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)
-{
- GskStandardContour *self = (GskStandardContour *) contour;
-
- 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);
-}
-
-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
-gsk_contour_get_size (const GskContour *contour)
-{
- return contour->klass->get_size (contour);
-}
-
-static gboolean
-gsk_contour_foreach (const GskContour *contour,
- float tolerance,
- GskPathForeachFunc func,
- gpointer user_data)
-{
- return contour->klass->foreach (contour, tolerance, func, user_data);
-}
-
-gpointer
-gsk_contour_init_measure (const GskContour *self,
- float tolerance,
- float *out_length)
-{
- return self->klass->init_measure (self, tolerance, out_length);
-}
-
-void
-gsk_contour_free_measure (const GskContour *self,
- gpointer data)
-{
- self->klass->free_measure (self, data);
-}
-
-void
-gsk_contour_get_start_end (const GskContour *self,
- graphene_point_t *start,
- graphene_point_t *end)
-{
- self->klass->get_start_end (self, start, end);
-}
-
-void
-gsk_contour_get_point (const GskContour *self,
- gpointer measure_data,
- float distance,
- graphene_point_t *pos,
- graphene_vec2_t *tangent)
-{
- self->klass->get_point (self, measure_data, distance, pos, tangent);
-}
-
-gboolean
-gsk_contour_get_closest_point (const GskContour *self,
- gpointer measure_data,
- float tolerance,
- const graphene_point_t *point,
- float threshold,
- float *out_distance,
- graphene_point_t *out_pos,
- float *out_offset,
- graphene_vec2_t *out_tangent)
-{
- return self->klass->get_closest_point (self,
- measure_data,
- tolerance,
- point,
- threshold,
- out_distance,
- out_pos,
- out_offset,
- 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);
-}
-
-int
-gsk_contour_get_winding (const GskContour *self,
- gpointer measure_data,
- const graphene_point_t *point,
- gboolean *on_edge)
-{
- return self->klass->get_winding (self, measure_data, point, on_edge);
-}
-
-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 */
-
GskPath *
gsk_path_new_from_contours (const GSList *contours)
{
@@ -1769,7 +81,7 @@ gsk_path_new_from_contours (const GSList *contours)
n_contours++;
size += sizeof (GskContour *);
size += gsk_contour_get_size (contour);
- flags &= contour->klass->get_flags (contour);
+ flags &= gsk_contour_get_flags (contour);
}
path = g_malloc0 (sizeof (GskPath) + size);
@@ -1919,7 +231,7 @@ gsk_path_print (GskPath *self,
{
if (i > 0)
g_string_append_c (string, ' ');
- self->contours[i]->klass->print (self->contours[i], string);
+ gsk_contour_print (self->contours[i], string);
}
}
@@ -2074,7 +386,7 @@ gsk_path_get_bounds (GskPath *self,
for (i = 0; i < self->n_contours; i++)
{
- if (self->contours[i]->klass->get_bounds (self->contours[i], bounds))
+ if (gsk_contour_get_bounds (self->contours[i], bounds))
break;
}
@@ -2088,7 +400,7 @@ gsk_path_get_bounds (GskPath *self,
{
graphene_rect_t tmp;
- if (self->contours[i]->klass->get_bounds (self->contours[i], &tmp))
+ if (gsk_contour_get_bounds (self->contours[i], &tmp))
graphene_rect_union (bounds, &tmp, bounds);
}
diff --git a/gsk/gskpathprivate.h b/gsk/gskpathprivate.h
index 7027267764..e9d9c386d9 100644
--- a/gsk/gskpathprivate.h
+++ b/gsk/gskpathprivate.h
@@ -23,81 +23,24 @@
#include "gskpath.h"
+#include "gskcontourprivate.h"
+
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,
- GskPathForeachFlags flags,
- double tolerance,
- GskPathForeachFunc func,
- gpointer user_data);
+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,
+ GskPathForeachFlags flags,
+ double tolerance,
+ GskPathForeachFunc func,
+ gpointer user_data);
-GskContour * gsk_contour_dup (const GskContour *src);
-gpointer gsk_contour_init_measure (const GskContour *self,
- float tolerance,
- float *out_length);
-void gsk_contour_free_measure (const GskContour *self,
- gpointer data);
-void gsk_contour_get_start_end (const GskContour *self,
- graphene_point_t *start,
- graphene_point_t *end);
-void gsk_contour_get_point (const GskContour *self,
- gpointer measure_data,
- float distance,
- graphene_point_t *pos,
- graphene_vec2_t *tangent);
-gboolean gsk_contour_get_closest_point (const GskContour *self,
- gpointer measure_data,
- float tolerance,
- const graphene_point_t *point,
- float threshold,
- float *out_distance,
- graphene_point_t *out_pos,
- float *out_offset,
- graphene_vec2_t *out_tangent);
-int gsk_contour_get_winding (const GskContour *self,
- gpointer measure_data,
- const graphene_point_t *point,
- gboolean *on_edge);
-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,
GskContour *contour);
diff --git a/gsk/meson.build b/gsk/meson.build
index a858694ffb..55f37e1c3e 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -39,6 +39,7 @@ gsk_public_sources = files([
gsk_private_sources = files([
'gskcairoblur.c',
+ 'gskcontour.c',
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',