Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gskpath.c

  Sprache: C
 

/*
 * 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 "gskpathprivate.h"

#include "gskcurveprivate.h"
#include "gskpathbuilder.h"
#include "gskpathpoint.h"
#include "gskcontourprivate.h"

/**
 * GskPath:
 *
 * Describes lines and curves that are more complex than simple rectangles.
 *
 * Paths can used for rendering (filling or stroking) and for animations
 * (e.g. as trajectories).
 *
 * `GskPath` is an immutable, opaque, reference-counted struct.
 * After creation, you cannot change the types it represents. Instead,
 * new `GskPath` objects have to be created. The [struct@Gsk.PathBuilder]
 * structure is meant to help in this endeavor.
 *
 * Conceptually, a path consists of zero or more contours (continuous, connected
 * curves), each of which may or may not be closed. Contours are typically
 * constructed from Bézier segments.
 *
 * <picture>
 *   <source srcset="path-dark.png" media="(prefers-color-scheme: dark)">
 *   <img alt="A Path" src="path-light.png">
 * </picture>
 *
 * Since: 4.14
 */


struct _GskPath
{
  /*< private >*/
  guint ref_count;

  GskPathFlags flags;

  gsize n_contours;
  GskContour *contours[];
  /* followed by the contours data */
};

G_DEFINE_BOXED_TYPE (GskPath, gsk_path, gsk_path_ref, gsk_path_unref)

/* {{{ Private API */

GskPath *
gsk_path_new_from_contours (const GSList *contours)
{
  GskPath *path;
  const GSList *l;
  gsize size;
  gsize n_contours;
  guint8 *contour_data;
  GskPathFlags flags;

  flags = GSK_PATH_CLOSED | GSK_PATH_FLAT | GSK_PATH_ZERO_LENGTH;
  size = 0;
  n_contours = 0;
  for (l = contours; l; l = l->next)
    {
      GskContour *contour = l->data;

      n_contours++;
      size += sizeof (GskContour *);
      size += gsk_contour_get_size (contour);
      flags &= gsk_contour_get_flags (contour);
    }

  path = g_malloc0 (sizeof (GskPath) + size);
  path->ref_count = 1;
  path->flags = flags;
  path->n_contours = n_contours;
  contour_data = (guint8 *) &path->contours[n_contours];
  n_contours = 0;

  for (l = contours; l; l = l->next)
    {
      GskContour *contour = l->data;

      path->contours[n_contours] = (GskContour *) contour_data;
      gsk_contour_copy ((GskContour *) contour_data, contour);
      size = gsk_contour_get_size (contour);
      contour_data += size;
      n_contours++;
    }

  return path;
}

const GskContour *
gsk_path_get_contour (const GskPath *self,
                      gsize          i)
{
  if (i < self->n_contours)
    return self->contours[i];
  else
    return NULL;
}

GskPathFlags
gsk_path_get_flags (const GskPath *self)
{
  return self->flags;
}

gsize
gsk_path_get_n_contours (const GskPath *self)
{
  return self->n_contours;
}

/* }}} */
/* {{{ Public API */

/**
 * gsk_path_ref:
 * @self: a path
 *
 * Increases the reference count of a path by one.
 *
 * Returns: the passed in `GskPath`
 *
 * Since: 4.14
 */

GskPath *
gsk_path_ref (GskPath *self)
{
  g_return_val_if_fail (self != NULL, NULL);

  self->ref_count++;

  return self;
}

/**
 * gsk_path_unref:
 * @self: a path
 *
 * Decreases the reference count of a path by one.
 *
 * If the resulting reference count is zero, frees the path.
 *
 * Since: 4.14
 */

void
gsk_path_unref (GskPath *self)
{
  g_return_if_fail (self != NULL);
  g_return_if_fail (self->ref_count > 0);

  self->ref_count--;
  if (self->ref_count > 0)
    return;

  g_free (self);
}

/**
 * gsk_path_print:
 * @self: a path
 * @string: the string to print into
 *
 * Converts the path into a human-readable representation.
 *
 * The string is compatible with (a superset of)
 * [SVG path syntax](https://www.w3.org/TR/SVG11/paths.html#PathData),
 * see [func@Gsk.Path.parse] for a summary of the syntax.
 *
 * Since: 4.14
 */

