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 + }