gfx/cairo/quartz-repeating-radial-gradients.patch

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/cairo/quartz-repeating-radial-gradients.patch	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,305 @@
     1.4 +# HG changeset patch
     1.5 +# User Robert O'Callahan <robert@ocallahan.org>
     1.6 +# Date 1249558156 -43200
     1.7 +# Node ID e564f3ab4ea6e3b5dd9c4e9e6042d3a84c229dde
     1.8 +# Parent  6ef9993a30bf2f983c9d64d7441d2e3b6b935de1
     1.9 +Bug 508227. Don't fallback to Quartz for repeating radial gradients. r=jmuizelaar
    1.10 +
    1.11 +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
    1.12 +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
    1.13 ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
    1.14 +@@ -708,20 +708,20 @@ CreateGradientFunction (const cairo_grad
    1.15 + 			     1,
    1.16 + 			     input_value_range,
    1.17 + 			     4,
    1.18 + 			     output_value_ranges,
    1.19 + 			     &callbacks);
    1.20 + }
    1.21 + 
    1.22 + static CGFunctionRef
    1.23 +-CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
    1.24 +-				 const cairo_gradient_pattern_t *gpat,
    1.25 +-				 CGPoint *start, CGPoint *end,
    1.26 +-				 CGAffineTransform matrix)
    1.27 ++CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
    1.28 ++				       const cairo_gradient_pattern_t *gpat,
    1.29 ++				       CGPoint *start, CGPoint *end,
    1.30 ++				       CGAffineTransform matrix)
    1.31 + {
    1.32 +     cairo_pattern_t *pat;
    1.33 +     float input_value_range[2];
    1.34 +     float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
    1.35 +     CGFunctionCallbacks callbacks = {
    1.36 + 	0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
    1.37 +     };
    1.38 + 
    1.39 +@@ -791,16 +791,156 @@ CreateRepeatingGradientFunction (cairo_q
    1.40 +     return CGFunctionCreate (pat,
    1.41 + 			     1,
    1.42 + 			     input_value_range,
    1.43 + 			     4,
    1.44 + 			     output_value_ranges,
    1.45 + 			     &callbacks);
    1.46 + }
    1.47 + 
    1.48 ++static void
    1.49 ++UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
    1.50 ++                                    double dr, double dx, double dy,
    1.51 ++                                    double x, double y)
    1.52 ++{
    1.53 ++    /* Compute a parameter t such that a circle centered at
    1.54 ++       (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
    1.55 ++       point (x,y).
    1.56 ++
    1.57 ++       Let px = x - center->x, py = y - center->y.
    1.58 ++       Parameter values for which t is on the circle are given by
    1.59 ++         (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
    1.60 ++
    1.61 ++       Solving for t using the quadratic formula, and simplifying, we get
    1.62 ++         numerator = dx*px + dy*py +-
    1.63 ++                     sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
    1.64 ++         denominator = dx^2 + dy^2 - dr^2
    1.65 ++         t = numerator/denominator
    1.66 ++
    1.67 ++       In CreateRepeatingRadialGradientFunction we know the outer circle
    1.68 ++       contains the inner circle. Therefore the distance between the circle
    1.69 ++       centers plus the radius of the inner circle is less than the radius of
    1.70 ++       the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
    1.71 ++       Therefore
    1.72 ++         dx^2 + dy^2 < dr^2
    1.73 ++       So the denominator is negative and the larger solution for t is given by
    1.74 ++         numerator = dx*px + dy*py -
    1.75 ++                     sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
    1.76 ++         denominator = dx^2 + dy^2 - dr^2
    1.77 ++         t = numerator/denominator
    1.78 ++       dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
    1.79 ++    */
    1.80 ++    double px = x - center->x;
    1.81 ++    double py = y - center->y;
    1.82 ++    double dx_py_minus_dy_px = dx*py - dy*px;
    1.83 ++    double numerator = dx*px + dy*py -
    1.84 ++        sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
    1.85 ++    double denominator = dx*dx + dy*dy - dr*dr;
    1.86 ++    double t = numerator/denominator;
    1.87 ++
    1.88 ++    if (*max_t < t) {
    1.89 ++        *max_t = t;
    1.90 ++    }
    1.91 ++}
    1.92 ++
    1.93 ++/* This must only be called when one of the circles properly contains the other */
    1.94 ++static CGFunctionRef
    1.95 ++CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
    1.96 ++                                       const cairo_gradient_pattern_t *gpat,
    1.97 ++                                       CGPoint *start, double *start_radius,
    1.98 ++                                       CGPoint *end, double *end_radius)
    1.99 ++{
   1.100 ++    CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
   1.101 ++    CGAffineTransform transform;
   1.102 ++    cairo_pattern_t *pat;
   1.103 ++    float input_value_range[2];
   1.104 ++    float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
   1.105 ++    CGFunctionCallbacks callbacks = {
   1.106 ++        0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
   1.107 ++    };
   1.108 ++    CGPoint *inner;
   1.109 ++    double *inner_radius;
   1.110 ++    CGPoint *outer;
   1.111 ++    double *outer_radius;
   1.112 ++    /* minimum and maximum t-parameter values that will make our gradient
   1.113 ++       cover the clipBox */
   1.114 ++    double t_min, t_max, t_temp;
   1.115 ++    /* outer minus inner */
   1.116 ++    double dr, dx, dy;
   1.117 ++
   1.118 ++    _cairo_quartz_cairo_matrix_to_quartz (&gpat->base.matrix, &transform);
   1.119 ++    /* clip is in cairo device coordinates; get it into cairo user space */
   1.120 ++    clip = CGRectApplyAffineTransform (clip, transform);
   1.121 ++
   1.122 ++    if (*start_radius < *end_radius) {
   1.123 ++        /* end circle contains start circle */
   1.124 ++        inner = start;
   1.125 ++        outer = end;
   1.126 ++        inner_radius = start_radius;
   1.127 ++        outer_radius = end_radius;
   1.128 ++    } else {
   1.129 ++        /* start circle contains end circle */
   1.130 ++        inner = end;
   1.131 ++        outer = start;
   1.132 ++        inner_radius = end_radius;
   1.133 ++        outer_radius = start_radius;
   1.134 ++    }
   1.135 ++
   1.136 ++    dr = *outer_radius - *inner_radius;
   1.137 ++    dx = outer->x - inner->x;
   1.138 ++    dy = outer->y - inner->y;
   1.139 ++
   1.140 ++    t_min = -(*inner_radius/dr);
   1.141 ++    inner->x += t_min*dx;
   1.142 ++    inner->y += t_min*dy;
   1.143 ++    *inner_radius = 0.;
   1.144 ++
   1.145 ++    t_temp = 0.;
   1.146 ++    UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
   1.147 ++                                        clip.origin.x, clip.origin.y);
   1.148 ++    UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
   1.149 ++                                        clip.origin.x + clip.size.width, clip.origin.y);
   1.150 ++    UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
   1.151 ++                                        clip.origin.x + clip.size.width, clip.origin.y + clip.size.height);
   1.152 ++    UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
   1.153 ++                                        clip.origin.x, clip.origin.y + clip.size.height);
   1.154 ++    /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
   1.155 ++       But for the parameter values we use with Quartz, t_min means radius 0.
   1.156 ++       Also, add a small fudge factor to avoid rounding issues. Since the
   1.157 ++       circles are alway expanding and containing the earlier circles, this is
   1.158 ++       OK. */
   1.159 ++    t_temp += 1e-6;
   1.160 ++    t_max = t_min + t_temp;
   1.161 ++    outer->x = inner->x + t_temp*dx;
   1.162 ++    outer->y = inner->y + t_temp*dy;
   1.163 ++    *outer_radius = t_temp*dr;
   1.164 ++
   1.165 ++    /* set the input range for the function -- the function knows how to
   1.166 ++       map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
   1.167 ++    if (*start_radius < *end_radius) {
   1.168 ++        input_value_range[0] = t_min;
   1.169 ++        input_value_range[1] = t_max;
   1.170 ++    } else {
   1.171 ++        input_value_range[0] = -t_max;
   1.172 ++        input_value_range[1] = -t_min;
   1.173 ++    }
   1.174 ++
   1.175 ++    if (_cairo_pattern_create_copy (&pat, &gpat->base))
   1.176 ++  /* quartz doesn't deal very well with malloc failing, so there's
   1.177 ++   * not much point in us trying either */
   1.178 ++  return NULL;
   1.179 ++
   1.180 ++    return CGFunctionCreate (pat,
   1.181 ++           1,
   1.182 ++           input_value_range,
   1.183 ++           4,
   1.184 ++           output_value_ranges,
   1.185 ++           &callbacks);
   1.186 ++}
   1.187 ++
   1.188 + /* Obtain a CGImageRef from a #cairo_surface_t * */
   1.189 + 
   1.190 + static void
   1.191 + DataProviderReleaseCallback (void *info, const void *data, size_t size)
   1.192 + {
   1.193 +     cairo_surface_t *surface = (cairo_surface_t *) info;
   1.194 +     cairo_surface_destroy (surface);
   1.195 + }
   1.196 +@@ -1112,23 +1252,24 @@ _cairo_quartz_setup_linear_source (cairo
   1.197 +     rgb = CGColorSpaceCreateDeviceRGB();
   1.198 + 
   1.199 +     start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
   1.200 + 			 _cairo_fixed_to_double (lpat->p1.y));
   1.201 +     end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
   1.202 + 		       _cairo_fixed_to_double (lpat->p2.y));
   1.203 + 
   1.204 +     if (abspat->extend == CAIRO_EXTEND_NONE ||
   1.205 +-	abspat->extend == CAIRO_EXTEND_PAD)
   1.206 ++        abspat->extend == CAIRO_EXTEND_PAD) 
   1.207 +     {
   1.208 + 	gradFunc = CreateGradientFunction (&lpat->base);
   1.209 +     } else {
   1.210 +-	gradFunc = CreateRepeatingGradientFunction (surface,
   1.211 +-						    &lpat->base,
   1.212 +-						    &start, &end, surface->sourceTransform);
   1.213 ++	gradFunc = CreateRepeatingLinearGradientFunction (surface,
   1.214 ++						          &lpat->base,
   1.215 ++						          &start, &end,
   1.216 ++						          surface->sourceTransform);
   1.217 +     }
   1.218 + 
   1.219 +     surface->sourceShading = CGShadingCreateAxial (rgb,
   1.220 + 						   start, end,
   1.221 + 						   gradFunc,
   1.222 + 						   extend, extend);
   1.223 + 
   1.224 +     CGColorSpaceRelease(rgb);
   1.225 +@@ -1142,52 +1283,68 @@ _cairo_quartz_setup_radial_source (cairo
   1.226 + 				   const cairo_radial_pattern_t *rpat)
   1.227 + {
   1.228 +     const cairo_pattern_t *abspat = &rpat->base.base;
   1.229 +     cairo_matrix_t mat;
   1.230 +     CGPoint start, end;
   1.231 +     CGFunctionRef gradFunc;
   1.232 +     CGColorSpaceRef rgb;
   1.233 +     bool extend = abspat->extend == CAIRO_EXTEND_PAD;
   1.234 ++    double c1x = _cairo_fixed_to_double (rpat->c1.x);
   1.235 ++    double c1y = _cairo_fixed_to_double (rpat->c1.y);
   1.236 ++    double c2x = _cairo_fixed_to_double (rpat->c2.x);
   1.237 ++    double c2y = _cairo_fixed_to_double (rpat->c2.y);
   1.238 ++    double r1 = _cairo_fixed_to_double (rpat->r1);
   1.239 ++    double r2 = _cairo_fixed_to_double (rpat->r2);
   1.240 ++    double dx = c1x - c2x;
   1.241 ++    double dy = c1y - c2y;
   1.242 ++    double centerDistance = sqrt (dx*dx + dy*dy);
   1.243 + 
   1.244 +     if (rpat->base.n_stops == 0) {
   1.245 + 	CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
   1.246 + 	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
   1.247 + 	return DO_SOLID;
   1.248 +     }
   1.249 + 
   1.250 +-    if (abspat->extend == CAIRO_EXTEND_REPEAT ||
   1.251 +-	abspat->extend == CAIRO_EXTEND_REFLECT)
   1.252 +-    {
   1.253 +-	/* I started trying to map these to Quartz, but it's much harder
   1.254 +-	 * then the linear case (I think it would involve doing multiple
   1.255 +-	 * Radial shadings).  So, instead, let's just render an image
   1.256 +-	 * for pixman to draw the shading into, and use that.
   1.257 ++    if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
   1.258 ++        r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
   1.259 ++	/* Quartz handles cases where neither circle contains the other very
   1.260 ++	 * differently from pixman.
   1.261 ++	 * Whatever the correct behaviour is, let's at least have only pixman's
   1.262 ++	 * implementation to worry about.
   1.263 ++	 * Note that this also catches the cases where r1 == r2.
   1.264 + 	 */
   1.265 +-	return _cairo_quartz_setup_fallback_source (surface, &rpat->base.base);
   1.266 ++	return _cairo_quartz_setup_fallback_source (surface, abspat);
   1.267 +     }
   1.268 + 
   1.269 +     mat = abspat->matrix;
   1.270 +     cairo_matrix_invert (&mat);
   1.271 +     _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
   1.272 + 
   1.273 +     rgb = CGColorSpaceCreateDeviceRGB();
   1.274 + 
   1.275 +-    start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x),
   1.276 +-			 _cairo_fixed_to_double (rpat->c1.y));
   1.277 +-    end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
   1.278 +-		       _cairo_fixed_to_double (rpat->c2.y));
   1.279 ++    start = CGPointMake (c1x, c1y);
   1.280 ++    end = CGPointMake (c2x, c2y);
   1.281 + 
   1.282 +-    gradFunc = CreateGradientFunction (&rpat->base);
   1.283 ++    if (abspat->extend == CAIRO_EXTEND_NONE ||
   1.284 ++        abspat->extend == CAIRO_EXTEND_PAD)
   1.285 ++    {
   1.286 ++	gradFunc = CreateGradientFunction (&rpat->base);
   1.287 ++    } else {
   1.288 ++	gradFunc = CreateRepeatingRadialGradientFunction (surface,
   1.289 ++						          &rpat->base,
   1.290 ++						          &start, &r1,
   1.291 ++						          &end, &r2);
   1.292 ++    }
   1.293 + 
   1.294 +     surface->sourceShading = CGShadingCreateRadial (rgb,
   1.295 + 						    start,
   1.296 +-						    _cairo_fixed_to_double (rpat->r1),
   1.297 ++						    r1,
   1.298 + 						    end,
   1.299 +-						    _cairo_fixed_to_double (rpat->r2),
   1.300 ++						    r2,
   1.301 + 						    gradFunc,
   1.302 + 						    extend, extend);
   1.303 + 
   1.304 +     CGColorSpaceRelease(rgb);
   1.305 +     CGFunctionRelease(gradFunc);
   1.306 + 
   1.307 +     return DO_SHADING;
   1.308 + }

mercurial