[Commit] cairo/src cairo.c, 1.14, 1.15 cairo.h, 1.15, 1.16 cairo_font.c, 1.6, 1.7 cairo_gstate.c, 1.14, 1.15 cairoint.h, 1.21, 1.22

Carl Worth commit at keithp.com
Mon Sep 29 09:36:32 PDT 2003


Committed by: cworth

Update of /local/src/CVS/cairo/src
In directory home.keithp.com:/tmp/cvs-serv7386/src

Modified Files:
	cairo.c cairo.h cairo_font.c cairo_gstate.c cairoint.h 
Log Message:
Added cairo_arc and cairo_arc_negative.

Index: cairo.c
===================================================================
RCS file: /local/src/CVS/cairo/src/cairo.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- cairo.c	25 Sep 2003 22:01:28 -0000	1.14
+++ cairo.c	29 Sep 2003 15:36:30 -0000	1.15
@@ -478,6 +478,53 @@
 }
 
 void
+cairo_arc (cairo_t *cr,
+	   double xc, double yc,
+	   double radius,
+	   double angle1, double angle2)
+{
+    if (cr->status)
+	return;
+
+    cr->status = _cairo_gstate_arc (cr->gstate,
+				    xc, yc,
+				    radius,
+				    angle1, angle2);
+}
+
+void
+cairo_arc_negative (cairo_t *cr,
+		    double xc, double yc,
+		    double radius,
+		    double angle1, double angle2)
+{
+    if (cr->status)
+	return;
+
+    cr->status = _cairo_gstate_arc_negative (cr->gstate,
+					     xc, yc,
+					     radius,
+					     angle1, angle2);
+}
+
+/* XXX: NYI
+void
+cairo_arc_to (cairo_t *cr,
+	      double x1, double y1,
+	      double x2, double y2,
+	      double radius)
+{
+    if (cr->status)
+	return;
+
+    cr->status = _cairo_gstate_arc_to (cr->gstate,
+				       x1, y1,
+				       x2, y2,
+				       radius);
+}
+*/
+
+void
 cairo_rel_move_to (cairo_t *cr, double dx, double dy)
 {
     if (cr->status)

Index: cairo.h
===================================================================
RCS file: /local/src/CVS/cairo/src/cairo.h,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- cairo.h	25 Sep 2003 22:01:28 -0000	1.15
+++ cairo.h	29 Sep 2003 15:36:30 -0000	1.16
@@ -261,6 +261,26 @@
 	  double x3, double y3);
 
 extern void __external_linkage
+cairo_arc (cairo_t *cr,
+	   double xc, double yc,
+	   double radius,
+	   double angle1, double angle2);
+
+extern void __external_linkage
+cairo_arc_negative (cairo_t *cr,
+		    double xc, double yc,
+		    double radius,
+		    double angle1, double angle2);
+
+/* XXX: NYI
+extern void __external_linkage
+cairo_arc_to (cairo_t *cr,
+	      double x1, double y1,
+	      double x2, double y2,
+	      double radius);
+*/
+
+extern void __external_linkage
 cairo_rel_move_to (cairo_t *cr, double dx, double dy);
 
 extern void __external_linkage

Index: cairo_font.c
===================================================================
RCS file: /local/src/CVS/cairo/src/cairo_font.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- cairo_font.c	5 Sep 2003 22:29:49 -0000	1.6
+++ cairo_font.c	29 Sep 2003 15:36:30 -0000	1.7
@@ -142,7 +142,16 @@
     /* XXX: The determinant gives an area expansion factor, so the
        math below should be correct for the (common) case of uniform
        X/Y scaling. Is there anything different we would want to do
-       for non-uniform X/Y scaling? */
+       for non-uniform X/Y scaling?
+
+       XXX: Actually, the reasoning above is bogus. A transformation
+       such as scale (N, 1/N) will give an expansion_factor of 1. So,
+       with the code below we'll end up with font_size == 1 instead of
+       N, (so the hinting will be all wrong). I think we want to use
+       the maximum eigen value rather than the square root of the
+       determinant.
+
+    */
     _cairo_matrix_compute_determinant (&matrix, &expansion);
     font_size = sqrt (fabs (expansion));
 

Index: cairo_gstate.c
===================================================================
RCS file: /local/src/CVS/cairo/src/cairo_gstate.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- cairo_gstate.c	25 Sep 2003 22:01:28 -0000	1.14
+++ cairo_gstate.c	29 Sep 2003 15:36:30 -0000	1.15
@@ -707,6 +707,272 @@
     return status;
 }
 
