[gtk/path-work-rebased: 1000/1045] path: Add gsk_path_get_stroke_bounds




commit 8756f89d9e9a599c9e05ded7c994d136246fe741
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Dec 5 19:23:33 2020 -0500

    path: Add gsk_path_get_stroke_bounds
    
    A relatively cheap way to get bounds for the area
    that would be affected by stroking a path.

 gsk/gskcontour.c        | 98 +++++++++++++++++++++++++++++++++++++++++++++++--
 gsk/gskcontourprivate.h |  3 ++
 gsk/gskpath.c           | 50 +++++++++++++++++++++++++
 gsk/gskpath.h           |  4 ++
 4 files changed, 152 insertions(+), 3 deletions(-)
---
diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c
index 1120aa385d..db7b854aa1 100644
--- a/gsk/gskcontour.c
+++ b/gsk/gskcontour.c
@@ -25,6 +25,7 @@
 #include "gskpathbuilder.h"
 #include "gskpathprivate.h"
 #include "gsksplineprivate.h"
+#include "gskstrokeprivate.h"
 
 typedef struct _GskContourClass GskContourClass;
 
@@ -81,6 +82,9 @@ struct _GskContourClass
                                                  gpointer                measure_data,
                                                  const graphene_point_t *point,
                                                  gboolean               *on_edge);
+  gboolean              (* get_stroke_bounds)   (const GskContour       *contour,
+                                                 const GskStroke        *stroke,
+                                                 graphene_rect_t        *bounds);
 };
 
 static gsize
@@ -474,6 +478,17 @@ gsk_rect_contour_get_winding (const GskContour       *contour,
   return 0;
 }
 
+static gboolean
+gsk_rect_contour_get_stroke_bounds (const GskContour       *contour,
+                                    const GskStroke        *stroke,
+                                    graphene_rect_t        *bounds)
+{
+  const GskRectContour *self = (const GskRectContour *) contour;
+  graphene_rect_init (bounds, self->x, self->y, self->width, self->height);
+  graphene_rect_inset (bounds, - stroke->line_width / 2, - stroke->line_width / 2);
+  return TRUE;
+}
+
 static const GskContourClass GSK_RECT_CONTOUR_CLASS =
 {
   sizeof (GskRectContour),
@@ -490,7 +505,8 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS =
   gsk_rect_contour_get_closest_point,
   gsk_rect_contour_copy,
   gsk_rect_contour_add_segment,
-  gsk_rect_contour_get_winding
+  gsk_rect_contour_get_winding,
+  gsk_rect_contour_get_stroke_bounds
 };
 
 GskContour *
@@ -801,6 +817,23 @@ gsk_circle_contour_get_winding (const GskContour       *contour,
   return 0;
 }
 
+static gboolean
+gsk_circle_contour_get_stroke_bounds (const GskContour       *contour,
+                                      const GskStroke        *stroke,
+                                      graphene_rect_t        *bounds)
+{
+  const GskCircleContour *self = (const GskCircleContour *) contour;
+
+  graphene_rect_init (bounds,
+                      self->center.x - self->radius,
+                      self->center.y - self->radius,
+                      2 * self->radius,
+                      2 * self->radius);
+  graphene_rect_inset (bounds, - stroke->line_width / 2, - stroke->line_width / 2);
+
+  return TRUE;
+}
+
 static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
 {
   sizeof (GskCircleContour),
@@ -817,7 +850,8 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
   gsk_circle_contour_get_closest_point,
   gsk_circle_contour_copy,
   gsk_circle_contour_add_segment,
-  gsk_circle_contour_get_winding
+  gsk_circle_contour_get_winding,
+  gsk_circle_contour_get_stroke_bounds
 };
 
 GskContour *
@@ -1470,6 +1504,55 @@ gsk_standard_contour_get_winding (const GskContour       *contour,
   return winding;
 }
 
