diff --git a/gsk/gsk.h b/gsk/gsk.h index 90c4d1c259..9f2ee451dd 100644 --- a/gsk/gsk.h +++ b/gsk/gsk.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/gsk/gskpathmeasure.c b/gsk/gskpathmeasure.c new file mode 100644 index 0000000000..2242adf0e3 --- /dev/null +++ b/gsk/gskpathmeasure.c @@ -0,0 +1,327 @@ +/* + * 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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gskpathmeasure.h" + +#include "gskpathbuilder.h" +#include "gskpathpointprivate.h" +#include "gskpathprivate.h" + +/** + * GskPathMeasure: + * + * `GskPathMeasure` is an object that allows measurements + * on `GskPath`s such as determining the length of the path. + * + * Many measuring operations require sampling the path length + * at intermediate points. Therefore, a `GskPathMeasure` has + * a tolerance that determines what precision is required + * for such approximations. + * + * A `GskPathMeasure` struct is a reference counted struct + * and should be treated as opaque. + */ + +typedef struct _GskContourMeasure GskContourMeasure; + +struct _GskContourMeasure +{ + float length; + gpointer contour_data; +}; + +struct _GskPathMeasure +{ + /*< private >*/ + guint ref_count; + + GskPath *path; + float tolerance; + + float length; + gsize n_contours; + GskContourMeasure measures[]; +}; + +G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure, + gsk_path_measure_ref, + gsk_path_measure_unref) + +/** + * gsk_path_measure_new: + * @path: the path to measure + * + * Creates a measure object for the given @path. + * + * Returns: a new `GskPathMeasure` representing @path + * + * Since: 4.14 + */ +GskPathMeasure * +gsk_path_measure_new (GskPath *path) +{ + return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT); +} + +/** + * gsk_path_measure_new_with_tolerance: + * @path: the path to measure + * @tolerance: the tolerance for measuring operations + * + * Creates a measure object for the given @path and @tolerance. + * + * Returns: a new `GskPathMeasure` representing @path + * + * Since: 4.14 + */ +GskPathMeasure * +gsk_path_measure_new_with_tolerance (GskPath *path, + float tolerance) +{ + GskPathMeasure *self; + gsize i, n_contours; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (tolerance > 0, NULL); + + n_contours = gsk_path_get_n_contours (path); + + self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure)); + + self->ref_count = 1; + self->path = gsk_path_ref (path); + self->tolerance = tolerance; + self->n_contours = n_contours; + + for (i = 0; i < n_contours; i++) + { + self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (path, i), + self->tolerance, + &self->measures[i].length); + self->length += self->measures[i].length; + } + + return self; +} + +/** + * gsk_path_measure_ref: + * @self: a `GskPathMeasure` + * + * Increases the reference count of a `GskPathMeasure` by one. + * + * Returns: the passed in `GskPathMeasure`. + * + * Since: 4.14 + */ +GskPathMeasure * +gsk_path_measure_ref (GskPathMeasure *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + self->ref_count++; + + return self; +} + +/** + * gsk_path_measure_unref: + * @self: a `GskPathMeasure` + * + * Decreases the reference count of a `GskPathMeasure` by one. + * + * If the resulting reference count is zero, frees the object. + * + * Since: 4.14 + */ +void +gsk_path_measure_unref (GskPathMeasure *self) +{ + gsize i; + + g_return_if_fail (self != NULL); + g_return_if_fail (self->ref_count > 0); + + self->ref_count--; + if (self->ref_count > 0) + return; + + for (i = 0; i < self->n_contours; i++) + { + gsk_contour_free_measure (gsk_path_get_contour (self->path, i), + self->measures[i].contour_data); + } + + gsk_path_unref (self->path); + g_free (self); +} + +/** + * gsk_path_measure_get_path: + * @self: a `GskPathMeasure` + * + * Returns the path that the measure was created for. + * + * Returns: (transfer none): the path of @self + * + * Since: 4.14 + */ +GskPath * +gsk_path_measure_get_path (GskPathMeasure *self) +{ + return self->path; +} + +/** + * gsk_path_measure_get_tolerance: + * @self: a `GskPathMeasure` + * + * Returns the tolerance that the measure was created with. + * + * Returns: the tolerance of @self + * + * Since: 4.14 + */ +float +gsk_path_measure_get_tolerance (GskPathMeasure *self) +{ + return self->tolerance; +} + +/** + * gsk_path_measure_get_length: + * @self: a `GskPathMeasure` + * + * Gets the length of the path being measured. + * + * The length is cached, so this function does not do any work. + * + * Returns: The length of the path measured by @self + * + * Since: 4.14 + */ +float +gsk_path_measure_get_length (GskPathMeasure *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return self->length; +} + +static float +gsk_path_measure_clamp_distance (GskPathMeasure *self, + float distance) +{ + if (isnan (distance)) + return 0; + + return CLAMP (distance, 0, self->length); +} + +/** + * gsk_path_measure_get_point: + * @self: a `GskPathMeasure` + * @distance: the distance + * @result: (out caller-allocates): return location for the result + * + * Sets @result to the point at the given distance into the path. + * + * An empty path has no points, so `FALSE` is returned in that case. + * + * Returns: `TRUE` if @result was set + * + * Since: 4.14 + */ +gboolean +gsk_path_measure_get_point (GskPathMeasure *self, + float distance, + GskPathPoint *result) +{ + GskRealPathPoint *res = (GskRealPathPoint *) result; + gsize i; + const GskContour *contour; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (result != NULL, FALSE); + + if (self->n_contours == 0) + return FALSE; + + distance = gsk_path_measure_clamp_distance (self, distance); + + for (i = 0; i < self->n_contours - 1; i++) + { + if (distance < self->measures[i].length) + break; + + distance -= self->measures[i].length; + } + + g_assert (0 <= i && i < self->n_contours); + + distance = CLAMP (distance, 0, self->measures[i].length); + + contour = gsk_path_get_contour (self->path, i); + + gsk_contour_get_point (contour, self->measures[i].contour_data, distance, res); + res->contour = i; + + return TRUE; +} + +/** + * gsk_path_point_get_distance: + * @point: a `GskPathPoint on the path + * @measure: a `GskPathMeasure` for the path + * + * Returns the distance from the beginning of the path + * to @point. + * + * Returns: the distance of @point + * + * Since: 4.14 + */ +float +gsk_path_point_get_distance (const GskPathPoint *point, + GskPathMeasure *measure) +{ + GskRealPathPoint *p = (GskRealPathPoint *)point; + const GskContour *contour; + float contour_offset = 0; + + g_return_val_if_fail (point != NULL, 0); + g_return_val_if_fail (measure != NULL, 0); + g_return_val_if_fail (p->contour < measure->n_contours, 0); + + contour = gsk_path_get_contour (measure->path, p->contour); + + for (gsize i = 0; i < measure->n_contours; i++) + { + if (contour == gsk_path_get_contour (measure->path, i)) + return contour_offset + gsk_contour_get_distance (contour, + p, + measure->measures[i].contour_data); + + contour_offset += measure->measures[i].length; + } + + g_return_val_if_reached (0); +} diff --git a/gsk/gskpathmeasure.h b/gsk/gskpathmeasure.h new file mode 100644 index 0000000000..1f72abb726 --- /dev/null +++ b/gsk/gskpathmeasure.h @@ -0,0 +1,62 @@ +/* + * 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 . + * + * Authors: Benjamin Otte + */ + +#pragma once + +#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + + +#include +#include + +G_BEGIN_DECLS + +#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ()) + +GDK_AVAILABLE_IN_4_14 +GType gsk_path_measure_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_4_14 +GskPathMeasure * gsk_path_measure_new (GskPath *path); +GDK_AVAILABLE_IN_4_14 +GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path, + float tolerance); + +GDK_AVAILABLE_IN_4_14 +GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self); +GDK_AVAILABLE_IN_4_14 +void gsk_path_measure_unref (GskPathMeasure *self); + +GDK_AVAILABLE_IN_4_14 +GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_14 +float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE; + +GDK_AVAILABLE_IN_4_14 +float gsk_path_measure_get_length (GskPathMeasure *self); + +GDK_AVAILABLE_IN_4_14 +gboolean gsk_path_measure_get_point (GskPathMeasure *self, + float distance, + GskPathPoint *result); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref) + +G_END_DECLS diff --git a/gsk/gskpathpoint.h b/gsk/gskpathpoint.h index 13711b768a..e88128dbb6 100644 --- a/gsk/gskpathpoint.h +++ b/gsk/gskpathpoint.h @@ -78,5 +78,8 @@ float gsk_path_point_get_curvature (const GskPathPoint *poin GskPathDirection direction, graphene_point_t *center); +GDK_AVAILABLE_IN_4_14 +float gsk_path_point_get_distance (const GskPathPoint *point, + GskPathMeasure *measure); G_END_DECLS diff --git a/gsk/gsktypes.h b/gsk/gsktypes.h index 234f8fe1b0..f9554bc886 100644 --- a/gsk/gsktypes.h +++ b/gsk/gsktypes.h @@ -27,6 +27,7 @@ typedef struct _GskPath GskPath; typedef struct _GskPathBuilder GskPathBuilder; +typedef struct _GskPathMeasure GskPathMeasure; typedef struct _GskPathPoint GskPathPoint; typedef struct _GskRenderer GskRenderer; typedef struct _GskRenderNode GskRenderNode; diff --git a/gsk/meson.build b/gsk/meson.build index bd9807e55f..5cb2e14e56 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -28,6 +28,7 @@ gsk_public_sources = files([ 'gskglshader.c', 'gskpath.c', 'gskpathbuilder.c', + 'gskpathmeasure.c', 'gskpathpoint.c', 'gskrenderer.c', 'gskrendernode.c', @@ -75,6 +76,7 @@ gsk_public_headers = files([ 'gskglshader.h', 'gskpath.h', 'gskpathbuilder.h', + 'gskpathmeasure.h', 'gskpathpoint.h', 'gskrenderer.h', 'gskrendernode.h',