[cairo-commit] 4 commits - src/cairoint.h src/cairo-quartz-private.h src/cairo-quartz-surface.c src/cairo-rectangle.c

Andrea Canciani ranma42 at kemper.freedesktop.org
Tue Oct 12 14:29:45 PDT 2010


 src/cairo-quartz-private.h |    1 
 src/cairo-quartz-surface.c |  145 ++++++++++++++++-----------------------------
 src/cairo-rectangle.c      |   23 +++++++
 src/cairoint.h             |    7 ++
 4 files changed, 84 insertions(+), 92 deletions(-)

New commits:
commit 08c59c6bf3296cc8c70b71a270a3685227c1621b
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 8 16:57:27 2010 +0200

    quartz: Improve gradient quality
    
    Instead of extending the range of the interpolation parameter to make
    sure that pixels exactly on the edge get drawn, we are now asking
    quartz to extend the gradient.

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index ecfae5d..ee2f150 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -918,11 +918,6 @@ CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
     UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
 					  bounds_x1, bounds_y2);
 
-    /* Move t_min and t_max to the nearest usable integer to try to avoid
-       subtle variations due to numerical instability, especially accidentally
-       cutting off a pixel. Extending the gradient repetitions is always safe. */
-    t_min = floor (t_min);
-    t_max = ceil (t_max);
     end->x = start->x + dx*t_max;
     end->y = start->y + dy*t_max;
     start->x = start->x + dx*t_min;
@@ -1054,11 +1049,7 @@ CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
     UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
 					 bounds_x1, bounds_y2);
     /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
-       But for the parameter values we use with Quartz, t_min means radius 0.
-       Since the circles are alway expanding and contain the earlier circles,
-       it's safe to extend t_max/t_temp as much as we want, so round t_temp up
-       to the nearest integer. This may help us give stable results. */
-    t_temp = ceil (t_temp);
+       But for the parameter values we use with Quartz, t_min means radius 0. */
     t_max = t_min + t_temp;
     outer->x = inner->x + t_temp*dx;
     outer->y = inner->y + t_temp*dy;
commit 620c43f50c2c613b8fb334b97d9edcbede0e61bb
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Sep 7 08:49:05 2010 +0200

    quartz: Fix EXTEND_PAD gradients
    
    Make PAD extended gardients more robust, by computing the color
    explicitly like for REPEAT and REFLECT extend modes.
    This removes a hack introducing a small but non-0 negative value
    that ensured that the gradient started with the correct color (but
    not that it ended with the correct one, too).
    
    Fixes linear-gradient-large.

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 935ab34..ecfae5d 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -832,7 +832,7 @@ static const CGFunctionCallbacks gradient_callbacks = {
    function into an array of fixed size, so if the input range is larger
    than needed, the resolution of the gradient will be unnecessarily low.
 */
-static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f };
+static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { 0., 1. };
 
 static CGFunctionRef
 CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
@@ -1346,7 +1346,7 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
     CGPoint start, end;
     CGFunctionRef gradFunc;
     CGColorSpaceRef rgb;
-    bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+    bool extend = abspat->extend != CAIRO_EXTEND_NONE;
 
     assert (lpat->base.n_stops > 0);
 
@@ -1361,16 +1361,13 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
     end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
 		       _cairo_fixed_to_double (lpat->p2.y));
 
