summaryrefslogtreecommitdiff
path: root/gfx/cairo/quartz-minimize-gradient-repeat.patch
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/cairo/quartz-minimize-gradient-repeat.patch')
-rw-r--r--gfx/cairo/quartz-minimize-gradient-repeat.patch561
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;