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.

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

mercurial