diff --git a/src/cpml/cpml-gobject.c b/src/cpml/cpml-gobject.c index 2b4ddfdf..09fcf526 100644 --- a/src/cpml/cpml-gobject.c +++ b/src/cpml/cpml-gobject.c @@ -1,310 +1,322 @@ /* CPML - Cairo Path Manipulation Library * Copyright (C) 2007-2020 Nicola Fontana * * 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 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, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:cpml-gobject * @Section_Id:GObject * @title: GObject wrappers * @short_description: Collection of boxed wrappers for CPML structs * * These wrappers are supposed to make bindings development easier. * This file defines the wrappers and the functions needed for * implementing the CPML primitives as #GBoxed type. * * Since: 1.0 **/ +/** + * cpml_memdup: + * @src: the memory to copy. + * @size: the number of bytes to copy. + * + * A backward compatibility wrapper that calls g_memdup2() (if present) + * or g_memdup(). Needed because the latter has been deprecated because + * of a unfixable vulnerability (the size was guint instead of gsize). + * + * Since: 1.0 + **/ + #include "cpml-internal.h" #include #include #include "cpml-extents.h" #include "cpml-segment.h" #include "cpml-primitive.h" #include "cpml-curve.h" #include "cpml-gobject.h" GType cpml_pair_get_type(void) { static GType pair_type = 0; if (G_UNLIKELY(pair_type == 0)) pair_type = g_boxed_type_register_static("CpmlPair", (GBoxedCopyFunc) cpml_pair_dup, g_free); return pair_type; } /** * cpml_pair_dup: * @pair: a #CpmlPair structure * * Duplicates @pair. * * Returns: (transfer full): the duplicate of @pair: must be freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlPair * cpml_pair_dup(const CpmlPair *pair) { return cpml_memdup(pair, sizeof(CpmlPair)); } GType cpml_primitive_get_type(void) { static GType primitive_type = 0; if (G_UNLIKELY(primitive_type == 0)) primitive_type = g_boxed_type_register_static("CpmlPrimitive", (GBoxedCopyFunc) cpml_primitive_dup, g_free); return primitive_type; } /** * cpml_primitive_dup: * @primitive: a #CpmlPrimitive structure * * Duplicates @primitive. This function makes a shallow duplication of * @primitives, that is the internal pointers of the resulting primitive * struct refer to the same memory as the original @primitive. Check * out cpml_primitive_deep_dup() if it is required also the content * duplication. * * Returns: (transfer full): a shallow duplicate of @primitive: must be * freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlPrimitive * cpml_primitive_dup(const CpmlPrimitive *primitive) { return cpml_memdup(primitive, sizeof(CpmlPrimitive)); } /** * cpml_primitive_deep_dup: * @primitive: a #CpmlPrimitive structure * * Duplicates @primitive. This function makes a deep duplication of * @primitive, that is it duplicates also the definition data (both * org and data). * * Furthermore, the new segment field will * point to a fake duplicated segment with only its first primitive * set (the first primitive of a segment should be a #CPML_MOVE). * This is needed in order to let a #CPML_CLOSE work as expected. * * All the data is allocated in the same chunk of memory so freeing * the returned pointer releases all the occupied memory. * * Returns: (transfer full): a deep duplicate of @primitive: must be * freed with g_free() when no longer needed * * Since: 1.0 **/ CpmlPrimitive * cpml_primitive_deep_dup(const CpmlPrimitive *primitive) { const CpmlPrimitive *src; CpmlPrimitive *dst; gsize primitive_size, org_size, data_size, segment_size; gchar *ptr; g_return_val_if_fail(primitive != NULL, NULL); src = primitive; primitive_size = sizeof(CpmlPrimitive); if (src->org != NULL) org_size = sizeof(cairo_path_data_t); else org_size = 0; if (src->data != NULL) data_size = sizeof(cairo_path_data_t) * src->data->header.length; else data_size = 0; if (src->segment != NULL && src->segment->data != NULL) segment_size = sizeof(CpmlSegment) + sizeof(cairo_path_data_t) * src->segment->data[0].header.length; else segment_size = 0; dst = g_malloc(primitive_size + org_size + data_size + segment_size); ptr = (gchar *) dst + primitive_size; if (org_size > 0) { dst->org = memcpy(ptr, src->org, org_size); ptr += org_size; } else { dst->org = NULL; } if (data_size > 0) { dst->data = memcpy(ptr, src->data, data_size); ptr += data_size; } else { dst->data = NULL; } if (segment_size > 0) { dst->segment = memcpy(ptr, src->segment, sizeof(CpmlSegment)); ptr += sizeof(CpmlSegment); dst->segment->data = memcpy(ptr, src->segment->data, sizeof(cairo_path_data_t) * src->segment->data[0].header.length); } else { dst->segment = NULL; } return dst; } GType cpml_segment_get_type(void) { static GType segment_type = 0; if (G_UNLIKELY(segment_type == 0)) segment_type = g_boxed_type_register_static("CpmlSegment", (GBoxedCopyFunc) cpml_segment_dup, g_free); return segment_type; } /** * cpml_segment_dup: * @segment: a #CpmlSegment structure * * Duplicates @segment. This function makes a shallow duplication, * that is the internal pointers of the resulting segment struct * refer to the same memory as the original @segment. Check out * cpml_segment_deep_dup() if it is required also the content * duplication. * * Returns: (transfer full): a shallow duplicate of @segment: must be freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlSegment * cpml_segment_dup(const CpmlSegment *segment) { return cpml_memdup(segment, sizeof(CpmlSegment)); } /** * cpml_segment_deep_dup: * @segment: a #CpmlSegment structure * * Duplicates @segment. This function makes a deep duplication, * that is it duplicates also the underlying data that defines * the segment. The path field * is set to NULL as * data is no more referring to the * original cairo path. * * All the data is allocated in the same chunk of memory so freeing * the returned pointer releases all the occupied memory. * * Returns: (transfer full): a deep duplicate of @segment: must be freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlSegment * cpml_segment_deep_dup(const CpmlSegment *segment) { CpmlSegment *dest; int num_data; gsize segment_size, data_size; cairo_path_data_t *p_data; g_return_val_if_fail(segment != NULL, NULL); num_data = segment->num_data; segment_size = sizeof(CpmlSegment); data_size = segment->data ? sizeof(cairo_path_data_t) * num_data : 0; dest = (CpmlSegment *) g_malloc(segment_size + data_size); p_data = (cairo_path_data_t *) ((gchar *) dest + segment_size); dest->path = NULL; if (data_size > 0) { dest->data = memcpy(p_data, segment->data, data_size); dest->num_data = num_data; } else { dest->data = NULL; dest->num_data = 0; } return dest; } GType cpml_curve_offset_algorithm_get_type(void) { static GType etype = 0; if (G_UNLIKELY(etype == 0)) { static const GEnumValue values[] = { { CPML_CURVE_OFFSET_ALGORITHM_NONE, "CPML_CURVE_OFFSET_ALGORITHM_NONE", "none" }, { CPML_CURVE_OFFSET_ALGORITHM_DEFAULT, "CPML_CURVE_OFFSET_ALGORITHM_DEFAULT", "default" }, { CPML_CURVE_OFFSET_ALGORITHM_HANDCRAFT, "CPML_CURVE_OFFSET_ALGORITHM_HANDCRAFT", "handcraft" }, { CPML_CURVE_OFFSET_ALGORITHM_BAIOCA, "CPML_CURVE_OFFSET_ALGORITHM_BAIOCA", "baioca" }, { 0, NULL, NULL } }; etype = g_enum_register_static("CpmlCurveOffsetAlgorithm", values); } return etype; } GType cpml_primitive_type_get_type(void) { static GType etype = 0; if (G_UNLIKELY(etype == 0)) { static const GEnumValue values[] = { { CPML_MOVE, "CPML_MOVE", "move" }, { CPML_LINE, "CPML_LINE", "line" }, { CPML_ARC, "CPML_ARC", "arc" }, { CPML_CURVE, "CPML_CURVE", "curve" }, { CPML_CLOSE, "CPML_CLOSE", "close" }, { 0, NULL, NULL } }; etype = g_enum_register_static("CpmlPrimitiveType", values); } return etype; } diff --git a/src/cpml/cpml-primitive.c b/src/cpml/cpml-primitive.c index d0491f6d..129dc0e8 100644 --- a/src/cpml/cpml-primitive.c +++ b/src/cpml/cpml-primitive.c @@ -1,917 +1,953 @@ /* CPML - Cairo Path Manipulation Library * Copyright (C) 2007-2020 Nicola Fontana * * 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 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, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:cpml-primitive * @Section_Id:Primitive * @title: CpmlPrimitive * @short_description: Basic component of segments * * A primitive is an atomic geometric element found inside #CpmlSegment. * The available primitives are the same defined by #cairo_path_data_type_t * with the additional #CPML_ARC type (check #CpmlPrimitiveType * for further information) and without #CPML_MOVE as it is not * considered a primitive and it is managed in different way: the move-to * primitives are only used to define the origin of a segment. * * Since: 1.0 **/ /** * CpmlPrimitiveType: - * @CPML_MOVE: equivalent to %CAIRO_PATH_MOVE_TO - * @CPML_LINE: equivalent to %CAIRO_PATH_LINE_TO - * @CPML_CURVE: equivalent to %CAIRO_PATH_CURVE_TO - * @CPML_CLOSE: equivalent to %CAIRO_PATH_CLOSE_PATH - * @CPML_ARC: an arc representation at CPML level * * This is a type compatible with #cairo_path_data_type_t type. It is * basically the same enum but it embodies an important difference: * it can be used to specify the special #CPML_ARC primitive. Having * two different types is a good way to make clear when a function * expect or not embedded #CPML_ARC primitives. * * Arcs are used extensively in technical drawing: some operations are * trivials with arcs and a nightmare with cubic Bézier curves. Actually, * at least up to version 1.10.2, the cairo library does not support arc * primitives natively and there is no plan they will be ever supported. * * The CPML library supports arc natively, converting them to curves * when the #CpmlSegment is returned to the cairo context, for instance * when using cpml_segment_to_cairo(). * * Since: 1.0 **/ /** + * CPML_MOVE: + * + * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_MOVE_TO. + * + * Since: 1.0 + **/ + +/** + * CPML_LINE: + * + * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_LINE_TO. + * + * Since: 1.0 + **/ + +/** + * CPML_CURVE: + * + * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_CURVE_TO. + * + * Since: 1.0 + **/ + +/** + * CPML_CLOSE: + * + * A #CpmlPrimitiveType value equivalent to CAIRO_PATH_CLOSE_PATH. + * + * Since: 1.0 + **/ + +/** + * CPML_ARC: + * + * A #CpmlPrimitiveType value that represents an arc representation at + * CPML level. + * + * Since: 1.0 + **/ + +/** * CpmlPrimitive: * @segment: the source #CpmlSegment * @org: a pointer to the first point of the primitive * @data: the array of the path data, prepended by the header * * As for #CpmlSegment, also the primitive is unobtrusive. This * means CpmlPrimitive does not include any coordinates but instead * keeps pointers to the original segment (and, by transition, to * the underlying #cairo_data_path_t struct). * * Since: 1.0 **/ #include "cpml-internal.h" #include "cpml-extents.h" #include "cpml-segment.h" #include "cpml-primitive.h" #include "cpml-primitive-private.h" #include "cpml-arc.h" #include "cpml-curve.h" #include #include /* This is the best way I found to get the maximum value * of a bunch of constant by using only the preprocessor */ #define CPML_MAX (CPML_LINE | CPML_ARC | CPML_CURVE | CPML_CLOSE) static const _CpmlPrimitiveClass * _cpml_class_from_type (CpmlPrimitiveType type); static const _CpmlPrimitiveClass * _cpml_class_from_obj (const CpmlPrimitive *primitive); static cairo_path_data_t * _cpml_get_point (const CpmlPrimitive *primitive, int n_point); static void _cpml_dump_point (const cairo_path_data_t *path_data); /** * cpml_primitive_type_get_n_points: * @type: a primitive type * * Gets the number of points required to identify the @type primitive. * * Returns: the number of points or 0 on errors * * Since: 1.0 **/ size_t cpml_primitive_type_get_n_points(CpmlPrimitiveType type) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_type(type); if (class_data == NULL) return 0; return class_data->n_points; } /** * cpml_primitive_type: * @primitive: a #CpmlPrimitive struct * * Returns the type of @primitive, i.e. %CPML_LINE for lines, * %CPML_ARC for arcs or circles, %CPML_CURVE for cubic Bézier - * curves and %CPML_CLOSE for closing lines. The %CPML_MOVE is + * curves and CPML_CLOSE for closing lines. The %CPML_MOVE is * used extensively *between* segments but it is not considered * by itself a primitive, so it should never been returned. * * Returns: the type of the primitive. * * Since: 1.0 **/ CpmlPrimitiveType cpml_primitive_type(const CpmlPrimitive *primitive) { return primitive->data[0].header.type; } /** * cpml_primitive_from_segment: * @primitive: (out): the destination #CpmlPrimitive struct * @segment: (in): the source segment * * Initializes @primitive to the first primitive of @segment. * * Since: 1.0 **/ void cpml_primitive_from_segment(CpmlPrimitive *primitive, CpmlSegment *segment) { primitive->segment = segment; /* The first element of a CpmlSegment is always a CPML_MOVE, * as ensured by cpml_segment_from_cairo() and by the browsing APIs, * so the origin is in the second data item */ primitive->org = segment->data + 1; /* Also, the segment APIs ensure that @segment is prepended by * only one CPML_MOVE */ primitive->data = segment->data + segment->data->header.length; } /** * cpml_primitive_copy: * @primitive: (out): the destination #CpmlPrimitive * @src: (in): the source #CpmlPrimitive * * Copies @src in @primitive. This is a shallow copy: the internal fields * of @primitive refer to the same memory as the original @src primitive. * * Since: 1.0 **/ void cpml_primitive_copy(CpmlPrimitive *primitive, const CpmlPrimitive *src) { memcpy(primitive, src, sizeof(CpmlPrimitive)); } /** * cpml_primitive_copy_data: * @primitive: the destination #CpmlPrimitive * @src: the source primitive to copy * * Copies the memory referenced by the org * and data fields from @src to @primitive. * For a shallow copy, check out cpml_primitive_copy(). * * This could seem a somewhat unusual operation because @primitive * should contain the same primitive as @src (i.e., the * data->header field must be the same) * but it can be convenient in some situation, such as when restoring * the original data from a backup primitive, e.g.: * * * CpmlPrimitive *backup; * * backup = cpml_primitive_deep_dup(&primitive); * // Now &primitive points can be freely modified * ... * // Let's restore &primitive original points * cpml_primitive_copy_data(&primitive, backup); * g_free(backup); * * * Returns: (type gboolean): 1 if the data has been succesfully copied, 0 on errors. * * Since: 1.0 **/ int cpml_primitive_copy_data(CpmlPrimitive *primitive, const CpmlPrimitive *src) { if (primitive->data[0].header.type != src->data[0].header.type || primitive->data[0].header.length != src->data[0].header.length) return 0; memcpy(primitive->org, src->org, sizeof(cairo_path_data_t)); memcpy(primitive->data, src->data, sizeof(cairo_path_data_t) * src->data[0].header.length); return 1; } /** * cpml_primitive_reset: * @primitive: (inout): a #CpmlPrimitive * * Resets @primitive so it refers to the first primitive of the * source segment. * * Since: 1.0 **/ void cpml_primitive_reset(CpmlPrimitive *primitive) { cpml_primitive_from_segment(primitive, primitive->segment); } /** * cpml_primitive_next: * @primitive: (inout): a #CpmlPrimitive * * Changes @primitive so it refers to the next primitive on the * source segment. If there are no more primitives, @primitive is * not changed and 0 is returned. * * Returns: (type boolean): 1 on success, 0 if no next primitive found or errors. * * Since: 1.0 **/ int cpml_primitive_next(CpmlPrimitive *primitive) { cairo_path_data_t *new_data; const cairo_path_data_t *end_data; new_data = primitive->data + primitive->data->header.length; end_data = primitive->segment->data + primitive->segment->num_data; if (new_data >= end_data) return 0; primitive->org = _cpml_get_point(primitive, -1); primitive->data = new_data; return 1; } /** * cpml_primitive_get_n_points: * @primitive: a #CpmlPrimitive * * Gets the number of points required to identify @primitive. * It is similar to cpml_primitive_type_get_n_points() but using * a @primitive instance instead of a type. * * Returns: the number of points or 0 on errors. * * * * Since: 1.0 **/ size_t cpml_primitive_get_n_points(const CpmlPrimitive *primitive) { return cpml_primitive_type_get_n_points(primitive->data->header.type); } /** * cpml_primitive_get_length: * @primitive: a #CpmlPrimitive * * Abstracts the length() family functions by providing a common * way to access the underlying primitive-specific implementation. * The function returns the length of @primitive. * * Returns: the requested length or 0 on errors * * * * Since: 1.0 **/ double cpml_primitive_get_length(const CpmlPrimitive *primitive) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->get_length == NULL) return 0; return class_data->get_length(primitive); } /** * cpml_primitive_put_extents: * @primitive: (in): a #CpmlPrimitive * @extents: (out): where to store the extents * * Abstracts the extents() family functions by providing a common * way to access the underlying primitive-specific implementation. * * This function stores in @extents the bounding box of @primitive. * * On errors, that is if the extents cannot be calculated for some * reason, this function does nothing. * * * * Since: 1.0 **/ void cpml_primitive_put_extents(const CpmlPrimitive *primitive, CpmlExtents *extents) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->put_extents == NULL) return; class_data->put_extents(primitive, extents); } /** * cpml_primitive_set_point: * @primitive: a #CpmlPrimitive * @n_point: the index of the point to retrieve * @pair: the source #CpmlPair * * Sets the specified @n_point of @primitive to @pair. The @n_point * index starts from 0: if @n_point is 0, the start point (the origin) * is changed, 1 for the second point and so on. If @n_point is * negative, it is considered as a negative index from the end, so * that -1 is the end point, -2 the point before the end point and * so on. * - * #CPML_CLOSE is managed in a special way: if @n_point + * CPML_CLOSE is managed in a special way: if @n_point * is -1 or 1 and @primitive is a close-path, this function cycles * the source #CpmlSegment and returns the first point. This is * needed because requesting the end point (or the second point) * of a close path is a valid operation and must returns the origin * of the segment. * * Returns: (type gboolean): 1 if the point to be set is existent, 0 otherwise. * * Since: 1.0 **/ int cpml_primitive_set_point(CpmlPrimitive *primitive, int n_point, const CpmlPair *pair) { cairo_path_data_t *point = _cpml_get_point(primitive, n_point); if (point == NULL) return 0; cpml_pair_to_cairo(pair, point); return 1; } /** * cpml_primitive_put_point: * @primitive: a #CpmlPrimitive * @n_point: the index of the point to retrieve * @pair: (out caller-allocates): the destination #CpmlPair * * Gets the specified @n_point from @primitive and stores it into * @pair. The @n_point index is subject to the same rules explained * in the cpml_primitive_set_point() function. * * Returns: (type gboolean): 1 if the point is found, 0 otherwise. * * Since: 1.0 **/ int cpml_primitive_put_point(const CpmlPrimitive *primitive, int n_point, CpmlPair *pair) { const cairo_path_data_t *point = _cpml_get_point(primitive, n_point); if (point == NULL) return 0; cpml_pair_from_cairo(pair, point); return 1; } /** * cpml_primitive_put_pair_at: * @primitive: (in): a #CpmlPrimitive * @pos: (in): the position value * @pair: (out): the destination #CpmlPair * * Abstracts the put_pair_at family functions by * providing a common way to access the underlying primitive-specific * implementation. * * It gets the coordinates of the point lying on @primitive * at position @pos. @pos is an homogeneous factor where 0 is the * start point, 1 the end point, 0.5 the mid point and so on. * @pos can be less than 0 or greater than 1, in which case the * coordinates of @pair are interpolated. * * On errors, that is if the coordinates cannot be calculated for * some reason, this function does nothing. * * * * Since: 1.0 **/ void cpml_primitive_put_pair_at(const CpmlPrimitive *primitive, double pos, CpmlPair *pair) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->put_pair_at == NULL) return; class_data->put_pair_at(primitive, pos, pair); } /** * cpml_primitive_put_vector_at: * @primitive: (in): a #CpmlPrimitive * @pos: (in): the position value * @vector: (out): the destination #CpmlVector * * Abstracts the put_vector_at family functions by * providing a common way to access the underlying primitive-specific * implementation. * * It gets the steepness of the point at position @pos on @primitive. * @pos is an homogeneous factor where 0 is the start point, 1 the * end point, 0.5 the mid point and so on. * @pos can be less than 0 or greater than 1, in which case the * coordinates of @pair are interpolated. * * On errors, that is if the steepness cannot be calculated for * some reason, this function does nothing. * * * * Since: 1.0 **/ void cpml_primitive_put_vector_at(const CpmlPrimitive *primitive, double pos, CpmlVector *vector) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->put_vector_at == NULL) return; class_data->put_vector_at(primitive, pos, vector); } /** * cpml_primitive_is_inside: * @primitive: a #CpmlPrimitive * @pair: the coordinates of the subject point * * Checks if @pair is inside the bounding box of @primitive. This * can be useful e.g. to verify if an intersection point is a real * intersection or an hypothetical one. * * Returns: 1 if @pair is inside the bounding box of @primitive, 0 otherwise. * * Since: 1.0 **/ int cpml_primitive_is_inside(const CpmlPrimitive *primitive, const CpmlPair *pair) { CpmlExtents extents = { 0 }; cpml_primitive_put_extents(primitive, &extents); return cpml_extents_pair_is_inside(&extents, pair); } /** * cpml_primitive_get_closest_pos: * @primitive: a #CpmlPrimitive * @pair: the coordinates of the subject point * * Returns the pos value of the point on @primitive nearest to @pair. * The returned value is always clamped between 0 and 1. * * Returns: the requested pos value between 0 and 1, or -1 on errors. * * * * Since: 1.0 **/ double cpml_primitive_get_closest_pos(const CpmlPrimitive *primitive, const CpmlPair *pair) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->get_closest_pos == NULL) return -1; return class_data->get_closest_pos(primitive, pair); } /** * cpml_primitive_put_intersections: * @primitive: the first #CpmlPrimitive * @primitive2: the second #CpmlPrimitive * @n_dest: maximum number of intersections to return * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair * * Finds the intersection points between the given primitives and * returns the result in @dest. The size of @dest should be enough * to store @n_dest #CpmlPair. The maximum number of intersections * is dependent on the type of the primitive involved in the * operation. If there are at least one Bézier curve involved, up to * 4 intersections could be returned. Otherwise, if there is an arc * the intersections will be 2 at maximum. For line primitives, there * is only 1 point (or 0 if the lines are parallel). * * Also hypothetical intersections are returned, that is intersections * made by extending the primitives outside their bounds. This means e.g. * two lines always return one intersection if they are not parallel. To * discriminate real intersections you should check the returned points * with cpml_primitive_is_inside(), for example: * * * if (cpml_primitive_put_intersections(line1, line2, 1, &pair) == 0) { * // line1 and line2 are parallels * } else if (cpml_primitive_is_inside(line1, &pair) && * cpml_primitive_is_inside(line2, &pair)) { * // This is a real intersection * } else { * // This is an hypothetical intersection * } * * * * * The convention used by the CPML library is that a primitive should * implement only the intersection algorithms with lower degree * primitives. This is required to avoid code duplication: intersection * between arc and Bézier curves must be implemented by #CPML_CURVE and * intersection between lines and arcs must be implemented by #CPML_ARC. * cpml_primitive_put_intersections() will take care of swapping the * arguments if they are not properly ordered. * * * * Returns: the number of intersection points found or 0 if the * primitives do not intersect or on errors * * * * Since: 1.0 **/ size_t cpml_primitive_put_intersections(const CpmlPrimitive *primitive, const CpmlPrimitive *primitive2, size_t n_dest, CpmlPair *dest) { const _CpmlPrimitiveClass *class_data; size_t n_points, n_points2; if (n_dest == 0) return 0; n_points = cpml_primitive_get_n_points(primitive); n_points2 = cpml_primitive_get_n_points(primitive2); /* Check if the primitives can intersect */ if (n_points == 0 || n_points2 == 0) return 0; /* Primitives reordering: the first must be the more complex one */ if (n_points < n_points2) { return cpml_primitive_put_intersections(primitive2, primitive, n_dest, dest); } /* Check if put_intersections is implemented */ class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->put_intersections == NULL) return 0; return class_data->put_intersections(primitive, primitive2, n_dest, dest); } /** * cpml_primitive_put_intersections_with_segment: * @primitive: a #CpmlPrimitive * @segment: a #CpmlSegment * @n_dest: maximum number of intersections to return * @dest: (out caller-allocates) (array length=n_dest): the destination buffer that can contain @n_dest #CpmlPair * * Computes the intersections between @segment and @primitive by * sequentially scanning the primitives in @segment and looking * for their intersections with @primitive. * * If the intersections are more than @n_dest, only the first * @n_dest pairs are stored. * * Beware: this function returns only real intersections. This is in contrast * to cpml_primitive_put_intersections() that, instead, returns all * intersections. Check that function documentation to know what virtual and * real intersections are. * * Returns: the number of real intersections found. * * Since: 1.0 **/ size_t cpml_primitive_put_intersections_with_segment(const CpmlPrimitive *primitive, const CpmlSegment *segment, size_t n_dest, CpmlPair *dest) { CpmlPrimitive portion; CpmlPair partial[5]; const CpmlPair *pair; size_t found, total; cpml_primitive_from_segment(&portion, (CpmlSegment *) segment); total = 0; while (total < n_dest) { found = cpml_primitive_put_intersections(&portion, primitive, 5, partial); /* Store only real intersections */ for (pair = partial; found; -- found, ++ pair) { if (cpml_primitive_is_inside(&portion, pair) && cpml_primitive_is_inside(primitive, pair)) { cpml_pair_copy(dest+total, pair); ++ total; } } if (!cpml_primitive_next(&portion)) break; } return total; } /** * cpml_primitive_offset: * @primitive: (inout): a #CpmlPrimitive * @offset: (in): distance for the computed offset primitive * * Given a primitive, computes the same (or approximated) parallel * primitive distant @offset from the original one and returns * the result by changing @primitive. * * On errors, that is if the offset primitive cannot be calculated * for some reason, this function does nothing. * * * * Since: 1.0 **/ void cpml_primitive_offset(CpmlPrimitive *primitive, double offset) { const _CpmlPrimitiveClass *class_data = _cpml_class_from_obj(primitive); if (class_data == NULL || class_data->offset == NULL) return; class_data->offset(primitive, offset); } /** * cpml_primitive_join: * @primitive: (inout): the first #CpmlPrimitive * @primitive2: (inout): the second #CpmlPrimitive * * Joins two primitive modifying the end point of @primitive and the * start point of @primitive2 so that the resulting points will overlap. * * * TODO * * Actually, the join is done by extending the end vector * of @primitive and the start vector of @primitive2 and * interpolating the intersection: this means no primitive * dependent code is needed. Anyway, it is likely to change * in the future because this approach is quite naive when * curves are involved. * * * * Returns: (type boolean): 1 on success, 0 if the primitives cannot be joined. * * * * Since: 1.0 **/ int cpml_primitive_join(CpmlPrimitive *primitive, CpmlPrimitive *primitive2) { cairo_path_data_t *end1, *start2; CpmlPrimitive line1, line2; cairo_path_data_t data1[2], data2[2]; CpmlPair joint; end1 = _cpml_get_point(primitive, -1); start2 = _cpml_get_point(primitive2, 0); /* Check if the primitives are yet connected */ if (end1->point.x == start2->point.x && end1->point.y == start2->point.y) return 1; line1.org = _cpml_get_point(primitive, -2); line1.data = data1; data1[0].header.type = CPML_LINE; data1[1] = *end1; line2.org = start2; line2.data = data2; data2[0].header.type = CPML_LINE; data2[1] = *_cpml_get_point(primitive2, 1); if (!cpml_primitive_put_intersections(&line1, &line2, 1, &joint)) return 0; cpml_pair_to_cairo(&joint, end1); cpml_pair_to_cairo(&joint, start2); return 1; } /** * cpml_primitive_to_cairo: * @primitive: (in): a #CpmlPrimitive * @cr: (inout): the destination cairo context * * Renders a single @primitive to the @cr cairo context. - * As a special case, if the primitive is a #CPML_CLOSE, an + * As a special case, if the primitive is a CPML_CLOSE, an * equivalent line is rendered, because a close path left alone * is not renderable. * * Also a #CPML_ARC primitive is treated specially, as it is not * natively supported by cairo and has its own rendering API. * * Since: 1.0 **/ void cpml_primitive_to_cairo(const CpmlPrimitive *primitive, cairo_t *cr) { CpmlPrimitiveType type; cairo_path_t path; cairo_path_data_t *path_data; cairo_move_to(cr, primitive->org->point.x, primitive->org->point.y); type = primitive->data->header.type; if (type == CPML_CLOSE) { path_data = _cpml_get_point(primitive, -1); cairo_line_to(cr, path_data->point.x, path_data->point.y); } else if (type == CPML_ARC) { cpml_arc_to_cairo(primitive, cr); } else { path.status = CAIRO_STATUS_SUCCESS; path.data = primitive->data; path.num_data = primitive->data->header.length; cairo_append_path(cr, &path); } } /** * cpml_primitive_dump: * @primitive: a #CpmlPrimitive * @org_also: (type boolean): whether to output also the origin coordinates * * Dumps info on the specified @primitive to stdout: useful for * debugging purposes. If @org_also is 1, a #CPML_MOVE to the * origin is prepended to the data otherwise the * org field is not used. * * Since: 1.0 **/ void cpml_primitive_dump(const CpmlPrimitive *primitive, int org_also) { const cairo_path_data_t *data; int type; const _CpmlPrimitiveClass *class_data; size_t n, n_points; data = primitive->data; type = data->header.type; class_data = _cpml_class_from_type(type); if (class_data == NULL) { printf("Unknown primitive type (%d)\n", type); return; } /* Dump the origin, if requested */ if (org_also) { printf("move to "); _cpml_dump_point(primitive->org); printf("\n"); } printf("%s ", class_data->name); n_points = cpml_primitive_get_n_points(primitive); for (n = 1; n < n_points; ++n) _cpml_dump_point(_cpml_get_point(primitive, n)); printf("\n"); } static const _CpmlPrimitiveClass * _cpml_class_from_type(CpmlPrimitiveType type) { static const _CpmlPrimitiveClass *table[CPML_MAX + 1] = { NULL }; if (table[CPML_LINE] == NULL) { table[CPML_LINE] = _cpml_line_get_class(); table[CPML_ARC] = _cpml_arc_get_class(); table[CPML_CURVE] = _cpml_curve_get_class(); table[CPML_CLOSE] = _cpml_close_get_class(); } return (int) type <= (int) CPML_MAX ? table[type] : NULL; } static const _CpmlPrimitiveClass * _cpml_class_from_obj(const CpmlPrimitive *primitive) { return _cpml_class_from_type(primitive->data->header.type); } /* * _cpml_get_point: * @primitive: a #CpmlPrimitive * @n_point: the index of the point to retrieve * * Gets the specified @n_point from @primitive. The index starts * at 0: if @n_point is 0, the start point (the origin) is * returned, 1 for the second point and so on. If @n_point is * negative, it is considered as a negative index from the end, * so that -1 is the end point, -2 the point before the end point * and so on. * - * #CPML_CLOSE is managed in a special way: if @n_point + * CPML_CLOSE is managed in a special way: if @n_point * is -1 or 1 and @primitive is a close-path, this function cycles * the source #CpmlSegment and returns the first point. This is * needed because requesting the end point (or the second point) * of a close path is a valid operation and must returns the start * of the segment. * * Returns: a pointer to the requested point (in cairo format) * or NULL if the point is outside * the valid range. * * Since: 1.0 **/ static cairo_path_data_t * _cpml_get_point(const CpmlPrimitive *primitive, int n_point) { size_t n_points; /* For a start point request, simply return the origin * without further checking */ if (n_point == 0) return primitive->org; /* The CPML_CLOSE special case */ if (primitive->data->header.type == CPML_CLOSE && (n_point == 1 || n_point == -1)) return &primitive->segment->data[1]; n_points = cpml_primitive_get_n_points(primitive); if (n_points == 0) return NULL; /* If n_point is negative, consider it as a negative index from the end */ if (n_point < 0) n_point = n_points + n_point; /* Out of range condition */ if (n_point < 0 || n_point >= n_points) return NULL; return n_point == 0 ? primitive->org : &primitive->data[n_point]; } static void _cpml_dump_point(const cairo_path_data_t *path_data) { printf("(%g %g) ", path_data->point.x, path_data->point.y); } diff --git a/src/cpml/cpml-segment.c b/src/cpml/cpml-segment.c index 3eebc498..ad3aba03 100644 --- a/src/cpml/cpml-segment.c +++ b/src/cpml/cpml-segment.c @@ -1,714 +1,714 @@ /* CPML - Cairo Path Manipulation Library * Copyright (C) 2007-2020 Nicola Fontana * * 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 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, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:cpml-segment * @Section_Id:Segment * @title: CpmlSegment * @short_description: Contiguous segment that can be a fragment * or a whole cairo path * * A segment is a single contiguous line got from a cairo path. The * CPML library relies on one assumption to let the data be independent * from the current point (and thus from the cairo context): any segment * MUST be preceded by at least one %CPML_MOVE primitive. * This means a valid segment in cairo could be rejected by CPML. * * #CpmlSegment provides an unobtrusive way to access a cairo path. * This means #CpmlSegment itsself does not hold any coordinates but * instead a bunch of pointers to the original #cairo_path_t struct: * modifying data through this struct also changes the original path. * * Every #cairo_path_t struct can contain more than one segment: the CPML * library provides iteration APIs to browse the segments of a path. * Use cpml_segment_reset() to reset the iterator at the start of the * cairo path (will point the first segment) and cpml_segment_next() * to get the next segment. Getting the previous segment is not provided * as the underlying cairo struct is not accessible in reverse order. * * When initialized, #CpmlSegment yet refers to the first segment so * the initial reset is not required. * * Since: 1.0 **/ /** * CpmlSegment: * @path: the source #cairo_path_t struct * @data: the data points of the segment; the first primitive * will always be a %CPML_MOVE * @num_data: size of @data * * This is an unobtrusive struct to identify a segment inside a * cairo path. Unobtrusive means that the real coordinates are * still stored in @path: CpmlSegment only provides a way to * access them. * * Since: 1.0 **/ #include "cpml-internal.h" #include "cpml-extents.h" #include "cpml-segment.h" #include "cpml-primitive.h" #include "cpml-curve.h" #include static int normalize (CpmlSegment *segment); static int ensure_one_leading_move (CpmlSegment *segment); static int reshape (CpmlSegment *segment); /** * cpml_segment_from_cairo: * @segment: a #CpmlSegment * @path: (type gpointer): the source #cairo_path_t * * Builds a CpmlSegment from a #cairo_path_t structure. This operation * involves stripping duplicate %CPML_MOVE primitives at the beginning * of @path and including all the primitives up to the end of the * contiguous line, that is before a %CPML_MOVE, when the original path * data finish or up to a %CPML_CLOSE. This is done unobtrusively by * setting the @segment fields appropriately, i.e. @path is not touched. * * The first primitive must be a %CPML_MOVE, so no dependency on the * cairo context (to know the current position) is needed. * * This function will fail if @path is empty or if its - * status member is not %CAIRO_STATUS_SUCCESS. + * status member is not CAIRO_STATUS_SUCCESS. * * Returns: (type gboolean): 1 if @segment has been succesfully computed, 0 on errors. * * Since: 1.0 **/ int cpml_segment_from_cairo(CpmlSegment *segment, cairo_path_t *path) { /* The cairo path should be in a good state */ if (path->num_data == 0 || path->status != CAIRO_STATUS_SUCCESS) return 0; segment->path = path; segment->data = path->data; segment->num_data = path->num_data; return normalize(segment); } /** * cpml_segment_copy: * @segment: a #CpmlSegment * @src: the source segment to copy * * Makes a shallow copy of @src into @segment. * * Since: 1.0 **/ void cpml_segment_copy(CpmlSegment *segment, const CpmlSegment *src) { memcpy(segment, src, sizeof(CpmlSegment)); } /** * cpml_segment_copy_data: * @segment: a #CpmlSegment structure * @src: the source segment to copy * * Copies the memory referenced by the data field * from @src to @segment. For a shallow copy, check out cpml_segment_copy(). * * This could seem a somewhat unusual operation because @segment should * be compatible with @src, i.e. it is expected that they have the same * num_data value. Anyway it is convenient * in some situation, such as when restoring the original data from a * backup segment, e.g.: * * * CpmlSegment *backup; * * backup = cpml_segment_deep_dup(&segment); * // Now &segment points can be freely modified * ... * // Let's restore &segment original points * cpml_segment_copy_data(&segment, backup); * g_free(backup); * * * Returns: (type gboolean): 1 if the data has been succesfully copied, 0 on errors. * * Since: 1.0 **/ int cpml_segment_copy_data(CpmlSegment *segment, const CpmlSegment *src) { if (segment->num_data != src->num_data) return 0; if (src->num_data > 0) { size_t n = sizeof(cairo_path_data_t) * segment->num_data; memcpy(segment->data, src->data, n); } return 1; } /** * cpml_segment_reset: * @segment: a #CpmlSegment * * Modifies @segment to point to the first segment of the source cairo path. * * Since: 1.0 **/ void cpml_segment_reset(CpmlSegment *segment) { segment->data = segment->path->data; segment->num_data = segment->path->num_data; normalize(segment); } /** * cpml_segment_next: * @segment: a #CpmlSegment * * Modifies @segment to point to the next segment of the source cairo path. * * Returns: (type gboolean): 1 on success, 0 if no next segment found or errors. * * Since: 1.0 **/ int cpml_segment_next(CpmlSegment *segment) { cairo_path_data_t *new_data; const cairo_path_data_t *end_data; new_data = segment->data + segment->num_data; end_data = segment->path->data + segment->path->num_data; if (new_data >= end_data) return 0; segment->data = new_data; segment->num_data = end_data - new_data; return normalize(segment); } /** * cpml_segment_get_length: * @segment: a #CpmlSegment * * Gets the whole length of @segment. * * Returns: the requested length * * Since: 1.0 **/ double cpml_segment_get_length(const CpmlSegment *segment) { CpmlPrimitive primitive; double length; cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment); length = 0; do { length += cpml_primitive_get_length(&primitive); } while (cpml_primitive_next(&primitive)); return length; } /** * cpml_segment_put_extents: * @segment: a #CpmlSegment * @extents: where to store the extents * * Gets the whole extents of @segment. * * Since: 1.0 **/ void cpml_segment_put_extents(const CpmlSegment *segment, CpmlExtents *extents) { CpmlPrimitive primitive; CpmlExtents primitive_extents; extents->is_defined = 0; cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment); do { cpml_primitive_put_extents(&primitive, &primitive_extents); cpml_extents_add(extents, &primitive_extents); } while (cpml_primitive_next(&primitive)); } /** * cpml_segment_put_pair_at: * @segment: a #CpmlSegment * @pos: the position value * @pair: the destination #CpmlPair * * Gets the coordinates of the point lying on @segment at position * @pos. @pos is an homogeneous factor where 0 is the start point, * 1 the end point, 0.5 the mid point and so on. * The relation 0 < @pos < 1 should be satisfied, * although some cases accept value outside this range. * * * TODO * * The actual implementation returns only the start and end points, * that is only when @pos is 0 or 1. * * * * Since: 1.0 **/ void cpml_segment_put_pair_at(const CpmlSegment *segment, double pos, CpmlPair *pair) { CpmlPrimitive primitive; cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment); /* Handle the common cases: start and end points */ if (pos == 0) { cpml_primitive_put_pair_at(&primitive, 0, pair); } if (pos == 1) { while (cpml_primitive_next(&primitive)) ; cpml_primitive_put_pair_at(&primitive, 1, pair); } } /** * cpml_segment_put_vector_at: * @segment: a #CpmlSegment * @pos: the position value * @vector: the destination #CpmlVector * * Gets the steepness of the point lying on @segment at position * @pos. @pos is an homogeneous factor where 0 is the start point, * 1 the end point, 0.5 the mid point and so on. * The relation 0 < @pos < 1 should be satisfied, * although some cases accept value outside this range. * * * TODO * * The actual implementation returns only the start and end * steepness, that is only when @pos is 0 or 1. * * * * Since: 1.0 **/ void cpml_segment_put_vector_at(const CpmlSegment *segment, double pos, CpmlVector *vector) { CpmlPrimitive primitive; cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment); /* Handle the common cases: start and end points */ if (pos == 0) { cpml_primitive_put_vector_at(&primitive, 0, vector); return; } if (pos == 1) { while (cpml_primitive_next(&primitive)) ; cpml_primitive_put_vector_at(&primitive, 1, vector); return; } } /** * cpml_segment_put_intersections: * @segment: the first #CpmlSegment * @segment2: the second #CpmlSegment * @n_dest: maximum number of intersections to return * @dest: the destination vector of #CpmlPair * * Computes the intersections between @segment and @segment2 and * returns the found points in @dest. If the intersections are more * than @n_dest, only the first @n_dest pairs are stored in @dest. * * To get the job done, the primitives of @segment are sequentially * scanned for intersections with any primitive in @segment2. This * means @segment has a higher precedence over @segment2. * * Returns: the number of intersections found * * Since: 1.0 **/ size_t cpml_segment_put_intersections(const CpmlSegment *segment, const CpmlSegment *segment2, size_t n_dest, CpmlPair *dest) { CpmlPrimitive portion; size_t partial, total; cpml_primitive_from_segment(&portion, (CpmlSegment *) segment); total = 0; do { partial = cpml_primitive_put_intersections_with_segment(&portion, segment2, n_dest - total, dest + total); total += partial; } while (total < n_dest && cpml_primitive_next(&portion)); return total; } /** * cpml_segment_offset: * @segment: a #CpmlSegment * @offset: the offset distance * * Offsets a segment of the specified amount, that is builds a "parallel" * segment at the @offset distance from the original one and returns the * result by replacing the original @segment. * * * TODO * * Closed path are not yet managed: an elegant solution is not * so obvious: use cpml_close_offset when * will be available. * Degenerated primitives, such as lines of length 0, are not * managed properly. * * * * Since: 1.0 **/ void cpml_segment_offset(CpmlSegment *segment, double offset) { CpmlPrimitive primitive; CpmlPrimitive last_primitive; CpmlPair old_end; cairo_path_data_t org, *old_org; int first_cycle; cpml_primitive_from_segment(&primitive, segment); first_cycle = 1; do { if (! first_cycle) { cpml_pair_to_cairo(&old_end, &org); old_org = primitive.org; primitive.org = &org; } cpml_primitive_put_point(&primitive, -1, &old_end); cpml_primitive_offset(&primitive, offset); if (! first_cycle) { cpml_primitive_join(&last_primitive, &primitive); primitive.org = old_org; } cpml_primitive_copy(&last_primitive, &primitive); first_cycle = 0; } while (cpml_primitive_next(&primitive)); } /** * cpml_segment_transform: * @segment: a #CpmlSegment * @matrix: the matrix to be applied * * Applies @matrix on all the points of @segment. * * Since: 1.0 **/ void cpml_segment_transform(CpmlSegment *segment, const cairo_matrix_t *matrix) { CpmlPrimitive primitive; cairo_path_data_t *data; size_t n_points; cpml_primitive_from_segment(&primitive, segment); cairo_matrix_transform_point(matrix, &(primitive.org)->point.x, &(primitive.org)->point.y); do { data = primitive.data; if (data->header.type != CPML_CLOSE) { n_points = cpml_primitive_get_n_points(&primitive); while (--n_points > 0) { ++data; cairo_matrix_transform_point(matrix, &data->point.x, &data->point.y); } } } while (cpml_primitive_next(&primitive)); } /** * cpml_segment_reverse: * @segment: a #CpmlSegment * * Reverses @segment in-place. The resulting rendering will be the same, * but with the primitives generated in reverse order. * * It is assumed that @segment has already been sanitized, e.g. when it * is returned by some CPML API or it is a cairo path already conforming * to the segment rules described in cpml_segment_from_cairo(). * * Since: 1.0 **/ void cpml_segment_reverse(CpmlSegment *segment) { cairo_path_data_t *data, *dst_data; size_t data_size; double end_x, end_y; int n, length; size_t n_points, n_point; const cairo_path_data_t *src_data; data_size = sizeof(cairo_path_data_t) * segment->num_data; data = malloc(data_size); src_data = segment->data; dst_data = data + segment->num_data; end_x = src_data[1].point.x; end_y = src_data[1].point.y; n = src_data->header.length; data->header.type = CPML_MOVE; data->header.length = n; while (n < segment->num_data) { src_data = segment->data + n; if (src_data->header.type == CPML_CLOSE) break; n_points = cpml_primitive_type_get_n_points(src_data->header.type); length = src_data->header.length; n += length; dst_data -= length; dst_data->header.type = src_data->header.type; dst_data->header.length = length; for (n_point = 1; n_point < n_points; ++n_point) { dst_data[n_points - n_point].point.x = end_x; dst_data[n_points - n_point].point.y = end_y; end_x = src_data[n_point].point.x; end_y = src_data[n_point].point.y; } /* Copy also the embedded data, if any */ if (n_points < length) { size_t size = (length - n_points) * sizeof(cairo_path_data_t); memcpy(dst_data + n_points, src_data + n_points, size); } } if (src_data->header.type == CPML_CLOSE) { memcpy(segment->data + segment->data->header.length, dst_data, data_size - ((char *) dst_data - (char *) data)); } else { memcpy(segment->data, data, data_size); } free(data); segment->data[1].point.x = end_x; segment->data[1].point.y = end_y; } /** * cpml_segment_to_cairo: * @segment: a #CpmlSegment * @cr: the destination cairo context * * Appends the path of @segment to @cr. The segment is "flattened", * that is %CPML_ARC primitives are approximated by one or more * %CPML_CURVE using cpml_arc_to_cairo(). Check its documentation * for further details. * * Since: 1.0 **/ void cpml_segment_to_cairo(const CpmlSegment *segment, cairo_t *cr) { CpmlPrimitive primitive; cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment); do { cpml_primitive_to_cairo(&primitive, cr); } while (cpml_primitive_next(&primitive)); } /** * cpml_segment_dump: * @segment: a #CpmlSegment * * Dumps the specified @segment to stdout. Useful for debugging purposes. * * Since: 1.0 **/ void cpml_segment_dump(const CpmlSegment *segment) { CpmlPrimitive primitive; int first_call = 1; cpml_primitive_from_segment(&primitive, (CpmlSegment *) segment); do { cpml_primitive_dump(&primitive, first_call); first_call = 0; } while (cpml_primitive_next(&primitive)); } /* * normalize: * @segment: a #CpmlSegment * * Sanitizes @segment by calling ensure_one_leading_move() and reshape(). * * Returns: 1 on success, 0 on no leading MOVE_TOs or on errors. **/ static int normalize(CpmlSegment *segment) { if (!ensure_one_leading_move(segment)) return 0; return reshape(segment); } /* * ensure_one_leading_move: * @segment: a #CpmlSegment * * Jumps to the first %CPML_MOVE primitives and skips all eventual * subsequent %CPML_MOVE but the last. This is done unobtrusively, * by updating the CpmlSegment fields * accordingly. One, and only one, %CPML_MOVE primitive is left. * * Returns: 1 on success, 0 on no leading MOVE_TOs or on empty path **/ static int ensure_one_leading_move(CpmlSegment *segment) { cairo_path_data_t *data = segment->data; int length, num_data = segment->num_data; /* Look up the first MOVE_TO */ while (num_data > 0 && data->header.type != CPML_MOVE) { length = data->header.length; data += length; num_data -= length; } if (num_data <= 0) return 0; /* Skip all duplicate CPML_MOVE but the last one */ while (num_data > (length = data->header.length) && data[length].header.type == CPML_MOVE) { data += length; num_data -= length; } if (num_data <= length) return 0; segment->data = data; segment->num_data = num_data; return 1; } /* * reshape: * @segment: a #CpmlSegment * * Looks for the segment termination, that is the end of the underlying * cairo path or if the current primitive is a %CPML_MOVE or if the * last primitive was a %CPML_CLOSE. num_data * field is modified to properly point to the end of @segment. @segment * must have only one leading %CPML_MOVE and it is supposed to be non-empty, * conditions already imposed by ensure_one_leading_move(). * * This function also checks that all the components of @segment * are valid primitives. * * Returns: 1 on success, 0 on invalid primitive found. **/ static int reshape(CpmlSegment *segment) { cairo_path_data_t *data = segment->data; cairo_path_data_type_t type; int length, n_points, num_data = 0; /* Always include the trailing CPML_MOVE */ length = data->header.length; data += length; num_data += length; while (num_data < segment->num_data) { type = data->header.type; /* A CPML_MOVE is usually the start of the next segment */ if (type == CPML_MOVE) break; length = data->header.length; data += length; num_data += length; /* Ensure the next primitive is valid and has enough data points */ n_points = type == CPML_CLOSE ? 1 : cpml_primitive_type_get_n_points(type); if (length < 1 || n_points == 0 || length < n_points) return 0; /* A CPML_CLOSE ends the current segment */ if (type == CPML_CLOSE) break; } /* The sum of the primitive lengths must *not* be greater than * the whole segment length */ if (num_data > segment->num_data) return 0; segment->num_data = num_data; return 1; }