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

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 # HG changeset patch
michael@0 2 # User Robert O'Callahan <robert@ocallahan.org>
michael@0 3 # Date 1249558156 -43200
michael@0 4 # Node ID e564f3ab4ea6e3b5dd9c4e9e6042d3a84c229dde
michael@0 5 # Parent 6ef9993a30bf2f983c9d64d7441d2e3b6b935de1
michael@0 6 Bug 508227. Don't fallback to Quartz for repeating radial gradients. r=jmuizelaar
michael@0 7
michael@0 8 diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
michael@0 9 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
michael@0 10 +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
michael@0 11 @@ -708,20 +708,20 @@ CreateGradientFunction (const cairo_grad
michael@0 12 1,
michael@0 13 input_value_range,
michael@0 14 4,
michael@0 15 output_value_ranges,
michael@0 16 &callbacks);
michael@0 17 }
michael@0 18
michael@0 19 static CGFunctionRef
michael@0 20 -CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
michael@0 21 - const cairo_gradient_pattern_t *gpat,
michael@0 22 - CGPoint *start, CGPoint *end,
michael@0 23 - CGAffineTransform matrix)
michael@0 24 +CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
michael@0 25 + const cairo_gradient_pattern_t *gpat,
michael@0 26 + CGPoint *start, CGPoint *end,
michael@0 27 + CGAffineTransform matrix)
michael@0 28 {
michael@0 29 cairo_pattern_t *pat;
michael@0 30 float input_value_range[2];
michael@0 31 float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
michael@0 32 CGFunctionCallbacks callbacks = {
michael@0 33 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
michael@0 34 };
michael@0 35
michael@0 36 @@ -791,16 +791,156 @@ CreateRepeatingGradientFunction (cairo_q
michael@0 37 return CGFunctionCreate (pat,
michael@0 38 1,
michael@0 39 input_value_range,
michael@0 40 4,
michael@0 41 output_value_ranges,
michael@0 42 &callbacks);
michael@0 43 }
michael@0 44
michael@0 45 +static void
michael@0 46 +UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
michael@0 47 + double dr, double dx, double dy,
michael@0 48 + double x, double y)
michael@0 49 +{
michael@0 50 + /* Compute a parameter t such that a circle centered at
michael@0 51 + (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
michael@0 52 + point (x,y).
michael@0 53 +
michael@0 54 + Let px = x - center->x, py = y - center->y.
michael@0 55 + Parameter values for which t is on the circle are given by
michael@0 56 + (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
michael@0 57 +
michael@0 58 + Solving for t using the quadratic formula, and simplifying, we get
michael@0 59 + numerator = dx*px + dy*py +-
michael@0 60 + sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
michael@0 61 + denominator = dx^2 + dy^2 - dr^2
michael@0 62 + t = numerator/denominator
michael@0 63 +
michael@0 64 + In CreateRepeatingRadialGradientFunction we know the outer circle
michael@0 65 + contains the inner circle. Therefore the distance between the circle
michael@0 66 + centers plus the radius of the inner circle is less than the radius of
michael@0 67 + the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
michael@0 68 + Therefore
michael@0 69 + dx^2 + dy^2 < dr^2
michael@0 70 + So the denominator is negative and the larger solution for t is given by
michael@0 71 + numerator = dx*px + dy*py -
michael@0 72 + sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
michael@0 73 + denominator = dx^2 + dy^2 - dr^2
michael@0 74 + t = numerator/denominator
michael@0 75 + dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
michael@0 76 + */
michael@0 77 + double px = x - center->x;
michael@0 78 + double py = y - center->y;
michael@0 79 + double dx_py_minus_dy_px = dx*py - dy*px;
michael@0 80 + double numerator = dx*px + dy*py -
michael@0 81 + sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
michael@0 82 + double denominator = dx*dx + dy*dy - dr*dr;
michael@0 83 + double t = numerator/denominator;
michael@0 84 +
michael@0 85 + if (*max_t < t) {
michael@0 86 + *max_t = t;
michael@0 87 + }
michael@0 88 +}
michael@0 89 +
michael@0 90 +/* This must only be called when one of the circles properly contains the other */
michael@0 91 +static CGFunctionRef
michael@0 92 +CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
michael@0 93 + const cairo_gradient_pattern_t *gpat,
michael@0 94 + CGPoint *start, double *start_radius,
michael@0 95 + CGPoint *end, double *end_radius)
michael@0 96 +{
michael@0 97 + CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
michael@0 98 + CGAffineTransform transform;
michael@0 99 + cairo_pattern_t *pat;
michael@0 100 + float input_value_range[2];
michael@0 101 + float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
michael@0 102 + CGFunctionCallbacks callbacks = {
michael@0 103 + 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
michael@0 104 + };
michael@0 105 + CGPoint *inner;
michael@0 106 + double *inner_radius;
michael@0 107 + CGPoint *outer;
michael@0 108 + double *outer_radius;
michael@0 109 + /* minimum and maximum t-parameter values that will make our gradient
michael@0 110 + cover the clipBox */
michael@0 111 + double t_min, t_max, t_temp;
michael@0 112 + /* outer minus inner */
michael@0 113 + double dr, dx, dy;
michael@0 114 +
michael@0 115 + _cairo_quartz_cairo_matrix_to_quartz (&gpat->base.matrix, &transform);
michael@0 116 + /* clip is in cairo device coordinates; get it into cairo user space */
michael@0 117 + clip = CGRectApplyAffineTransform (clip, transform);
michael@0 118 +
michael@0 119 + if (*start_radius < *end_radius) {
michael@0 120 + /* end circle contains start circle */
michael@0 121 + inner = start;
michael@0 122 + outer = end;
michael@0 123 + inner_radius = start_radius;
michael@0 124 + outer_radius = end_radius;
michael@0 125 + } else {
michael@0 126 + /* start circle contains end circle */
michael@0 127 + inner = end;
michael@0 128 + outer = start;
michael@0 129 + inner_radius = end_radius;
michael@0 130 + outer_radius = start_radius;
michael@0 131 + }
michael@0 132 +
michael@0 133 + dr = *outer_radius - *inner_radius;
michael@0 134 + dx = outer->x - inner->x;
michael@0 135 + dy = outer->y - inner->y;
michael@0 136 +
michael@0 137 + t_min = -(*inner_radius/dr);
michael@0 138 + inner->x += t_min*dx;
michael@0 139 + inner->y += t_min*dy;
michael@0 140 + *inner_radius = 0.;
michael@0 141 +
michael@0 142 + t_temp = 0.;
michael@0 143 + UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 144 + clip.origin.x, clip.origin.y);
michael@0 145 + UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 146 + clip.origin.x + clip.size.width, clip.origin.y);
michael@0 147 + UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 148 + clip.origin.x + clip.size.width, clip.origin.y + clip.size.height);
michael@0 149 + UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 150 + clip.origin.x, clip.origin.y + clip.size.height);
michael@0 151 + /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
michael@0 152 + But for the parameter values we use with Quartz, t_min means radius 0.
michael@0 153 + Also, add a small fudge factor to avoid rounding issues. Since the
michael@0 154 + circles are alway expanding and containing the earlier circles, this is
michael@0 155 + OK. */
michael@0 156 + t_temp += 1e-6;
michael@0 157 + t_max = t_min + t_temp;
michael@0 158 + outer->x = inner->x + t_temp*dx;
michael@0 159 + outer->y = inner->y + t_temp*dy;
michael@0 160 + *outer_radius = t_temp*dr;
michael@0 161 +
michael@0 162 + /* set the input range for the function -- the function knows how to
michael@0 163 + map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
michael@0 164 + if (*start_radius < *end_radius) {
michael@0 165 + input_value_range[0] = t_min;
michael@0 166 + input_value_range[1] = t_max;
michael@0 167 + } else {
michael@0 168 + input_value_range[0] = -t_max;
michael@0 169 + input_value_range[1] = -t_min;
michael@0 170 + }
michael@0 171 +
michael@0 172 + if (_cairo_pattern_create_copy (&pat, &gpat->base))
michael@0 173 + /* quartz doesn't deal very well with malloc failing, so there's
michael@0 174 + * not much point in us trying either */
michael@0 175 + return NULL;
michael@0 176 +
michael@0 177 + return CGFunctionCreate (pat,
michael@0 178 + 1,
michael@0 179 + input_value_range,
michael@0 180 + 4,
michael@0 181 + output_value_ranges,
michael@0 182 + &callbacks);
michael@0 183 +}
michael@0 184 +
michael@0 185 /* Obtain a CGImageRef from a #cairo_surface_t * */
michael@0 186
michael@0 187 static void
michael@0 188 DataProviderReleaseCallback (void *info, const void *data, size_t size)
michael@0 189 {
michael@0 190 cairo_surface_t *surface = (cairo_surface_t *) info;
michael@0 191 cairo_surface_destroy (surface);
michael@0 192 }
michael@0 193 @@ -1112,23 +1252,24 @@ _cairo_quartz_setup_linear_source (cairo
michael@0 194 rgb = CGColorSpaceCreateDeviceRGB();
michael@0 195
michael@0 196 start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
michael@0 197 _cairo_fixed_to_double (lpat->p1.y));
michael@0 198 end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
michael@0 199 _cairo_fixed_to_double (lpat->p2.y));
michael@0 200
michael@0 201 if (abspat->extend == CAIRO_EXTEND_NONE ||
michael@0 202 - abspat->extend == CAIRO_EXTEND_PAD)
michael@0 203 + abspat->extend == CAIRO_EXTEND_PAD)
michael@0 204 {
michael@0 205 gradFunc = CreateGradientFunction (&lpat->base);
michael@0 206 } else {
michael@0 207 - gradFunc = CreateRepeatingGradientFunction (surface,
michael@0 208 - &lpat->base,
michael@0 209 - &start, &end, surface->sourceTransform);
michael@0 210 + gradFunc = CreateRepeatingLinearGradientFunction (surface,
michael@0 211 + &lpat->base,
michael@0 212 + &start, &end,
michael@0 213 + surface->sourceTransform);
michael@0 214 }
michael@0 215
michael@0 216 surface->sourceShading = CGShadingCreateAxial (rgb,
michael@0 217 start, end,
michael@0 218 gradFunc,
michael@0 219 extend, extend);
michael@0 220
michael@0 221 CGColorSpaceRelease(rgb);
michael@0 222 @@ -1142,52 +1283,68 @@ _cairo_quartz_setup_radial_source (cairo
michael@0 223 const cairo_radial_pattern_t *rpat)
michael@0 224 {
michael@0 225 const cairo_pattern_t *abspat = &rpat->base.base;
michael@0 226 cairo_matrix_t mat;
michael@0 227 CGPoint start, end;
michael@0 228 CGFunctionRef gradFunc;
michael@0 229 CGColorSpaceRef rgb;
michael@0 230 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
michael@0 231 + double c1x = _cairo_fixed_to_double (rpat->c1.x);
michael@0 232 + double c1y = _cairo_fixed_to_double (rpat->c1.y);
michael@0 233 + double c2x = _cairo_fixed_to_double (rpat->c2.x);
michael@0 234 + double c2y = _cairo_fixed_to_double (rpat->c2.y);
michael@0 235 + double r1 = _cairo_fixed_to_double (rpat->r1);
michael@0 236 + double r2 = _cairo_fixed_to_double (rpat->r2);
michael@0 237 + double dx = c1x - c2x;
michael@0 238 + double dy = c1y - c2y;
michael@0 239 + double centerDistance = sqrt (dx*dx + dy*dy);
michael@0 240
michael@0 241 if (rpat->base.n_stops == 0) {
michael@0 242 CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
michael@0 243 CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
michael@0 244 return DO_SOLID;
michael@0 245 }
michael@0 246
michael@0 247 - if (abspat->extend == CAIRO_EXTEND_REPEAT ||
michael@0 248 - abspat->extend == CAIRO_EXTEND_REFLECT)
michael@0 249 - {
michael@0 250 - /* I started trying to map these to Quartz, but it's much harder
michael@0 251 - * then the linear case (I think it would involve doing multiple
michael@0 252 - * Radial shadings). So, instead, let's just render an image
michael@0 253 - * for pixman to draw the shading into, and use that.
michael@0 254 + if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
michael@0 255 + r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
michael@0 256 + /* Quartz handles cases where neither circle contains the other very
michael@0 257 + * differently from pixman.
michael@0 258 + * Whatever the correct behaviour is, let's at least have only pixman's
michael@0 259 + * implementation to worry about.
michael@0 260 + * Note that this also catches the cases where r1 == r2.
michael@0 261 */
michael@0 262 - return _cairo_quartz_setup_fallback_source (surface, &rpat->base.base);
michael@0 263 + return _cairo_quartz_setup_fallback_source (surface, abspat);
michael@0 264 }
michael@0 265
michael@0 266 mat = abspat->matrix;
michael@0 267 cairo_matrix_invert (&mat);
michael@0 268 _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
michael@0 269
michael@0 270 rgb = CGColorSpaceCreateDeviceRGB();
michael@0 271
michael@0 272 - start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x),
michael@0 273 - _cairo_fixed_to_double (rpat->c1.y));
michael@0 274 - end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
michael@0 275 - _cairo_fixed_to_double (rpat->c2.y));
michael@0 276 + start = CGPointMake (c1x, c1y);
michael@0 277 + end = CGPointMake (c2x, c2y);
michael@0 278
michael@0 279 - gradFunc = CreateGradientFunction (&rpat->base);
michael@0 280 + if (abspat->extend == CAIRO_EXTEND_NONE ||
michael@0 281 + abspat->extend == CAIRO_EXTEND_PAD)
michael@0 282 + {
michael@0 283 + gradFunc = CreateGradientFunction (&rpat->base);
michael@0 284 + } else {
michael@0 285 + gradFunc = CreateRepeatingRadialGradientFunction (surface,
michael@0 286 + &rpat->base,
michael@0 287 + &start, &r1,
michael@0 288 + &end, &r2);
michael@0 289 + }
michael@0 290
michael@0 291 surface->sourceShading = CGShadingCreateRadial (rgb,
michael@0 292 start,
michael@0 293 - _cairo_fixed_to_double (rpat->r1),
michael@0 294 + r1,
michael@0 295 end,
michael@0 296 - _cairo_fixed_to_double (rpat->r2),
michael@0 297 + r2,
michael@0 298 gradFunc,
michael@0 299 extend, extend);
michael@0 300
michael@0 301 CGColorSpaceRelease(rgb);
michael@0 302 CGFunctionRelease(gradFunc);
michael@0 303
michael@0 304 return DO_SHADING;
michael@0 305 }

mercurial