[cairo-commit] cairo/src cairo_pattern.c,1.26,1.27

Owen Taylor commit at pdx.freedesktop.org
Sun Mar 6 12:05:25 PST 2005


Committed by: otaylor

Update of /cvs/cairo/cairo/src
In directory gabe:/tmp/cvs-serv21362/src

Modified Files:
	cairo_pattern.c 
Log Message:
2005-03-06  Owen Taylor  <otaylor at redhat.com>

        * src/cairo_pattern.c (_cairo_image_data_set_linear): Comment
        and clean up the gradient computation.
        (_cairo_linear_pattern_classify): Determine if a linear
        gradient is horizontal or vertical.
        (_cairo_pattern_acquire_surface_for_gradient): Optimize
        horizontal/vertical gradients with a repeating surface.

        * test/linear_gradient.c: Test case for linear gradients
        at angles and with a rotated pattern matrix.


Index: cairo_pattern.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo_pattern.c,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -d -r1.26 -r1.27
--- cairo_pattern.c	4 Mar 2005 17:41:34 -0000	1.26
+++ cairo_pattern.c	6 Mar 2005 20:05:23 -0000	1.27
@@ -642,9 +642,8 @@
 {
     int x, y;
     cairo_point_double_t point0, point1;
-    double px, py, ex, ey;
     double a, b, c, d, tx, ty;
-    double length, start, angle, fx, fy, factor;
+    double scale, start, dx, dy, factor;
     cairo_shader_op_t op;
     cairo_status_t status;
 
@@ -652,6 +651,16 @@
     if (status)
 	return status;
 
+    /* We compute the position in the linear gradient for
+     * a point q as:
+     *
+     *  [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2
+     *
+     * The computation is done in pattern space. The 
+     * calculation could be heavily optimized by using the
+     * fact that 'factor' increases linearly in both
+     * directions.
+     */
     point0.x = pattern->point0.x;
     point0.y = pattern->point0.y;
     point1.x = pattern->point1.x;
@@ -659,28 +668,24 @@
 
     cairo_matrix_get_affine (&pattern->base.base.matrix,
 			     &a, &b, &c, &d, &tx, &ty);
-    
-    length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) +
-		   (point1.y - point0.y) * (point1.y - point0.y));
-    length = (length) ? 1.0 / length : CAIRO_MAXSHORT;
 
-    angle = -atan2 (point1.y - point0.y, point1.x - point0.x);
-    fx = cos (angle);
-    fy = -sin (angle);
-    
-    start = fx * point0.x;
-    start += fy * point0.y;
+    dx = point1.x - point0.x;
+    dy = point1.y - point0.y;
+    scale = dx * dx + dy * dy;
+    scale = (scale) ? 1.0 / scale : 1.0;
+
+    start = dx * point0.x + dy * point0.y;
 
     for (y = 0; y < height; y++) {
 	for (x = 0; x < width; x++) {
-	    px = x + offset_x;
-	    py = y + offset_y;
+	    double qx_device = x + offset_x;
+	    double qy_device = y + offset_y;
 		
-	    /* transform fragment */
-	    ex = a * px + c * py + tx;
-	    ey = b * px + d * py + ty;
+	    /* transform fragment into pattern space */
+	    double qx = a * qx_device + c * qy_device + tx;
+	    double qy = b * qx_device + d * qy_device + ty;
 	    
-	    factor = ((fx * ex + fy * ey) - start) * length;
+	    factor = ((dx * qx + dy * qy) - start) * scale;
 
 	    _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++);
 	}
@@ -691,6 +696,64 @@
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
+				double		       offset_x,
+				double		       offset_y,
+				int		       width,
+				int		       height,
+				cairo_bool_t           *is_horizontal,
+				cairo_bool_t           *is_vertical)
+{
+    cairo_point_double_t point0, point1;
+    double a, b, c, d, tx, ty;
+    double scale, start, dx, dy;
+    cairo_fixed_t factors[3];
+    int i;
+
+    /* To classidy a pattern as horizontal or vertical, we first
+     * compute the (fixed point) factors at the corners of the
+     * pattern. We actually only need 3/4 corners, so we skip the
+     * fourth.
+     */
+    point0.x = pattern->point0.x;
+    point0.y = pattern->point0.y;
+    point1.x = pattern->point1.x;
+    point1.y = pattern->point1.y;
+
+    cairo_matrix_get_affine (&pattern->base.base.matrix,
+			     &a, &b, &c, &d, &tx, &ty);
+
+    dx = point1.x - point0.x;
+    dy = point1.y - point0.y;
+    scale = dx * dx + dy * dy;
+    scale = (scale) ? 1.0 / scale : 1.0;
+
+    start = dx * point0.x + dy * point0.y;
+
+    for (i = 0; i < 3; i++) {
+	double qx_device = (i % 2) * (width - 1) + offset_x;
+	double qy_device = (i / 2) * (height - 1) + offset_y;
+
+	/* transform fragment into pattern space */
+	double qx = a * qx_device + c * qy_device + tx;
+	double qy = b * qx_device + d * qy_device + ty;
+	
+	factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
+    }
+
+    /* We consider a pattern to be vertical if the fixed point factor
+     * at the two upper corners is the same. We could accept a small
+     * change, but determining what change is acceptable would require
+     * sorting the stops in the pattern and looking at the differences.
+     *
+     * Horizontal works the same way with the two left corners.
+     */
+
+    *is_vertical = factors[1] == factors[0];
+    *is_horizontal = factors[2] == factors[0];
+}
+
 static cairo_status_t
 _cairo_image_data_set_radial (cairo_radial_pattern_t *pattern,
 			      double		     offset_x,
@@ -828,6 +891,24 @@
     cairo_image_surface_t *image;
     cairo_status_t	  status;
     uint32_t		  *data;
+    cairo_bool_t          repeat = FALSE;
+
+    if (pattern->base.type == CAIRO_PATTERN_LINEAR) {
+	cairo_bool_t is_horizontal;
+	cairo_bool_t is_vertical;
+
+	_cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
+					x, y, width, height,
+					&is_horizontal, &is_vertical);
+	if (is_horizontal) {
+	    height = 1;
+	    repeat = TRUE;
+	}
+	if (is_vertical) {
+	    width = 1;
+	    repeat = TRUE;
+	}
+    }
     
     data = malloc (width * height * 4);
     if (!data)
@@ -873,7 +954,7 @@
     attr->x_offset = -x;
     attr->y_offset = -y;
     cairo_matrix_set_identity (&attr->matrix);
-    attr->extend = CAIRO_EXTEND_NONE;
+    attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
     attr->filter = CAIRO_FILTER_NEAREST;
     attr->acquired = FALSE;
     




More information about the cairo-commit mailing list