+static gboolean
+add_stroke_bounds (GskPathOperation        op,
+                   const graphene_point_t *pts,
+                   gsize                   n_pts,
+                   float                   weight,
+                   gpointer                user_data)
+{
+  struct {
+    graphene_rect_t *bounds;
+    float lw;
+    float mw;
+  } *data = user_data;
+  graphene_rect_t bounds;
+
+  for (int i = 1; i < n_pts - 1; i++)
+    {
+      graphene_rect_init (&bounds, pts[i].x - data->lw/2, pts[i].y - data->lw/2, data->lw, data->lw);
+      graphene_rect_union (&bounds, data->bounds, data->bounds);
+    }
+
+  graphene_rect_init (&bounds, pts[n_pts - 1].x - data->mw/2, pts[n_pts  - 1].y - data->mw/2, data->mw, 
data->mw);
+  graphene_rect_union (&bounds, data->bounds, data->bounds);
+
+  return TRUE;
+}
+
+static gboolean
+gsk_standard_contour_get_stroke_bounds (const GskContour *contour,
+                                        const GskStroke  *stroke,
+                                        graphene_rect_t  *bounds)
+{
+  GskStandardContour *self = (GskStandardContour *) contour;
+  struct {
+    graphene_rect_t *bounds;
+    float lw;
+    float mw;
+  } data;
+
+  data.bounds = bounds;
+  data.lw = stroke->line_width;
+  data.mw = MAX (stroke->miter_limit, 1.f) * stroke->line_width;
+
+  graphene_rect_init (bounds, self->points[0].x - data.mw/2, self->points[0].y - data.mw/2, data.mw, 
data.mw);
+
+  gsk_standard_contour_foreach (contour, GSK_PATH_TOLERANCE_DEFAULT, add_stroke_bounds, &data);
+
+  return TRUE;
+}
+
 static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
 {
   sizeof (GskStandardContour),
@@ -1486,7 +1569,8 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
   gsk_standard_contour_get_closest_point,
   gsk_standard_contour_copy,
   gsk_standard_contour_add_segment,
-  gsk_standard_contour_get_winding
+  gsk_standard_contour_get_winding,
+  gsk_standard_contour_get_stroke_bounds
 };
 
 /* You must ensure the contour has enough size allocated,
@@ -1649,6 +1733,14 @@ gsk_contour_get_winding (const GskContour       *self,
   return self->klass->get_winding (self, measure_data, point, on_edge);
 }
 
+gboolean
+gsk_contour_get_stroke_bounds (const GskContour *self,
+                               const GskStroke  *stroke,
+                               graphene_rect_t  *bounds)
+{
+  return self->klass->get_stroke_bounds (self, stroke, bounds);
+}
+
 void
 gsk_contour_copy (GskContour       *dest,
                   const GskContour *src)
diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h
index abb8ab588d..391b502314 100644
--- a/gsk/gskcontourprivate.h
+++ b/gsk/gskcontourprivate.h
@@ -96,6 +96,9 @@ void                    gsk_contour_add_segment                 (const GskContou
                                                                  gpointer                measure_data,
                                                                  float                   start,
                                                                  float                   end);
+gboolean                gsk_contour_get_stroke_bounds           (const GskContour       *self,
+                                                                 const GskStroke        *stroke,
+                                                                 graphene_rect_t        *bounds);
 
 G_END_DECLS
 
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 97dadab520..2f2abc502c 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -1172,3 +1172,53 @@ error:
 
   return NULL;
 }
+
+/**
+ * gsk_path_get_stroke_bounds:
+ * @path: a `GtkPath`
+ * @stroke: stroke parameters
+ * @bounds: (out caller-allocates): the bounds to fill in
+ *
+ * Computes the bounds for stroking the given path with the
+ * parameters in @stroke.
+ *
+ * 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.
+ */
+gboolean
+gsk_path_get_stroke_bounds (GskPath         *path,
+                            const GskStroke *stroke,
+                            graphene_rect_t *bounds)
+{
+  gsize i;
+
+  g_return_val_if_fail (path != NULL, FALSE);
+  g_return_val_if_fail (bounds != NULL, FALSE);
+
+  for (i = 0; i < path->n_contours; i++)
+    {
+      if (gsk_contour_get_stroke_bounds (path->contours[i], stroke, bounds))
+        break;
+    }
+
+  if (i >= path->n_contours)
+    {
+      graphene_rect_init_from_rect (bounds, graphene_rect_zero ());
+      return FALSE;
+    }
+
+  for (i++; i < path->n_contours; i++)
+    {
+      graphene_rect_t tmp;
+
+      if (gsk_contour_get_stroke_bounds (path->contours[i], stroke, &tmp))
+        graphene_rect_union (bounds, &tmp, bounds);
+    }
+
+  return TRUE;
+}
diff --git a/gsk/gskpath.h b/gsk/gskpath.h
index 67f4486ff3..1c44acfede 100644
--- a/gsk/gskpath.h
+++ b/gsk/gskpath.h
@@ -98,6 +98,10 @@ gboolean                gsk_path_is_empty                       (GskPath
 GDK_AVAILABLE_IN_ALL
 gboolean                gsk_path_get_bounds                     (GskPath                *self,
                                                                  graphene_rect_t        *bounds);
+GDK_AVAILABLE_IN_ALL
+gboolean                gsk_path_get_stroke_bounds              (GskPath                *path,
+                                                                 const GskStroke        *stroke,
+                                                                 graphene_rect_t        *bounds);
 
 GDK_AVAILABLE_IN_ALL
 gboolean                gsk_path_foreach                        (GskPath                *self,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]