+/* Spline deviation from the circle in radius would be given by:
+
+	error = sqrt (x**2 + y**2) - 1
+
+   A simpler error function to work with is:
+
+	e = x**2 + y**2 - 1
+
+   From "Good approximation of circles by curvature-continuous Bezier
+   curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
+   Design 8 (1990) 22-41, we learn:
+
+	abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
+
+   and
+	abs (error) =~ 1/2 * e
+
+   Of course, this error value applies only for the particular spline
+   approximation that is used in _cairo_gstate_arc_segment.
+*/
+static double
+_arc_error_normalized (double angle)
+{
+    return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
+}
+
+static double
+_arc_max_angle_for_tolerance_normalized (double tolerance)
+{
+    double angle, error;
+    int i;
+
+    /* Use table lookup to reduce search time in most cases. */
+    struct {
+	double angle;
+	double error;
+    } table[] = {
+	{ M_PI / 1.0,   0.0185185185185185036127 },
+	{ M_PI / 2.0,   0.000272567143730179811158 },
+	{ M_PI / 3.0,   2.38647043651461047433e-05 },
+	{ M_PI / 4.0,   4.2455377443222443279e-06 },
+	{ M_PI / 5.0,   1.11281001494389081528e-06 },
+	{ M_PI / 6.0,   3.72662000942734705475e-07 },
+	{ M_PI / 7.0,   1.47783685574284411325e-07 },
+	{ M_PI / 8.0,   6.63240432022601149057e-08 },
+	{ M_PI / 9.0,   3.2715520137536980553e-08 },
+	{ M_PI / 10.0,  1.73863223499021216974e-08 },
+	{ M_PI / 11.0,  9.81410988043554039085e-09 },
+    };
+    int table_size = (sizeof (table) / sizeof (table[0]));
+
+    for (i = 0; i < table_size; i++)
+	if (table[i].error < tolerance)
+	    return table[i].angle;
+
+    ++i;
+    do {
+	angle = M_PI / i++;
+	error = _arc_error_normalized (angle);
+    } while (error > tolerance);
+
+    return angle;
+}
+
+static int
+_cairo_gstate_arc_segments_needed (cairo_gstate_t *gstate,
+				   double angle,
+				   double radius)
+{
+    double l1, l2, lmax;
+    double max_angle;
+
+    _cairo_matrix_compute_eigen_values (&gstate->ctm, &l1, &l2);
+
+    l1 = fabs (l1);
+    l2 = fabs (l2);
+    if (l1 > l2)
+	lmax = l1;
+    else
+	lmax = l2;
+
+    max_angle = _arc_max_angle_for_tolerance_normalized (gstate->tolerance / (radius * lmax));
+
+    return (int) ceil (angle / max_angle);
+}
+
+/* We want to draw a single spline approximating a circular arc radius
+   R from angle A to angle B. Since we want a symmetric spline that
+   matches the endpoints of the arc in position and slope, we know
+   that the spline control points must be:
+
+	(R * cos(A), R * sin(A))
+	(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
+	(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
+	(R * cos(B), R * sin(B))
+
+   for some value of h.
+
+   "Approximation of circular arcs by cubic poynomials", Michael
+   Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
+   various values of h along with error analysis for each.
+
+   From that paper, a very practical value of h is:
+
+	h = 4/3 * tan(angle/4)
+
+   This value does not give the spline with minimal error, but it does
+   provide a very good approximation, (6th-order convergence), and the
+   error expression is quite simple, (see the comment for
+   _arc_error_normalized).
+*/
+static cairo_status_t
+_cairo_gstate_arc_segment (cairo_gstate_t *gstate,
+			   double xc, double yc,
+			   double radius,
+			   double angle_A, double angle_B)
+{
+    cairo_status_t status;
+    double r_sin_A, r_cos_A;
+    double r_sin_B, r_cos_B;
+    double h;
+
+    r_sin_A = radius * sin (angle_A);
+    r_cos_A = radius * cos (angle_A);
+    r_sin_B = radius * sin (angle_B);
+    r_cos_B = radius * cos (angle_B);
+
+    h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
+
+    status = _cairo_gstate_curve_to (gstate,
+				     xc + r_cos_A - h * r_sin_A, yc + r_sin_A + h * r_cos_A,
+				     xc + r_cos_B + h * r_sin_B, yc + r_sin_B - h * r_cos_B,
+				     xc + r_cos_B, yc + r_sin_B);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gstate_arc_dir (cairo_gstate_t *gstate,
+		       double xc, double yc,
+		       double radius,
+		       double angle_min,
+		       double angle_max,
+		       cairo_direction_t dir)
+{
+    cairo_status_t status;
+
+    while (angle_max - angle_min > 4 * M_PI)
+	angle_max -= 2 * M_PI;
+
+    /* Recurse if drawing arc larger than pi */
+    if (angle_max - angle_min > M_PI) {
+	/* XXX: Something tells me this block could be condensed. */
+	if (dir == CAIRO_DIRECTION_FORWARD) {
+	    status = _cairo_gstate_arc_dir (gstate, xc, yc, radius,
+					    angle_min, angle_min + M_PI, dir);
+	    if (status)
+		return status;
+	    
+	    status = _cairo_gstate_arc_dir (gstate, xc, yc, radius,
+					    angle_min + M_PI, angle_max, dir);
+	    if (status)
+		return status;
+	} else {
+	    status = _cairo_gstate_arc_dir (gstate, xc, yc, radius,
+					    angle_min + M_PI, angle_max, dir);
+	    if (status)
+		return status;
+
+	    status = _cairo_gstate_arc_dir (gstate, xc, yc, radius,
+					    angle_min, angle_min + M_PI, dir);
+	    if (status)
+		return status;
+	}
+    } else {
+	int i, segments;
+	double angle, angle_step;
+
+	segments = _cairo_gstate_arc_segments_needed (gstate,
+						      angle_max - angle_min,
+						      radius);
+	angle_step = (angle_max - angle_min) / (double) segments;
+
+	if (dir == CAIRO_DIRECTION_FORWARD) {
+	    angle = angle_min;
+	} else {
+	    angle = angle_max;
+	    angle_step = - angle_step;
+	}
+
+	for (i = 0; i < segments; i++, angle += angle_step) {
+	    _cairo_gstate_arc_segment (gstate,
+				       xc, yc,
+				       radius,
+				       angle,
+				       angle + angle_step);
+	}
+	
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_arc (cairo_gstate_t *gstate,
+		   double xc, double yc,
+		   double radius,
+		   double angle1, double angle2)
+{
+    cairo_status_t status;
+
+    while (angle2 < angle1)
+	angle2 += 2 * M_PI;
+
+    status = _cairo_gstate_line_to (gstate,
+				    xc + radius * cos (angle1),
+				    yc + radius * sin (angle1));
+    if (status)
+	return status;
+
+    status = _cairo_gstate_arc_dir (gstate, xc, yc, radius,
+				    angle1, angle2, CAIRO_DIRECTION_FORWARD);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_arc_negative (cairo_gstate_t *gstate,
+			    double xc, double yc,
+			    double radius,
+			    double angle1, double angle2)
+{
+    cairo_status_t status;
+
+    while (angle2 > angle1)
+	angle2 -= 2 * M_PI;
+
+    status = _cairo_gstate_line_to (gstate,
+				    xc + radius * cos (angle1),
+				    yc + radius * sin (angle1));
+    if (status)
+	return status;
+
+    status = _cairo_gstate_arc_dir (gstate, xc, yc, radius,
+				    angle2, angle1, CAIRO_DIRECTION_REVERSE);
+    if (status)
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* XXX: NYI
+cairo_status_t
+_cairo_gstate_arc_to (cairo_gstate_t *gstate,
+		      double x1, double y1,
+		      double x2, double y2,
+		      double radius)
+{
+
+}
+*/
+
 cairo_status_t
 _cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy)
 {
@@ -775,7 +1041,7 @@
 {
     cairo_status_t status;
 
-    _cairo_pen_init (&gstate
+    _cairo_pen_init (&gstate);
     return CAIRO_STATUS_SUCCESS;
 }
 */

Index: cairoint.h
===================================================================
RCS file: /local/src/CVS/cairo/src/cairoint.h,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- cairoint.h	27 Sep 2003 12:08:38 -0000	1.21
+++ cairoint.h	29 Sep 2003 15:36:30 -0000	1.22
@@ -544,6 +544,18 @@
 			double x3, double y3);
 
 extern cairo_status_t __internal_linkage
+_cairo_gstate_arc (cairo_gstate_t *gstate,
+		   double xc, double yc,
+		   double radius,
+		   double angle1, double angle2);
+
+extern cairo_status_t __internal_linkage
+_cairo_gstate_arc_negative (cairo_gstate_t *gstate,
+			    double xc, double yc,
+			    double radius,
+			    double angle1, double angle2);
+
+extern cairo_status_t __internal_linkage
 _cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy);
 
 extern cairo_status_t __internal_linkage




More information about the Commit mailing list