void
gsk_path_print (GskPath *self,
                GString *string)
{
  gsize i;

  g_return_if_fail (self != NULL);
  g_return_if_fail (string != NULL);

  for (i = 0; i < self->n_contours; i++)
    {
      if (i > 0)
        g_string_append_c (string, ' ');
      gsk_contour_print (self->contours[i], string);
    }
}

/**
 * gsk_path_to_string:
 * @self: a path
 *
 * Converts the path into a human-readable string.
 *
 * You can use this function in a debugger to get a quick overview
 * of the path.
 *
 * This is a wrapper around [method@Gsk.Path.print], see that function
 * for details.
 *
 * Returns: a new string for @self
 *
 * Since: 4.14
 */

char *
gsk_path_to_string (GskPath *self)
{
  GString *string;

  g_return_val_if_fail (self != NULL, NULL);

  string = g_string_new ("");

  gsk_path_print (self, string);

  return g_string_free (string, FALSE);
}

static gboolean
gsk_path_to_cairo_add_op (GskPathOperation        op,
                          const graphene_point_t *pts,
                          gsize                   n_pts,
                          float                   weight,
                          gpointer                cr)
{
  switch (op)
  {
    case GSK_PATH_MOVE:
      cairo_move_to (cr, pts[0].x, pts[0].y);
      break;

    case GSK_PATH_CLOSE:
      cairo_close_path (cr);
      break;

    case GSK_PATH_LINE:
      cairo_line_to (cr, pts[1].x, pts[1].y);
      break;

    case GSK_PATH_CUBIC:
      cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
      break;

    case GSK_PATH_QUAD:
    case GSK_PATH_CONIC:
    default:
      g_assert_not_reached ();
      return FALSE;
  }

  return TRUE;
}

/**
 * gsk_path_to_cairo:
 * @self: a path
 * @cr: a cairo context
 *
 * Appends the path to a cairo context for drawing with Cairo.
 *
 * This may cause some suboptimal conversions to be performed as
 * Cairo does not support all features of `GskPath`.
 *
 * This function does not clear the existing Cairo path. Call
 * cairo_new_path() if you want this.
 *
 * Since: 4.14
 */

void
gsk_path_to_cairo (GskPath *self,
                   cairo_t *cr)
{
  g_return_if_fail (self != NULL);
  g_return_if_fail (cr != NULL);

  gsk_path_foreach_with_tolerance (self,
                                   GSK_PATH_FOREACH_ALLOW_CUBIC,
                                   cairo_get_tolerance (cr),
                                   gsk_path_to_cairo_add_op,
                                   cr);
}

/**
 * gsk_path_is_empty:
 * @self: a path
 *
 * Checks if the path is empty, i.e. contains no lines or curves.
 *
 * Returns: true if the path is empty
 *
 * Since: 4.14
 */

gboolean
gsk_path_is_empty (GskPath *self)
{
  g_return_val_if_fail (self != NULL, FALSE);

  return self->n_contours == 0;
}

/**
 * gsk_path_is_closed:
 * @self: a path
 *
 * Returns if the path represents a single closed contour.
 *
 * Returns: true if the path is closed
 *
 * Since: 4.14
 */

gboolean
gsk_path_is_closed (GskPath *self)
{
  g_return_val_if_fail (self != NULL, FALSE);

  /* XXX: is the empty path closed? Currently it's not */
  if (self->n_contours != 1)
    return FALSE;

  return gsk_contour_get_flags (self->contours[0]) & GSK_PATH_CLOSED ? TRUE : FALSE;
}

/**
 * gsk_path_get_bounds:
 * @self: a path
 * @bounds: (out caller-allocates): return location for the bounds
 *
 * Computes the bounds of the given path.
 *
 * The returned bounds may be larger than necessary, because this
 * function aims to be fast, not accurate. The bounds are guaranteed
 * to contain the path. For accurate bounds, use
 * [method@Gsk.Path.get_tight_bounds].
 *
 * It is possible that the returned rectangle has 0 width and/or height.
 * This can happen when the path only describes a point or an
 * axis-aligned line.
 *
 * If the path is empty, false is returned and @bounds are set to
 * graphene_rect_zero(). This is different from the case where the path
 * is a single point at the origin, where the @bounds will also be set to
 * the zero rectangle but true will be returned.
 *
 * Returns: true if the path has bounds, false if the path is known
 *   to be empty and have no bounds
 *
 * Since: 4.14
 */

