No subject


Tue Feb 3 09:21:25 PST 2009


device extents and each try different tricks to achieve this.

The attached patch adds cairo_(stroke|fill|path)_device_extents() which
act the same as the current api but do not transform the extents back
into userspace. The patch is large because it refactors the existing
implementation to share a common device_extents implementation.

Comments welcome,

-Jeff

--cNdxnHkX5QqsyA0e
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="extents.patch"

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 9ee31b0..98dfc4d 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -787,30 +787,6 @@ _cairo_gstate_stroke_to_path (cairo_gstate_t *gstate)
 }
 */
 
-void
-_cairo_gstate_path_extents (cairo_gstate_t     *gstate,
-			    cairo_path_fixed_t *path,
-			    double *x1, double *y1,
-			    double *x2, double *y2)
-{
-    double px1, py1, px2, py2;
-
-    _cairo_path_fixed_bounds (path,
-			      &px1, &py1, &px2, &py2);
-
-    _cairo_gstate_backend_to_user_rectangle (gstate,
-					     &px1, &py1, &px2, &py2,
-					     NULL);
-    if (x1)
-	*x1 = px1;
-    if (y1)
-	*y1 = py1;
-    if (x2)
-	*x2 = px2;
-    if (y2)
-	*y2 = py2;
-}
-
 static cairo_status_t
 _cairo_gstate_copy_transformed_pattern (cairo_gstate_t  *gstate,
 					cairo_pattern_t **pattern,
@@ -1109,7 +1085,7 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate)
 }
 
 static void
-_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t	  *gstate,
+_cairo_gstate_traps_extents_to_device_rectangle (cairo_gstate_t	  *gstate,
                                                cairo_traps_t      *traps,
                                                double *x1, double *y1,
                                                double *x2, double *y2)
@@ -1118,40 +1094,18 @@ _cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t	  *gstate,
 
     if (traps->num_traps == 0) {
         /* no traps, so we actually won't draw anything */
-	if (x1)
-	    *x1 = 0.0;
-	if (y1)
-	    *y1 = 0.0;
-	if (x2)
-	    *x2 = 0.0;
-	if (y2)
-	    *y2 = 0.0;
     } else {
-	double px1, py1, px2, py2;
-
 	_cairo_traps_extents (traps, &extents);
 
-	px1 = _cairo_fixed_to_double (extents.p1.x);
-	py1 = _cairo_fixed_to_double (extents.p1.y);
-	px2 = _cairo_fixed_to_double (extents.p2.x);
-	py2 = _cairo_fixed_to_double (extents.p2.y);
-
-        _cairo_gstate_backend_to_user_rectangle (gstate,
-						 &px1, &py1, &px2, &py2,
-						 NULL);
-	if (x1)
-	    *x1 = px1;
-	if (y1)
-	    *y1 = py1;
-	if (x2)
-	    *x2 = px2;
-	if (y2)
-	    *y2 = py2;
+	*x1 = _cairo_fixed_to_double (extents.p1.x);
+	*y1 = _cairo_fixed_to_double (extents.p1.y);
+	*x2 = _cairo_fixed_to_double (extents.p2.x);
+	*y2 = _cairo_fixed_to_double (extents.p2.y);
     }
 }
 
 cairo_status_t
-_cairo_gstate_stroke_extents (cairo_gstate_t	 *gstate,
+_cairo_gstate_stroke_device_extents (cairo_gstate_t	 *gstate,
 			      cairo_path_fixed_t *path,
                               double *x1, double *y1,
 			      double *x2, double *y2)
@@ -1160,14 +1114,6 @@ _cairo_gstate_stroke_extents (cairo_gstate_t	 *gstate,
     cairo_traps_t traps;
 
     if (gstate->stroke_style.line_width <= 0.0) {
-	if (x1)
-	    *x1 = 0.0;
-	if (y1)
-	    *y1 = 0.0;
-	if (x2)
-	    *x2 = 0.0;
-	if (y2)
-	    *y2 = 0.0;
 	return CAIRO_STATUS_SUCCESS;
     }
 
@@ -1180,7 +1126,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t	 *gstate,
 						gstate->tolerance,
 						&traps);
     if (status == CAIRO_STATUS_SUCCESS) {
-	_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
+	_cairo_gstate_traps_extents_to_device_rectangle (gstate, &traps,
 						       x1, y1, x2, y2);
     }
 
@@ -1190,7 +1136,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t	 *gstate,
 }
 
 cairo_status_t
-_cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
+_cairo_gstate_fill_device_extents (cairo_gstate_t     *gstate,
 			    cairo_path_fixed_t *path,
                             double *x1, double *y1,
 			    double *x2, double *y2)
