summaryrefslogtreecommitdiffstats
path: root/gfx/cairo/quartz-repeating-radial-gradients.patch
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/cairo/quartz-repeating-radial-gradients.patch')
-rw-r--r--gfx/cairo/quartz-repeating-radial-gradients.patch305
1 files changed, 305 insertions, 0 deletions
diff --git a/gfx/cairo/quartz-repeating-radial-gradients.patch b/gfx/cairo/quartz-repeating-radial-gradients.patch
new file mode 100644
index 000000000..67fca30d0
--- /dev/null
+++ b/gfx/cairo/quartz-repeating-radial-gradients.patch
@@ -0,0 +1,305 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1249558156 -43200
+# Node ID e564f3ab4ea6e3b5dd9c4e9e6042d3a84c229dde
+# Parent 6ef9993a30bf2f983c9d64d7441d2e3b6b935de1
+Bug 508227. Don't fallback to Quartz for repeating radial gradients. 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
+@@ -708,20 +708,20 @@ CreateGradientFunction (const cairo_grad
+ 1,
+ input_value_range,
+ 4,
+ output_value_ranges,
+ &callbacks);
+ }
+
+ static CGFunctionRef
+-CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
+- const cairo_gradient_pattern_t *gpat,
+- CGPoint *start, CGPoint *end,
+- CGAffineTransform matrix)
++CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
++ const cairo_gradient_pattern_t *gpat,
++ CGPoint *start, CGPoint *end,
++ CGAffineTransform matrix)
+ {
+ cairo_pattern_t *pat;
+ float input_value_range[2];
+ float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+ CGFunctionCallbacks callbacks = {
+ 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+ };
+
+@@ -791,16 +791,156 @@ CreateRepeatingGradientFunction (cairo_q
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+ output_value_ranges,
+ &callbacks);
+ }
+
++static void
++UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
++ double dr, double dx, double dy,
++ double x, double y)
++{
++ /* Compute a parameter t such that a circle centered at
++ (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
++ point (x,y).
++
++ Let px = x - center->x, py = y - center->y.
++ Parameter values for which t is on the circle are given by
++ (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
++
++ Solving for t using the quadratic formula, and simplifying, we get
++ numerator = dx*px + dy*py +-
++ sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
++ denominator = dx^2 + dy^2 - dr^2
++ t = numerator/denominator
++
++ In CreateRepeatingRadialGradientFunction we know the outer circle
++ contains the inner circle. Therefore the distance between the circle
++ centers plus the radius of the inner circle is less than the radius of
++ the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
++ Therefore
++ dx^2 + dy^2 < dr^2
++ So the denominator is negative and the larger solution for t is given by
++ numerator = dx*px + dy*py -
++ sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
++ denominator = dx^2 + dy^2 - dr^2
++ t = numerator/denominator
++ dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
++ */
++ double px = x - center->x;
++ double py = y - center->y;
++ double dx_py_minus_dy_px = dx*py - dy*px;
++ double numerator = dx*px + dy*py -
++ sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
++ double denominator = dx*dx + dy*dy - dr*dr;
++ double t = numerator/denominator;
++
++ if (*max_t < t) {
++ *max_t = t;
++ }
++}
++
++/* 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)
++{
++ CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
++ CGAffineTransform transform;
++ cairo_pattern_t *pat;
++ float input_value_range[2];
++ float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
++ CGFunctionCallbacks callbacks = {
++ 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
++ };
++ 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;
++
++ _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 (*start_radius < *end_radius) {
++ /* end circle contains start circle */
++ inner = start;
++ outer = end;
++ inner_radius = start_radius;
++ outer_radius = end_radius;
++ } else {
++ /* start circle contains end circle */
++ inner = end;
++ outer = start;
++ inner_radius = end_radius;
++ outer_radius = start_radius;
++ }
++
++ dr = *outer_radius - *inner_radius;
++ dx = outer->x - inner->x;
++ dy = outer->y - inner->y;
++
++ 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);
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x + clip.size.width, clip.origin.y);
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x + clip.size.width, clip.origin.y + clip.size.height);
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x, clip.origin.y + clip.size.height);
++ /* 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;
++ 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) {
++ input_value_range[0] = t_min;
++ input_value_range[1] = t_max;
++ } else {
++ input_value_range[0] = -t_max;
++ input_value_range[1] = -t_min;
++ }
++
++ 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,
++ input_value_range,
++ 4,
++ output_value_ranges,
++ &callbacks);
++}
++
+ /* Obtain a CGImageRef from a #cairo_surface_t * */
+
+ static void
+ DataProviderReleaseCallback (void *info, const void *data, size_t size)
+ {
+ cairo_surface_t *surface = (cairo_surface_t *) info;
+ cairo_surface_destroy (surface);
+ }
+@@ -1112,23 +1252,24 @@ _cairo_quartz_setup_linear_source (cairo
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y));
+ 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)
++ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction (&lpat->base);
+ } else {
+- gradFunc = CreateRepeatingGradientFunction (surface,
+- &lpat->base,
+- &start, &end, surface->sourceTransform);
++ gradFunc = CreateRepeatingLinearGradientFunction (surface,
++ &lpat->base,
++ &start, &end,
++ surface->sourceTransform);
+ }
+
+ surface->sourceShading = CGShadingCreateAxial (rgb,
+ start, end,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+@@ -1142,52 +1283,68 @@ _cairo_quartz_setup_radial_source (cairo
+ const cairo_radial_pattern_t *rpat)
+ {
+ 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);
++ double c1y = _cairo_fixed_to_double (rpat->c1.y);
++ double c2x = _cairo_fixed_to_double (rpat->c2.x);
++ double c2y = _cairo_fixed_to_double (rpat->c2.y);
++ double r1 = _cairo_fixed_to_double (rpat->r1);
++ double r2 = _cairo_fixed_to_double (rpat->r2);
++ double dx = c1x - c2x;
++ double dy = c1y - c2y;
++ double centerDistance = sqrt (dx*dx + dy*dy);
+
+ if (rpat->base.n_stops == 0) {
+ CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+ return DO_SOLID;
+ }
+
+- if (abspat->extend == CAIRO_EXTEND_REPEAT ||
+- abspat->extend == CAIRO_EXTEND_REFLECT)
+- {
+- /* I started trying to map these to Quartz, but it's much harder
+- * then the linear case (I think it would involve doing multiple
+- * Radial shadings). So, instead, let's just render an image
+- * for pixman to draw the shading into, and use that.
++ if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
++ r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
++ /* Quartz handles cases where neither circle contains the other very
++ * differently from pixman.
++ * Whatever the correct behaviour is, let's at least have only pixman's
++ * implementation to worry about.
++ * Note that this also catches the cases where r1 == r2.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, &rpat->base.base);
++ 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 (rpat->c1.x),
+- _cairo_fixed_to_double (rpat->c1.y));
+- end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
+- _cairo_fixed_to_double (rpat->c2.y));
++ start = CGPointMake (c1x, c1y);
++ end = CGPointMake (c2x, c2y);
+
+- gradFunc = CreateGradientFunction (&rpat->base);
++ 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);
++ }
+
+ surface->sourceShading = CGShadingCreateRadial (rgb,
+ start,
+- _cairo_fixed_to_double (rpat->r1),
++ r1,
+ end,
+- _cairo_fixed_to_double (rpat->r2),
++ r2,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ return DO_SHADING;
+ }