gboolean
gsk_path_get_bounds (GskPath         *self,
                     graphene_rect_t *bounds)
{
  GskBoundingBox b;

  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (bounds != NULL, FALSE);

  if (self->n_contours == 0)
    {
      graphene_rect_init_from_rect (bounds, graphene_rect_zero ());
      return FALSE;
    }

  gsk_contour_get_bounds (self->contours[0], &b);

  for (gsize i = 1; i < self->n_contours; i++)
    {
      GskBoundingBox tmp;

      gsk_contour_get_bounds (self->contours[i], &tmp);
      gsk_bounding_box_union (&b, &tmp, &b);
    }

  gsk_bounding_box_to_rect (&b, bounds);

  return TRUE;
}

/**
 * gsk_path_get_tight_bounds:
 * @self: a path
 * @bounds: (out caller-allocates): return location for the bounds
 *
 * Computes the tight bounds of the given path.
 *
 * This function works harder than [method@Gsk.Path.get_bounds] to
 * produce the smallest possible bounds.
 *
 * Returns: true if the path has bounds, false if the path is known
 *   to be empty and have no bounds
 *
 * Since: 4.22
 */

gboolean
gsk_path_get_tight_bounds (GskPath         *self,
                           graphene_rect_t *bounds)
{
  GskBoundingBox b;

  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (bounds != NULL, FALSE);

  if (self->n_contours == 0)
    {
      graphene_rect_init_from_rect (bounds, graphene_rect_zero ());
      return FALSE;
    }

  gsk_contour_get_tight_bounds (self->contours[0], &b);

  for (gsize i = 1; i < self->n_contours; i++)
    {
      GskBoundingBox tmp;

      gsk_contour_get_tight_bounds (self->contours[i], &tmp);
      gsk_bounding_box_union (&b, &tmp, &b);
    }

  gsk_bounding_box_to_rect (&b, bounds);

  return TRUE;
}

/**
 * gsk_path_get_stroke_bounds:
 * @self: a path
 * @stroke: stroke parameters
 * @bounds: (out caller-allocates): the bounds to fill in
 *
 * Computes the bounds for stroking the given path with the
 * given parameters.
 *
 * The returned bounds may be larger than necessary, because this
 * function aims to be fast, not accurate. The bounds are guaranteed
 * to contain the area affected by the stroke, including protrusions
 * like miters.
 *
 * Returns: true if the path has bounds, false if the path is known
 *   to be empty and have no bounds.
 *
 * Since: 4.14
 */

gboolean
gsk_path_get_stroke_bounds (GskPath         *self,
                            const GskStroke *stroke,
                            graphene_rect_t *bounds)
{
  GskBoundingBox b;

  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (bounds != NULL, FALSE);

  if (self->n_contours == 0)
    {
      graphene_rect_init_from_rect (bounds, graphene_rect_zero ());
      return FALSE;
    }

  gsk_contour_get_stroke_bounds (self->contours[0], stroke, &b);

  for (gsize i = 1; i < self->n_contours; i++)
    {
      GskBoundingBox tmp;

      if (gsk_contour_get_stroke_bounds (self->contours[i], stroke, &tmp))
        gsk_bounding_box_union (&b, &tmp, &b);
    }

  gsk_bounding_box_to_rect (&b, bounds);

  return TRUE;
}

/**
 * gsk_path_in_fill:
 * @self: a path
 * @point: the point to test
 * @fill_rule: the fill rule to follow
 *
 * Returns whether a point is inside the fill area of a path.
 *
 * Note that this function assumes that filling a contour
 * implicitly closes it.
 *
 * Returns: true if @point is inside
 *
 * Since: 4.14
 */