@@ -1205,7 +1151,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
 					      gstate->tolerance,
 					      &traps);
     if (status == CAIRO_STATUS_SUCCESS) {
-	_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
+	_cairo_gstate_traps_extents_to_device_rectangle (gstate, &traps,
 						       x1, y1, x2, y2);
     }
 
@@ -1214,6 +1160,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
     return status;
 }
 
+
 cairo_status_t
 _cairo_gstate_reset_clip (cairo_gstate_t *gstate)
 {
@@ -1246,37 +1193,23 @@ _cairo_gstate_int_clip_extents (cairo_gstate_t        *gstate,
 }
 
 cairo_status_t
-_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
-		            double         *x1,
-		            double         *y1,
-			    double         *x2,
-			    double         *y2)
+_cairo_gstate_clip_device_extents (cairo_gstate_t *gstate,
+				   double         *x1,
+				   double         *y1,
+				   double         *x2,
+				   double         *y2)
 {
     cairo_rectangle_int_t extents;
-    double px1, py1, px2, py2;
     cairo_status_t status;
 
     status = _cairo_gstate_int_clip_extents (gstate, &extents);
     if (unlikely (status))
         return status;
 
-    px1 = extents.x;
-    py1 = extents.y;
-    px2 = extents.x + (int) extents.width;
-    py2 = extents.y + (int) extents.height;
-
-    _cairo_gstate_backend_to_user_rectangle (gstate,
-					     &px1, &py1, &px2, &py2,
-					     NULL);
-
-    if (x1)
-	*x1 = px1;
-    if (y1)
-	*y1 = py1;
-    if (x2)
-	*x2 = px2;
-    if (y2)
-	*y2 = py2;
+    *x1 = extents.x;
+    *y1 = extents.y;
+    *x2 = extents.x + (int) extents.width;
+    *y2 = extents.y + (int) extents.height;
 
     return CAIRO_STATUS_SUCCESS;
 }
diff --git a/src/cairo.c b/src/cairo.c
index 4cdf685..181c8e6 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -1941,24 +1941,62 @@ void
 cairo_path_extents (cairo_t *cr,
 		    double *x1, double *y1, double *x2, double *y2)
 {
-    if (cr->status) {
-	if (x1)
-	    *x1 = 0.0;
-	if (y1)
-	    *y1 = 0.0;
-	if (x2)
-	    *x2 = 0.0;
-	if (y2)
-	    *y2 = 0.0;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+    if (!cr->status) {
+	_cairo_path_fixed_bounds (cr->path,
+			      &px1, &py1, &px2, &py2);
+	_cairo_gstate_backend_to_user_rectangle (cr->gstate,
+					     &px1, &py1, &px2, &py2,
+					     NULL);
+    }
 
-	return;
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
+}
+
+/**
+ * cairo_path_device_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in device-space coordinates covering the
+ * points on the current path. The computation is similar to
+ * cairo_path_extents() however the results are not transformed back into
+ * user space
+ *
+ * Since: 1.10
+ **/
+void
+cairo_path_device_extents (cairo_t *cr,
+		    double *x1, double *y1, double *x2, double *y2)
+{
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+	_cairo_path_fixed_bounds (cr->path,
+			      &px1, &py1, &px2, &py2);
     }
 
-    _cairo_gstate_path_extents (cr->gstate,
-				cr->path,
-				x1, y1, x2, y2);
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
 }
 