-    if (abspat->extend == CAIRO_EXTEND_NONE ||
-        abspat->extend == CAIRO_EXTEND_PAD)
-    {
+    if (!extend)
 	gradFunc = CreateGradientFunction (&lpat->base);
-    } else {
+    else
 	gradFunc = CreateRepeatingLinearGradientFunction (surface,
 						          &lpat->base,
 						          &start, &end,
 						          extents);
-    }
 
     surface->sourceShading = CGShadingCreateAxial (rgb,
 						   start, end,
@@ -1393,7 +1390,7 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
     CGPoint start, end;
     CGFunctionRef gradFunc;
     CGColorSpaceRef rgb;
-    bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+    bool extend = abspat->extend != CAIRO_EXTEND_NONE;
     double c1x = _cairo_fixed_to_double (rpat->c1.x);
     double c1y = _cairo_fixed_to_double (rpat->c1.y);
     double c2x = _cairo_fixed_to_double (rpat->c2.x);
@@ -1412,17 +1409,14 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
     start = CGPointMake (c1x, c1y);
     end = CGPointMake (c2x, c2y);
 
-    if (abspat->extend == CAIRO_EXTEND_NONE ||
-        abspat->extend == CAIRO_EXTEND_PAD)
-    {
+    if (!extend)
 	gradFunc = CreateGradientFunction (&rpat->base);
-    } else {
+    else
 	gradFunc = CreateRepeatingRadialGradientFunction (surface,
 						          &rpat->base,
 						          &start, &r1,
 						          &end, &r2,
 						          extents);
-    }
 
     surface->sourceShading = CGShadingCreateRadial (rgb,
 						    start,
commit 2af3ae92ebe91e39b835eae048addc442533fb67
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Oct 12 22:52:54 2010 +0200

    quartz: Improve gradient consistency
    
    By keeping "virtual extents", quartz surfaces now keep track of
    the extents where they want the gradients to be consistent.
    This works across various API for surface creation and editing:
     - cairo_surface_create_for_rectangle
     - cairo_surface_create_similar + cairo_surface_set_device_offset
     - cairo_push_group/cairo_pop_group
    
    This method does not use clip extents, so it also makes gradient
    rasterization independent of clip/path extents.

diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h
index 38f0d8f..8401a82 100644
--- a/src/cairo-quartz-private.h
+++ b/src/cairo-quartz-private.h
@@ -61,6 +61,7 @@ typedef struct cairo_quartz_surface {
 
     cairo_surface_clipper_t clipper;
     cairo_rectangle_int_t extents;
+    cairo_rectangle_int_t virtual_extents;
 
     /* These are stored while drawing operations are in place, set up
      * by quartz_setup_source() and quartz_finish_source()
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index d8ae886..935ab34 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -890,7 +890,7 @@ static CGFunctionRef
 CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
 				       const cairo_gradient_pattern_t *gpat,
 				       CGPoint *start, CGPoint *end,
-				       cairo_rectangle_int_t *extents)
+				       const cairo_rectangle_int_t *extents)
 {
     cairo_pattern_t *pat;
     cairo_quartz_float_t input_value_range[2];
@@ -900,9 +900,6 @@ CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
     double dy = end->y - start->y;
     double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
 
-    if (!extents) {
-        extents = &surface->extents;
-    }
     bounds_x1 = extents->x;
     bounds_y1 = extents->y;
     bounds_x2 = extents->x + extents->width;
@@ -999,7 +996,7 @@ CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
                                        const cairo_gradient_pattern_t *gpat,
                                        CGPoint *start, double *start_radius,
                                        CGPoint *end, double *end_radius,
-                                       cairo_rectangle_int_t *extents)
+                                       const cairo_rectangle_int_t *extents)
 {
     cairo_pattern_t *pat;
     cairo_quartz_float_t input_value_range[2];
@@ -1014,9 +1011,6 @@ CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
     double dr, dx, dy;
     double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
 
-    if (!extents) {
-        extents = &surface->extents;
-    }
     bounds_x1 = extents->x;
     bounds_y1 = extents->y;
     bounds_x2 = extents->x + extents->width;
@@ -1345,7 +1339,7 @@ clip region).
 static cairo_quartz_action_t
 _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
 				   const cairo_linear_pattern_t *lpat,
-				   cairo_rectangle_int_t *extents)
+				   const cairo_rectangle_int_t *extents)
 {
     const cairo_pattern_t *abspat = &lpat->base.base;
     cairo_matrix_t mat;
@@ -1392,7 +1386,7 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
 static cairo_quartz_action_t
 _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
 				   const cairo_radial_pattern_t *rpat,
-				   cairo_rectangle_int_t *extents)
+				   const cairo_rectangle_int_t *extents)
 {
     const cairo_pattern_t *abspat = &rpat->base.base;
     cairo_matrix_t mat;
@@ -1447,7 +1441,7 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
 static cairo_quartz_action_t
 _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
 			    const cairo_pattern_t *source,
-			    cairo_rectangle_int_t *extents)
+			    const cairo_rectangle_int_t *extents)
 {
     assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
 
@@ -1859,8 +1853,8 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
 				      int width,
 				      int height)
 {
-    /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
-
+    cairo_quartz_surface_t *surface, *similar_quartz;
+    cairo_surface_t *similar;
     cairo_format_t format;
 
     if (content == CAIRO_CONTENT_COLOR_ALPHA)
@@ -1878,7 +1872,15 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
 					       (CAIRO_STATUS_INVALID_SIZE));
     }
 
-    return cairo_quartz_surface_create (format, width, height);
+    similar = cairo_quartz_surface_create (format, width, height);
+    if (unlikely (similar->status))
+	return similar;
+
+    surface = (cairo_quartz_surface_t *) abstract_surface;
+    similar_quartz = (cairo_quartz_surface_t *) similar;
+    similar_quartz->virtual_extents = surface->virtual_extents;
+
+    return similar;
 }
 
 static cairo_status_t
@@ -1984,6 +1986,7 @@ _cairo_quartz_surface_paint_cg (cairo_quartz_surface_t *surface,
 {
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_action_t action;
+    cairo_rectangle_int_t extents;
 
     ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
 
@@ -1998,7 +2001,11 @@ _cairo_quartz_surface_paint_cg (cairo_quartz_surface_t *surface,
     if (unlikely (rv))
 	return rv;
 
-    action = _cairo_quartz_setup_source (surface, source, NULL);
+    extents = surface->virtual_extents;
+    extents.x -= surface->base.device_transform.x0;
+    extents.y -= surface->base.device_transform.y0;
+    _cairo_rectangle_union (&extents, &surface->extents);
+    action = _cairo_quartz_setup_source (surface, source, &extents);
 
     if (action == DO_SOLID || action == DO_PATTERN) {
 	CGContextFillRect (surface->cgContext, CGRectMake (surface->extents.x,
@@ -2024,25 +2031,6 @@ _cairo_quartz_surface_paint_cg (cairo_quartz_surface_t *surface,
     return rv;
 }
 
-static cairo_bool_t
-_cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
-{
-    /* For repeating gradients we need to manually extend the gradient and
-       repeat stops, since Quartz doesn't support repeating gradients natively.
-       We need to minimze the number of repeated stops, and since rasterization
-       depends on the number of repetitions we use (even if some of the
-       repetitions go beyond the extents of the object or outside the clip
-       region), it's important to use the same number of repetitions when
-       rendering an object no matter what the clip region is. So the
-       computation of the repetition count cannot depended on the clip region,
-       and should only depend on the object extents, so we need to compute
-       the object extents for repeating gradients. */
-    return (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
-            source->type == CAIRO_PATTERN_TYPE_RADIAL) &&
-           (source->extend == CAIRO_EXTEND_REPEAT ||
-            source->extend == CAIRO_EXTEND_REFLECT);
-}
-
 static cairo_int_status_t
 _cairo_quartz_surface_paint (void *abstract_surface,
 			     cairo_operator_t op,
@@ -2083,6 +2071,7 @@ _cairo_quartz_surface_fill_cg (cairo_quartz_surface_t *surface,
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_action_t action;
     CGPathRef path_for_unbounded = NULL;
+    cairo_rectangle_int_t extents;
 
     ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
 
@@ -2101,17 +2090,11 @@ _cairo_quartz_surface_fill_cg (cairo_quartz_surface_t *surface,
 
     CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
 
-    if (_cairo_quartz_source_needs_extents (source))
-    {
-        /* We don't need precise extents since these are only used to
-           compute the number of gradient reptitions needed to cover the
-           object. */
-        cairo_rectangle_int_t path_extents;
-        _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
-        action = _cairo_quartz_setup_source (surface, source, &path_extents);
-    } else {
-        action = _cairo_quartz_setup_source (surface, source, NULL);
-    }
+    extents = surface->virtual_extents;
+    extents.x -= surface->base.device_transform.x0;
+    extents.y -= surface->base.device_transform.y0;
+    _cairo_rectangle_union (&extents, &surface->extents);
+    action = _cairo_quartz_setup_source (surface, source, &extents);
 
     _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
 
@@ -2216,6 +2199,7 @@ _cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface,
     cairo_quartz_action_t action;
     CGAffineTransform origCTM, strokeTransform;
     CGPathRef path_for_unbounded = NULL;
+    cairo_rectangle_int_t extents;
 
     ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
 
@@ -2266,15 +2250,11 @@ _cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface,
 
     CGContextSaveGState (surface->cgContext);
 
-
-    if (_cairo_quartz_source_needs_extents (source))
-    {
-        cairo_rectangle_int_t path_extents;
-        _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
-        action = _cairo_quartz_setup_source (surface, source, &path_extents);
-    } else {
-        action = _cairo_quartz_setup_source (surface, source, NULL);
-    }
+    extents = surface->virtual_extents;
+    extents.x -= surface->base.device_transform.x0;
+    extents.y -= surface->base.device_transform.y0;
+    _cairo_rectangle_union (&extents, &surface->extents);
+    action = _cairo_quartz_setup_source (surface, source, &extents);
 
     _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
 
@@ -2386,7 +2366,7 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface,
     CGGlyph *cg_glyphs = &glyphs_static[0];
     CGSize *cg_advances = &cg_advances_static[0];
 
-    cairo_rectangle_int_t glyph_extents;
+    cairo_rectangle_int_t extents;
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_action_t action;
     cairo_quartz_float_t xprev, yprev;
@@ -2415,14 +2395,11 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface,
 
     CGContextSaveGState (surface->cgContext);
 
-    if (_cairo_quartz_source_needs_extents (source) &&
-	!_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
-						  &glyph_extents, NULL))
-    {
-        action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
-    } else {
-        action = _cairo_quartz_setup_source (surface, source, NULL);
-    }
+    extents = surface->virtual_extents;
+    extents.x -= surface->base.device_transform.x0;
+    extents.y -= surface->base.device_transform.y0;
+    _cairo_rectangle_union (&extents, &surface->extents);
+    action = _cairo_quartz_setup_source (surface, source, &extents);
 
     if (action == DO_SOLID || action == DO_PATTERN) {
 	CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
@@ -2662,17 +2639,15 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
 					 const cairo_pattern_t *mask,
 					 cairo_clip_t *clip)
 {
-    int width = surface->extents.width;
-    int height = surface->extents.height;
-
     cairo_surface_t *gradient_surf;
     cairo_surface_pattern_t surface_pattern;
     cairo_int_status_t status;
 
     /* Render the gradient to a surface */
-    gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_A8,
-						 width,
-						 height);
+    gradient_surf = _cairo_quartz_surface_create_similar (surface,
+							  CAIRO_CONTENT_ALPHA,
+							  surface->extents.width,
+							  surface->extents.height);
     status = gradient_surf->status;
     if (unlikely (status))
 	goto BAIL;
@@ -2875,6 +2850,7 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
     surface->extents.x = surface->extents.y = 0;
     surface->extents.width = width;
     surface->extents.height = height;
+    surface->virtual_extents = surface->extents;
 
     if (IS_EMPTY (surface)) {
 	surface->cgContext = NULL;
commit d21b4f31665d409e1bfa6eae82b3c10dd77c4b28
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Oct 12 22:35:36 2010 +0200

    Add _cairo_rectangle_union
    
    Implement _cairo_rectangle_union(), a function to compute a rectangle
    containing two input rectangles.

diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
index cc27180..185c75c 100644
--- a/src/cairo-rectangle.c
+++ b/src/cairo-rectangle.c
@@ -149,6 +149,29 @@ _cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
     }
 }
 
+/* Extends the dst rectangle to also contain src.
+ * If one of the rectangles is empty, the result is undefined
+ */
+void
+_cairo_rectangle_union (cairo_rectangle_int_t *dst,
+			const cairo_rectangle_int_t *src)
+{
+    int x1, y1, x2, y2;
+
+    x1 = MIN (dst->x, src->x);
+    y1 = MIN (dst->y, src->y);
+    /* Beware the unsigned promotion, fortunately we have bits to spare
+     * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
+     */
+    x2 = MAX (dst->x + (int) dst->width,  src->x + (int) src->width);
+    y2 = MAX (dst->y + (int) dst->height, src->y + (int) src->height);
+
+    dst->x = x1;
+    dst->y = y1;
+    dst->width  = x2 - x1;
+    dst->height = y2 - y1;
+}
+
 #define P1x (line->p1.x)
 #define P1y (line->p1.y)
 #define P2x (line->p2.x)
diff --git a/src/cairoint.h b/src/cairoint.h
index 2bb88d3..e3582ec 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -280,6 +280,13 @@ cairo_private cairo_bool_t
 _cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
 			    const cairo_rectangle_int_t *src);
 
+/* Extends the dst rectangle to also contain src.
+ * If one of the rectangles is empty, the result is undefined
+ */
+cairo_private void
+_cairo_rectangle_union (cairo_rectangle_int_t *dst,
+			const cairo_rectangle_int_t *src);
+
 cairo_private cairo_bool_t
 _cairo_box_intersects_line_segment (cairo_box_t *box,
 	                            cairo_line_t *line) cairo_pure;


More information about the cairo-commit mailing list