gboolean
gsk_path_in_fill (GskPath                *self,
                  const graphene_point_t *point,
                  GskFillRule             fill_rule)
{
  int winding = 0;

  for (int i = 0; i < self->n_contours; i++)
    winding += gsk_contour_get_winding (self->contours[i], point);

  switch (fill_rule)
    {
    case GSK_FILL_RULE_EVEN_ODD:
      return winding & 1;
    case GSK_FILL_RULE_WINDING:
      return winding != 0;
    default:
      g_assert_not_reached ();
    }
}

/**
 * gsk_path_get_start_point:
 * @self: a path
 * @result: (out caller-allocates): return location for point
 *
 * Gets the start point of the path.
 *
 * An empty path has no points, so false
 * is returned in this case.
 *
 * Returns: true if @result was filled
 *
 * Since: 4.14
 */

gboolean
gsk_path_get_start_point (GskPath      *self,
                          GskPathPoint *result)
{
  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (result != NULL, FALSE);

  if (self->n_contours == 0)
    return FALSE;

  /* Conceptually, there is always a move at the
   * beginning, which jumps from where to the start
   * point of the contour, so we use idx == 1 here.
   */

  result->contour = 0;
  result->idx = 1;
  result->idx = MIN (1, gsk_contour_get_n_ops (self->contours[0]) - 1);
  result->t = 0;

  return TRUE;
}

/**
 * gsk_path_get_end_point:
 * @self: a path
 * @result: (out caller-allocates): return location for point
 *
 * Gets the end point of the path.
 *
 * An empty path has no points, so false
 * is returned in this case.
 *
 * Returns: true if @result was filled
 *
 * Since: 4.14
 */

gboolean
gsk_path_get_end_point (GskPath      *self,
                        GskPathPoint *result)
{
  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (result != NULL, FALSE);

  if (self->n_contours == 0)
    return FALSE;

  result->contour = self->n_contours - 1;
  result->idx = gsk_contour_get_n_ops (self->contours[self->n_contours - 1]) - 1;
  result->t = 1;

  return TRUE;
}

/**
 * gsk_path_get_next:
 * @self: a path
 * @point: (inout): the current point
 *
 * Moves @point to the next vertex.
 *
 * An empty path has no points, so false
 * is returned in this case.
 *
 * Returns: true if @point was set
 *
 * Since: 4.22
 */

gboolean
gsk_path_get_next (GskPath      *self,
                   GskPathPoint *point)
{
  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (point != NULL, FALSE);

  if (self->n_contours == 0)
    return FALSE;

  if (point->t < 1)
    {
      point->t = 1;
    }
  else if (point->idx < gsk_contour_get_n_ops (self->contours[point->contour]) - 1)
    {
      point->idx++;
    }
  else if (point->contour < self->n_contours - 1)
    {
      point->contour++;
      point->idx = 0;
      point->t = 0;
    }
  else
    {
      return FALSE;
    }

  return TRUE;
}

/**
 * gsk_path_get_previous:
 * @self: a path
 * @point: (inout): the current point
 *
 * Moves @point to the previous vertex.
 *
 * An empty path has no points, so false
 * is returned in this case.
 *
 * Returns: true if @point was set
 *
 * Since: 4.22
 */

gboolean
gsk_path_get_previous (GskPath      *self,
                       GskPathPoint *point)
{
  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (point != NULL, FALSE);

  if (self->n_contours == 0)
    return FALSE;

  if (point->t > 0)
    {
      point->t = 0;
    }
  else if (point->idx > 0)
    {
      point->idx--;
    }
  else if (point->contour > 0)
    {
      point->contour--;
      point->idx = gsk_contour_get_n_ops (self->contours[point->contour]) - 1;
      point->t = 1;
    }
  else
    {
      return FALSE;
    }

  return TRUE;
}

/**
 * gsk_path_get_closest_point:
 * @self: a path
 * @point: the point
 * @threshold: maximum allowed distance
 * @result: (out caller-allocates): return location for the closest point
 * @distance: (out) (optional): return location for the distance
 *
 * Computes the closest point on the path to the given point.
 *
 * If there is no point closer than the given threshold,
 * false is returned.
 *
 * Returns: true if @point was set to the closest point
 *   on @self, false if no point is closer than @threshold
 *
 * Since: 4.14
 */

