diff options
Diffstat (limited to 'gfx/cairo/quartz-minimize-gradient-repeat.patch')
-rw-r--r-- | gfx/cairo/quartz-minimize-gradient-repeat.patch | 561 |
1 files changed, 0 insertions, 561 deletions
diff --git a/gfx/cairo/quartz-minimize-gradient-repeat.patch b/gfx/cairo/quartz-minimize-gradient-repeat.patch deleted file mode 100644 index 9782bef11e..0000000000 --- a/gfx/cairo/quartz-minimize-gradient-repeat.patch +++ /dev/null @@ -1,561 +0,0 @@ -# HG changeset patch -# User Robert O'Callahan <robert@ocallahan.org> -# Date 1249558989 -43200 -# Node ID 0bac4c903d2bb1d5c0d5426209001fc2a77cc105 -# Parent 963b9451ad305924738d05d997a640698cd3af91 -Bug 508730. Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops. r=jmuizelaar - -diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c ---- a/gfx/cairo/cairo/src/cairo-quartz-surface.c -+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c -@@ -710,82 +710,100 @@ CreateGradientFunction (const cairo_grad - return CGFunctionCreate (pat, - 1, - input_value_range, - 4, - gradient_output_value_ranges, - &gradient_callbacks); - } - -+static void -+UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start, -+ double dx, double dy, -+ double x, double y) -+{ -+ /* Compute a parameter t such that a line perpendicular to the (dx,dy) -+ vector, passing through (start->x + dx*t, start->y + dy*t), also -+ passes through (x,y). -+ -+ Let px = x - start->x, py = y - start->y. -+ t is given by -+ (px - dx*t)*dx + (py - dy*t)*dy = 0 -+ -+ Solving for t we get -+ numerator = dx*px + dy*py -+ denominator = dx^2 + dy^2 -+ t = numerator/denominator -+ -+ In CreateRepeatingLinearGradientFunction we know the length of (dx,dy) -+ is not zero. (This is checked in _cairo_quartz_setup_linear_source.) -+ */ -+ double px = x - start->x; -+ double py = y - start->y; -+ double numerator = dx*px + dy*py; -+ double denominator = dx*dx + dy*dy; -+ double t = numerator/denominator; -+ -+ if (*min_t > t) { -+ *min_t = t; -+ } -+ if (*max_t < t) { -+ *max_t = t; -+ } -+} -+ - static CGFunctionRef - CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface, - const cairo_gradient_pattern_t *gpat, - CGPoint *start, CGPoint *end, -- CGAffineTransform matrix) -+ cairo_rectangle_int_t *extents) - { - cairo_pattern_t *pat; - float input_value_range[2]; -+ double t_min = 0.; -+ double t_max = 0.; -+ double dx = end->x - start->x; -+ double dy = end->y - start->y; -+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - -- CGPoint mstart, mend; -+ if (!extents) { -+ extents = &surface->extents; -+ } -+ bounds_x1 = extents->x; -+ bounds_y1 = extents->y; -+ bounds_x2 = extents->x + extents->width; -+ bounds_y2 = extents->y + extents->height; -+ _cairo_matrix_transform_bounding_box (&gpat->base.matrix, -+ &bounds_x1, &bounds_y1, -+ &bounds_x2, &bounds_y2, -+ NULL); - -- double dx, dy; -- int x_rep_start = 0, x_rep_end = 0; -- int y_rep_start = 0, y_rep_end = 0; -+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, -+ bounds_x1, bounds_y1); -+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, -+ bounds_x2, bounds_y1); -+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, -+ bounds_x2, bounds_y2); -+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, -+ bounds_x1, bounds_y2); - -- int rep_start, rep_end; -- -- // figure out how many times we'd need to repeat the gradient pattern -- // to cover the whole (transformed) surface area -- mstart = CGPointApplyAffineTransform (*start, matrix); -- mend = CGPointApplyAffineTransform (*end, matrix); -- -- dx = fabs (mend.x - mstart.x); -- dy = fabs (mend.y - mstart.y); -- -- if (dx > 1e-6) { -- x_rep_start = (int) ceil(MIN(mstart.x, mend.x) / dx); -- x_rep_end = (int) ceil((surface->extents.width - MAX(mstart.x, mend.x)) / dx); -- -- if (mend.x < mstart.x) { -- int swap = x_rep_end; -- x_rep_end = x_rep_start; -- x_rep_start = swap; -- } -- } -- -- if (dy > 1e-6) { -- y_rep_start = (int) ceil(MIN(mstart.y, mend.y) / dy); -- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y)) / dy); -- -- if (mend.y < mstart.y) { -- int swap = y_rep_end; -- y_rep_end = y_rep_start; -- y_rep_start = swap; -- } -- } -- -- rep_start = MAX(x_rep_start, y_rep_start); -- rep_end = MAX(x_rep_end, y_rep_end); -- -- // extend the line between start and end by rep_start times from the start -- // and rep_end times from the end -- -- dx = end->x - start->x; -- dy = end->y - start->y; -- -- start->x = start->x - dx * rep_start; -- start->y = start->y - dy * rep_start; -- -- end->x = end->x + dx * rep_end; -- end->y = end->y + dy * rep_end; -+ /* 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; -+ start->y = start->y + dy*t_min; - - // set the input range for the function -- the function knows how to - // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. -- input_value_range[0] = 0.0 - 1.0 * rep_start; -- input_value_range[1] = 1.0 + 1.0 * rep_end; -+ input_value_range[0] = t_min; -+ input_value_range[1] = t_max; - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ - return NULL; - - return CGFunctionCreate (pat, - 1, -@@ -840,35 +858,43 @@ UpdateRadialParameterToIncludePoint(doub - } - } - - /* This must only be called when one of the circles properly contains the other */ - static CGFunctionRef - CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface, - const cairo_gradient_pattern_t *gpat, - CGPoint *start, double *start_radius, -- CGPoint *end, double *end_radius) -+ CGPoint *end, double *end_radius, -+ cairo_rectangle_int_t *extents) - { -- CGRect clip = CGContextGetClipBoundingBox (surface->cgContext); -- CGAffineTransform transform; - cairo_pattern_t *pat; - float input_value_range[2]; - CGPoint *inner; - double *inner_radius; - CGPoint *outer; - double *outer_radius; - /* minimum and maximum t-parameter values that will make our gradient - cover the clipBox */ - double t_min, t_max, t_temp; - /* outer minus inner */ - double dr, dx, dy; -+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - -- _cairo_quartz_cairo_matrix_to_quartz (&gpat->base.matrix, &transform); -- /* clip is in cairo device coordinates; get it into cairo user space */ -- clip = CGRectApplyAffineTransform (clip, transform); -+ if (!extents) { -+ extents = &surface->extents; -+ } -+ bounds_x1 = extents->x; -+ bounds_y1 = extents->y; -+ bounds_x2 = extents->x + extents->width; -+ bounds_y2 = extents->y + extents->height; -+ _cairo_matrix_transform_bounding_box (&gpat->base.matrix, -+ &bounds_x1, &bounds_y1, -+ &bounds_x2, &bounds_y2, -+ NULL); - - if (*start_radius < *end_radius) { - /* end circle contains start circle */ - inner = start; - outer = end; - inner_radius = start_radius; - outer_radius = end_radius; - } else { -@@ -878,36 +904,37 @@ CreateRepeatingRadialGradientFunction (c - inner_radius = end_radius; - outer_radius = start_radius; - } - - dr = *outer_radius - *inner_radius; - dx = outer->x - inner->x; - dy = outer->y - inner->y; - -+ /* We can't round or fudge t_min here, it has to be as accurate as possible. */ - t_min = -(*inner_radius/dr); - inner->x += t_min*dx; - inner->y += t_min*dy; - *inner_radius = 0.; - - t_temp = 0.; - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, -- clip.origin.x, clip.origin.y); -+ bounds_x1, bounds_y1); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, -- clip.origin.x + clip.size.width, clip.origin.y); -+ bounds_x2, bounds_y1); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, -- clip.origin.x + clip.size.width, clip.origin.y + clip.size.height); -+ bounds_x2, bounds_y2); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, -- clip.origin.x, clip.origin.y + clip.size.height); -+ 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. -- Also, add a small fudge factor to avoid rounding issues. Since the -- circles are alway expanding and containing the earlier circles, this is -- OK. */ -- t_temp += 1e-6; -+ 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); - t_max = t_min + t_temp; - outer->x = inner->x + t_temp*dx; - outer->y = inner->y + t_temp*dy; - *outer_radius = t_temp*dr; - - /* set the input range for the function -- the function knows how to - map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */ - if (*start_radius < *end_radius) { -@@ -1218,33 +1245,57 @@ _cairo_quartz_setup_fallback_source (cai - surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h); - surface->sourceImage = img; - surface->sourceImageSurface = fallback; - surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0); - - return DO_IMAGE; - } - -+/* -+Quartz does not support repeating radients. We handle repeating gradients -+by manually extending the gradient and repeating color stops. We need to -+minimize the number of repetitions since Quartz seems to sample our color -+function across the entire range, even if part of that range is not needed -+for the visible area of the gradient, and it samples with some fixed resolution, -+so if the gradient range is too large it samples with very low resolution and -+the gradient is very coarse. CreateRepeatingLinearGradientFunction and -+CreateRepeatingRadialGradientFunction compute the number of repetitions needed -+based on the extents of the object (the clip region cannot be used here since -+we don't want the rasterization of the entire gradient to depend on the -+clip region). -+*/ - static cairo_quartz_action_t - _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, -- const cairo_linear_pattern_t *lpat) -+ const cairo_linear_pattern_t *lpat, -+ cairo_rectangle_int_t *extents) - { - const cairo_pattern_t *abspat = &lpat->base.base; - cairo_matrix_t mat; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb; - bool extend = abspat->extend == CAIRO_EXTEND_PAD; - - if (lpat->base.n_stops == 0) { - CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.); - CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.); - return DO_SOLID; - } - -+ if (lpat->p1.x == lpat->p2.x && -+ lpat->p1.y == lpat->p2.y) { -+ /* Quartz handles cases where the vector has no length very -+ * differently from pixman. -+ * Whatever the correct behaviour is, let's at least have only pixman's -+ * implementation to worry about. -+ */ -+ return _cairo_quartz_setup_fallback_source (surface, abspat); -+ } -+ - mat = abspat->matrix; - cairo_matrix_invert (&mat); - _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform); - - rgb = CGColorSpaceCreateDeviceRGB(); - - start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x), - _cairo_fixed_to_double (lpat->p1.y)); -@@ -1254,33 +1305,34 @@ _cairo_quartz_setup_linear_source (cairo - if (abspat->extend == CAIRO_EXTEND_NONE || - abspat->extend == CAIRO_EXTEND_PAD) - { - gradFunc = CreateGradientFunction (&lpat->base); - } else { - gradFunc = CreateRepeatingLinearGradientFunction (surface, - &lpat->base, - &start, &end, -- surface->sourceTransform); -+ extents); - } - - surface->sourceShading = CGShadingCreateAxial (rgb, - start, end, - gradFunc, - extend, extend); - - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - return DO_SHADING; - } - - static cairo_quartz_action_t - _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, -- const cairo_radial_pattern_t *rpat) -+ const cairo_radial_pattern_t *rpat, -+ cairo_rectangle_int_t *extents) - { - const cairo_pattern_t *abspat = &rpat->base.base; - cairo_matrix_t mat; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb; - bool extend = abspat->extend == CAIRO_EXTEND_PAD; - double c1x = _cairo_fixed_to_double (rpat->c1.x); -@@ -1322,17 +1374,18 @@ _cairo_quartz_setup_radial_source (cairo - if (abspat->extend == CAIRO_EXTEND_NONE || - abspat->extend == CAIRO_EXTEND_PAD) - { - gradFunc = CreateGradientFunction (&rpat->base); - } else { - gradFunc = CreateRepeatingRadialGradientFunction (surface, - &rpat->base, - &start, &r1, -- &end, &r2); -+ &end, &r2, -+ extents); - } - - surface->sourceShading = CGShadingCreateRadial (rgb, - start, - r1, - end, - r2, - gradFunc, -@@ -1341,17 +1394,18 @@ _cairo_quartz_setup_radial_source (cairo - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - return DO_SHADING; - } - - static cairo_quartz_action_t - _cairo_quartz_setup_source (cairo_quartz_surface_t *surface, -- const cairo_pattern_t *source) -+ const cairo_pattern_t *source, -+ cairo_rectangle_int_t *extents) - { - assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern)); - - surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext); - CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter)); - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; -@@ -1367,24 +1421,22 @@ _cairo_quartz_setup_source (cairo_quartz - solid->color.blue, - solid->color.alpha); - - return DO_SOLID; - } - - if (source->type == CAIRO_PATTERN_TYPE_LINEAR) { - const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source; -- return _cairo_quartz_setup_linear_source (surface, lpat); -- -+ return _cairo_quartz_setup_linear_source (surface, lpat, extents); - } - - if (source->type == CAIRO_PATTERN_TYPE_RADIAL) { - const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source; -- return _cairo_quartz_setup_radial_source (surface, rpat); -- -+ return _cairo_quartz_setup_radial_source (surface, rpat, extents); - } - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) - { - const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; - cairo_surface_t *pat_surf = spat->surface; - CGImageRef img; -@@ -1852,17 +1904,17 @@ _cairo_quartz_surface_paint (void *abstr - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); - -- action = _cairo_quartz_setup_source (surface, source); -+ action = _cairo_quartz_setup_source (surface, source, NULL); - - if (action == DO_SOLID || action == DO_PATTERN) { - CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x, - surface->extents.y, - surface->extents.width, - surface->extents.height)); - } else if (action == DO_SHADING) { - CGContextSaveGState (surface->cgContext); -@@ -1886,16 +1938,35 @@ _cairo_quartz_surface_paint (void *abstr - } - - _cairo_quartz_teardown_source (surface, source); - - ND((stderr, "-- paint\n")); - 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_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, -@@ -1926,17 +1997,27 @@ _cairo_quartz_surface_fill (void *abstra - return CAIRO_STATUS_SUCCESS; - } - - CGContextSaveGState (surface->cgContext); - - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); - -- action = _cairo_quartz_setup_source (surface, source); -+ 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); -+ } - - CGContextBeginPath (surface->cgContext); - - stroke.cgContext = surface->cgContext; - stroke.ctm_inverse = NULL; - rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke); - if (rv) - goto BAIL; -@@ -2059,17 +2140,24 @@ _cairo_quartz_surface_stroke (void *abst - - CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes); - if (fdash != sdash) - free (fdash); - } - - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op)); - -- action = _cairo_quartz_setup_source (surface, source); -+ 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); -+ } - - CGContextBeginPath (surface->cgContext); - - stroke.cgContext = surface->cgContext; - stroke.ctm_inverse = ctm_inverse; - rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke); - if (rv) - goto BAIL; -@@ -2180,17 +2268,26 @@ _cairo_quartz_surface_show_glyphs (void - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_STATUS_SUCCESS; - - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) - return CAIRO_INT_STATUS_UNSUPPORTED; - - CGContextSaveGState (surface->cgContext); - -- action = _cairo_quartz_setup_source (surface, source); -+ if (_cairo_quartz_source_needs_extents (source)) -+ { -+ cairo_rectangle_int_t glyph_extents; -+ _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, -+ &glyph_extents); -+ action = _cairo_quartz_setup_source (surface, source, &glyph_extents); -+ } else { -+ action = _cairo_quartz_setup_source (surface, source, NULL); -+ } -+ - if (action == DO_SOLID || action == DO_PATTERN) { - CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill); - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) { - CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip); - isClipping = TRUE; - } else { - if (action != DO_NOTHING) - rv = CAIRO_INT_STATUS_UNSUPPORTED; |