diff options
author | Benjamin Otte <otte@redhat.com> | 2020-11-30 19:25:19 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2020-12-27 00:31:18 +0100 |
commit | 0f332de3d31782e4d5785854d0d11fc256db3693 (patch) | |
tree | 7f9c2b517232d4457b2fc82f525d61d9c2b0003c | |
parent | 15e8f2bacc45e87c46a7c70972259e71abb6da9f (diff) | |
download | gtk+-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.c | 1735 | ||||
-rw-r--r-- | gsk/gskcontourprivate.h | 103 | ||||
-rw-r--r-- | gsk/gskpath.c | 1696 | ||||
-rw-r--r-- | gsk/gskpathprivate.h | 77 | ||||
-rw-r--r-- | gsk/meson.build | 1 |
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', |