gboolean
gsk_path_get_closest_point (GskPath                *self,
                            const graphene_point_t *point,
                            float                   threshold,
                            GskPathPoint           *result,
                            float                  *distance)
{
  gboolean found;

  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (point != NULL, FALSE);
  g_return_val_if_fail (threshold >= 0FALSE);
  g_return_val_if_fail (result != NULL, FALSE);

  found = FALSE;

  for (int i = 0; i < self->n_contours; i++)
    {
      float dist;

      if (gsk_contour_get_closest_point (self->contours[i], point, threshold, result, &dist))
        {
          found = TRUE;
          g_assert (0 <= result->t && result->t <= 1);
          result->contour = i;
          threshold = dist;

          if (distance)
            *distance = dist;
        }
    }

  return found;
}

/**
 * gsk_path_equal:
 * @path1: a path
 * @path2: another path
 *
 * Returns whether two paths have identical structure.
 *
 * Note that it is possible to construct paths that render
 * identical even though they don't have the same structure.
 *
 * Returns: true if @path1 and @path2 have identical structure
 *
 * Since: 4.22
 */

gboolean
gsk_path_equal (const GskPath *path1,
                const GskPath *path2)
{
  if (path1 == path2)
    return TRUE;

  if (path1->n_contours != path2->n_contours)
    return FALSE;

  for (int i = 0; i < path1->n_contours; i++)
    if (!gsk_contour_equal (path1->contours[i], path2->contours[i]))
      return FALSE;

  return TRUE;
}

/* }}} */
/* {{{ Foreach and decomposition */

/**
 * gsk_path_foreach:
 * @self: a path
 * @flags: flags to pass to the foreach function
 * @func: (scope call) (closure user_data): the function to call for operations
 * @user_data: (nullable): user data passed to @func
 *
 * Calls @func for every operation of the path.
 *
 * Note that this may only approximate @self, because paths can contain
 * optimizations for various specialized contours, and depending on the
 * @flags, the path may be decomposed into simpler curves than the ones
 * that it contained originally.
 *
 * This function serves two purposes:
 *
 * - When the @flags allow everything, it provides access to the raw,
 *   unmodified data of the path.
 * - When the @flags disallow certain operations, it provides
 *   an approximation of the path using just the allowed operations.
 *
 * Returns: false if @func returned false, true otherwise.
 *
 * Since: 4.14
 */

gboolean
gsk_path_foreach (GskPath             *self,
                  GskPathForeachFlags  flags,
                  GskPathForeachFunc   func,
                  gpointer             user_data)
{
  g_return_val_if_fail (self != NULL, FALSE);
  g_return_val_if_fail (func, FALSE);

  return gsk_path_foreach_with_tolerance (self,
                                          flags,
                                          GSK_PATH_TOLERANCE_DEFAULT,
                                          func,
                                          user_data);
}

typedef struct _GskPathForeachTrampoline GskPathForeachTrampoline;
struct _GskPathForeachTrampoline
{
  GskPathForeachFlags flags;
  double tolerance;
  GskPathForeachFunc func;
  gpointer user_data;
};

static gboolean
gsk_path_foreach_trampoline_add_line (const graphene_point_t *from,
                                      const graphene_point_t *to,
                                      float                   from_progress,
                                      float                   to_progress,
                                      GskCurveLineReason      reason,
                                      gpointer                data)
{
  GskPathForeachTrampoline *trampoline = data;

  return trampoline->func (GSK_PATH_LINE,
                           (graphene_point_t[2]) { *from, *to },
                           2,
                           0.f,
                           trampoline->user_data);
}

static gboolean
gsk_path_foreach_trampoline_add_curve (GskPathOperation        op,
                                       const graphene_point_t *pts,
                                       gsize                   n_pts,
                                       float                   weight,
                                       gpointer                data)
{
  GskPathForeachTrampoline *trampoline = data;

  return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
}