+
 /**
  * cairo_paint:
  * @cr: a cairo context
@@ -2349,25 +2387,83 @@ cairo_stroke_extents (cairo_t *cr,
                       double *x1, double *y1, double *x2, double *y2)
 {
     cairo_status_t status;
-
-    if (cr->status) {
-	if (x1)
-	    *x1 = 0.0;
-	if (y1)
-	    *y1 = 0.0;
-	if (x2)
-	    *x2 = 0.0;
-	if (y2)
-	    *y2 = 0.0;
-
-	return;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+        status = _cairo_gstate_stroke_device_extents (cr->gstate,
+					       cr->path,
+					       &px1, &py1, &px2, &py2);
+	if (likely(!status)) {
+	    _cairo_gstate_backend_to_user_rectangle (cr->gstate,
+		    &px1, &py1, &px2, &py2,
+		    NULL);
+	} else {
+	    _cairo_set_error (cr, status);
+	}
     }
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
+}
 
-    status = _cairo_gstate_stroke_extents (cr->gstate,
-					   cr->path,
-					   x1, y1, x2, y2);
-    if (unlikely (status))
-	_cairo_set_error (cr, status);
+/**
+ * cairo_stroke_device_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area that
+ * would be affected, (the "inked" area), by a cairo_stroke()
+ * operation operation given the current path and stroke
+ * parameters. If the current path is empty, returns an empty
+ * rectangle ((0,0), (0,0)). Surface dimensions and clipping are not
+ * taken into account.
+ *
+ * Note that if the line width is set to exactly zero, then
+ * cairo_stroke_extents() will return an empty rectangle. Contrast with
+ * cairo_path_extents() which can be used to compute the non-empty
+ * bounds as the line width approaches zero.
+ *
+ * Note that cairo_stroke_extents() must necessarily do more work to
+ * compute the precise inked areas in light of the stroke parameters,
+ * so cairo_path_extents() may be more desirable for sake of
+ * performance if non-inked path extents are desired.
+ *
+ * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(),
+ * cairo_set_line_cap(), cairo_set_dash(), and
+ * cairo_stroke_preserve().
+ *
+ * Since: 1.10
+ **/
+void
+cairo_stroke_device_extents (cairo_t *cr,
+                      double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_status_t status;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+        status = _cairo_gstate_stroke_device_extents (cr->gstate,
+					       cr->path,
+					       &px1, &py1, &px2, &py2);
+	if (unlikely (status))
+	    _cairo_set_error (cr, status);
+    }
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
 }
 
 /**
@@ -2400,25 +2496,80 @@ cairo_fill_extents (cairo_t *cr,
                     double *x1, double *y1, double *x2, double *y2)
 {
     cairo_status_t status;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+        status = _cairo_gstate_fill_device_extents (cr->gstate,
+					       cr->path,
+					       &px1, &py1, &px2, &py2);
+	if (likely(!status)) {
+	    _cairo_gstate_backend_to_user_rectangle (cr->gstate,
+		    &px1, &py1, &px2, &py2,
+		    NULL);
+	} else {
+	    _cairo_set_error (cr, status);
+	}
+    }
 
-    if (cr->status) {
-	if (x1)
-	    *x1 = 0.0;
-	if (y1)
-	    *y1 = 0.0;
-	if (x2)
-	    *x2 = 0.0;
-	if (y2)
-	    *y2 = 0.0;
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
+}
 
-	return;
+/**
+ * cairo_fill_device_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area that
+ * would be affected, (the "inked" area), by a cairo_fill() operation
+ * given the current path and fill parameters. If the current path is
+ * empty, returns an empty rectangle ((0,0), (0,0)). Surface
+ * dimensions and clipping are not taken into account.
+ *
+ * Contrast with cairo_path_extents(), which is similar, but returns
+ * non-zero extents for some paths with no inked area, (such as a
+ * simple line segment).
+ *
+ * Note that cairo_fill_extents() must necessarily do more work to
+ * compute the precise inked areas in light of the fill rule, so
+ * cairo_path_extents() may be more desirable for sake of performance
+ * if the non-inked path extents are desired.
+ *
+ * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve().
+ *
+ * Since: 1.10
+ **/
+void
+cairo_fill_device_extents (cairo_t *cr,
+                    double *x1, double *y1, double *x2, double *y2)
+{
+    cairo_status_t status;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+        status = _cairo_gstate_fill_device_extents (cr->gstate,
+					       cr->path,
+					       &px1, &py1, &px2, &py2);
+	if (unlikely (status))
+	    _cairo_set_error (cr, status);
     }
-
-    status = _cairo_gstate_fill_extents (cr->gstate,
-					 cr->path,
-					 x1, y1, x2, y2);
-    if (unlikely (status))
-	_cairo_set_error (cr, status);
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
 }
 
 /**
@@ -2535,25 +2686,68 @@ cairo_clip_extents (cairo_t *cr,
 		    double *x2, double *y2)
 {
     cairo_status_t status;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+	status = _cairo_gstate_clip_device_extents (cr->gstate, &px1, &py1, &px2, &py2);
+	if (likely(!status)) {
+	    _cairo_gstate_backend_to_user_rectangle (cr->gstate,
+		    &px1, &py1, &px2, &py2,
+		    NULL);
+	} else {
+	    _cairo_set_error (cr, status);
+	}
+    }
 
-    if (cr->status) {
-	if (x1)
-	    *x1 = 0.0;
-	if (y1)
-	    *y1 = 0.0;
-	if (x2)
-	    *x2 = 0.0;
-	if (y2)
-	    *y2 = 0.0;
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
+}
 
-	return;
+/**
+ * cairo_clip_device_extents:
+ * @cr: a cairo context
+ * @x1: left of the resulting extents
+ * @y1: top of the resulting extents
+ * @x2: right of the resulting extents
+ * @y2: bottom of the resulting extents
+ *
+ * Computes a bounding box in user coordinates covering the area inside the
+ * current clip.
+ *
+ * Since: 1.10
+ **/
+void
+cairo_clip_device_extents (cairo_t *cr,
+		    double *x1, double *y1,
+		    double *x2, double *y2)
+{
+    cairo_status_t status;
+    double px1 = 0.0, py1 = 0.0, px2 = 0.0, py2 = 0.0;
+
+    if (!cr->status) {
+	status = _cairo_gstate_clip_device_extents (cr->gstate, &px1, &py1, &px2, &py2);
+	if (unlikely(status)) {
+	    _cairo_set_error (cr, status);
+	}
     }
 
-    status = _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2);
-    if (unlikely (status))
-	_cairo_set_error (cr, status);
+    if (x1)
+	*x1 = px1;
+    if (y1)
+	*y1 = py1;
+    if (x2)
+	*x2 = px2;
+    if (y2)
+	*y2 = py2;
 }
 
+
 static cairo_rectangle_list_t *
 _cairo_rectangle_list_create_in_error (cairo_status_t status)
 {
diff --git a/src/cairo.h b/src/cairo.h
index 856f7af..a6077c1 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -686,6 +686,12 @@ cairo_path_extents (cairo_t *cr,
 		    double *x1, double *y1,
 		    double *x2, double *y2);
 
+cairo_public void
+cairo_path_device_extents (cairo_t *cr,
+			   double *x1, double *y1,
+			   double *x2, double *y2);
+
+
 /* Painting functions */
 cairo_public void
 cairo_paint (cairo_t *cr);
@@ -740,6 +746,16 @@ cairo_fill_extents (cairo_t *cr,
 		    double *x1, double *y1,
 		    double *x2, double *y2);
 
+cairo_public void
+cairo_stroke_device_extents (cairo_t *cr,
+			     double *x1, double *y1,
+			     double *x2, double *y2);
+
+cairo_public void
+cairo_fill_device_extents (cairo_t *cr,
+			   double *x1, double *y1,
+			   double *x2, double *y2);
+
 /* Clipping */
 cairo_public void
 cairo_reset_clip (cairo_t *cr);
@@ -755,6 +771,12 @@ cairo_clip_extents (cairo_t *cr,
 		    double *x1, double *y1,
 		    double *x2, double *y2);
 
+cairo_public void
+cairo_clip_device_extents (cairo_t *cr,
+			   double *x1, double *y1,
+			   double *x2, double *y2);
+
+
 /**
  * cairo_rectangle_t:
  * @x: X coordinate of the left side of the rectangle
diff --git a/src/cairoint.h b/src/cairoint.h
index 8115c81..5eed1cf 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1160,12 +1160,6 @@ _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
                                          double *x2, double *y2,
                                          cairo_bool_t *is_tight);
 
-cairo_private void
-_cairo_gstate_path_extents (cairo_gstate_t     *gstate,
-			    cairo_path_fixed_t *path,
-			    double *x1, double *y1,
-			    double *x2, double *y2);
-
 cairo_private cairo_status_t
 _cairo_gstate_paint (cairo_gstate_t *gstate);
 
@@ -1186,16 +1180,16 @@ cairo_private cairo_status_t
 _cairo_gstate_show_page (cairo_gstate_t *gstate);
 
 cairo_private cairo_status_t
-_cairo_gstate_stroke_extents (cairo_gstate_t	 *gstate,
-			      cairo_path_fixed_t *path,
-                              double *x1, double *y1,
-			      double *x2, double *y2);
+_cairo_gstate_stroke_device_extents (cairo_gstate_t	 *gstate,
+				     cairo_path_fixed_t *path,
+				     double *x1, double *y1,
+				     double *x2, double *y2);
 
 cairo_private cairo_status_t
-_cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
-			    cairo_path_fixed_t *path,
-                            double *x1, double *y1,
-			    double *x2, double *y2);
+_cairo_gstate_fill_device_extents (cairo_gstate_t     *gstate,
+				   cairo_path_fixed_t *path,
+				   double *x1, double *y1,
+				   double *x2, double *y2);
 
 cairo_private cairo_status_t
 _cairo_gstate_in_stroke (cairo_gstate_t	    *gstate,
@@ -1218,11 +1212,11 @@ cairo_private cairo_status_t
 _cairo_gstate_reset_clip (cairo_gstate_t *gstate);
 
 cairo_private cairo_status_t
-_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
-		            double         *x1,
-		            double         *y1,
-        		    double         *x2,
-        		    double         *y2);
+_cairo_gstate_clip_device_extents (cairo_gstate_t *gstate,
+				   double         *x1,
+				   double         *y1,
+				   double         *x2,
+				   double         *y2);
 
 cairo_private cairo_rectangle_list_t*
 _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate);

--cNdxnHkX5QqsyA0e--


More information about the cairo mailing list