static gboolean
gsk_path_foreach_trampoline (GskPathOperation        op,
                             const graphene_point_t *pts,
                             gsize                   n_pts,
                             float                   weight,
                             gpointer                data)
{
  GskPathForeachTrampoline *trampoline = data;
  GskAlignedPoint *aligned = g_alloca (sizeof (graphene_point_t) * n_pts);

  /* We can't necessarily guarantee that pts is 8-byte aligned
   * (probably it is, but we've been through too many layers of
   * indirection to be sure) so copy it into a buffer that is
   * definitely suitably-aligned. */

  memcpy (aligned, pts, sizeof (graphene_point_t) * n_pts);

  switch (op)
    {
    case GSK_PATH_MOVE:
    case GSK_PATH_CLOSE:
    case GSK_PATH_LINE:
      return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);

    case GSK_PATH_QUAD:
      {
        GskCurve curve;

        if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_QUAD)
          return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
        else if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
          {
            return trampoline->func (GSK_PATH_CUBIC,
                                     (graphene_point_t[4]) {
                                       pts[0],
                                       GRAPHENE_POINT_INIT ((pts[0].x + 2 * pts[1].x) / 3,
                                                            (pts[0].y + 2 * pts[1].y) / 3),
                                       GRAPHENE_POINT_INIT ((pts[2].x + 2 * pts[1].x) / 3,
                                                            (pts[2].y + 2 * pts[1].y) / 3),
                                       pts[2],
                                     },
                                     4,
                                     weight,
                                     trampoline->user_data);
          }

        gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_QUAD, aligned));
        return gsk_curve_decompose (&curve,
                                    trampoline->tolerance,
                                    gsk_path_foreach_trampoline_add_line,
                                    trampoline);
      }

    case GSK_PATH_CUBIC:
      {
        GskCurve curve;

        if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CUBIC)
          return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);

        gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CUBIC, aligned));
        if (trampoline->flags & (GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CONIC))
          return gsk_curve_decompose_curve (&curve,
                                            trampoline->flags,
                                            trampoline->tolerance,
                                            gsk_path_foreach_trampoline_add_curve,
                                            trampoline);

        return gsk_curve_decompose (&curve,
                                    trampoline->tolerance,
                                    gsk_path_foreach_trampoline_add_line,
                                    trampoline);
      }

    case GSK_PATH_CONIC:
      {
        GskCurve curve;

        if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CONIC)
          return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);

        gsk_curve_init (&curve, gsk_pathop_encode (GSK_PATH_CONIC, (GskAlignedPoint[4]) { { pts[0] }, { pts[1] }, { { weight, 0.f } }, { pts[2] } } ));
        if (trampoline->flags & (GSK_PATH_FOREACH_ALLOW_QUAD|GSK_PATH_FOREACH_ALLOW_CUBIC))
          return gsk_curve_decompose_curve (&curve,
                                            trampoline->flags,
                                            trampoline->tolerance,
                                            gsk_path_foreach_trampoline_add_curve,
                                            trampoline);

        return gsk_curve_decompose (&curve,
                                    trampoline->tolerance,
                                    gsk_path_foreach_trampoline_add_line,
                                    trampoline);
      }

    default:
      g_assert_not_reached ();
      return FALSE;
    }
}

#define ALLOW_ANY (GSK_PATH_FOREACH_ALLOW_QUAD  | \
                   GSK_PATH_FOREACH_ALLOW_CUBIC | \
                   GSK_PATH_FOREACH_ALLOW_CONIC)

gboolean
gsk_path_foreach_with_tolerance (GskPath             *self,
                                 GskPathForeachFlags  flags,
                                 double               tolerance,
                                 GskPathForeachFunc   func,
                                 gpointer             user_data)
{
  GskPathForeachTrampoline trampoline;
  gsize i;

  /* If we need to massage the data, set up a trampoline here */
  if ((flags & ALLOW_ANY) != ALLOW_ANY)
    {
      trampoline = (GskPathForeachTrampoline) { flags, tolerance, func, user_data };
      func = gsk_path_foreach_trampoline;
      user_data = &trampoline;
    }

  for (i = 0; i < self->n_contours; i++)
    {
      if (!gsk_contour_foreach (self->contours[i], func, user_data))
        return FALSE;
    }

  return TRUE;
}

/* }}} */

/* vim:set foldmethod=marker: */

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik