Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/2d/Path.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "2D.h"
8
#include "PathAnalysis.h"
9
#include "PathHelpers.h"
10
11
namespace mozilla {
12
namespace gfx {
13
14
0
static double CubicRoot(double aValue) {
15
0
  if (aValue < 0.0) {
16
0
    return -CubicRoot(-aValue);
17
0
  }
18
0
  else {
19
0
    return pow(aValue, 1.0 / 3.0);
20
0
  }
21
0
}
22
23
struct PointD : public BasePoint<double, PointD> {
24
  typedef BasePoint<double, PointD> Super;
25
26
0
  PointD() : Super() {}
27
0
  PointD(double aX, double aY) : Super(aX, aY) {}
28
0
  MOZ_IMPLICIT PointD(const Point& aPoint) : Super(aPoint.x, aPoint.y) {}
29
30
0
  Point ToPoint() const {
31
0
    return Point(static_cast<Float>(x), static_cast<Float>(y));
32
0
  }
33
};
34
35
struct BezierControlPoints
36
{
37
0
  BezierControlPoints() {}
38
  BezierControlPoints(const PointD &aCP1, const PointD &aCP2,
39
                      const PointD &aCP3, const PointD &aCP4)
40
    : mCP1(aCP1), mCP2(aCP2), mCP3(aCP3), mCP4(aCP4)
41
0
  {
42
0
  }
43
44
  PointD mCP1, mCP2, mCP3, mCP4;
45
};
46
47
void
48
FlattenBezier(const BezierControlPoints &aPoints,
49
              PathSink *aSink, double aTolerance);
50
51
52
Path::Path()
53
0
{
54
0
}
55
56
Path::~Path()
57
0
{
58
0
}
59
60
Float
61
Path::ComputeLength()
62
0
{
63
0
  EnsureFlattenedPath();
64
0
  return mFlattenedPath->ComputeLength();
65
0
}
66
67
Point
68
Path::ComputePointAtLength(Float aLength, Point* aTangent)
69
0
{
70
0
  EnsureFlattenedPath();
71
0
  return mFlattenedPath->ComputePointAtLength(aLength, aTangent);
72
0
}
73
74
void
75
Path::EnsureFlattenedPath()
76
0
{
77
0
  if (!mFlattenedPath) {
78
0
    mFlattenedPath = new FlattenedPath();
79
0
    StreamToSink(mFlattenedPath);
80
0
  }
81
0
}
82
83
// This is the maximum deviation we allow (with an additional ~20% margin of
84
// error) of the approximation from the actual Bezier curve.
85
const Float kFlatteningTolerance = 0.0001f;
86
87
void
88
FlattenedPath::MoveTo(const Point &aPoint)
89
0
{
90
0
  MOZ_ASSERT(!mCalculatedLength);
91
0
  FlatPathOp op;
92
0
  op.mType = FlatPathOp::OP_MOVETO;
93
0
  op.mPoint = aPoint;
94
0
  mPathOps.push_back(op);
95
0
96
0
  mLastMove = aPoint;
97
0
}
98
99
void
100
FlattenedPath::LineTo(const Point &aPoint)
101
0
{
102
0
  MOZ_ASSERT(!mCalculatedLength);
103
0
  FlatPathOp op;
104
0
  op.mType = FlatPathOp::OP_LINETO;
105
0
  op.mPoint = aPoint;
106
0
  mPathOps.push_back(op);
107
0
}
108
109
void
110
FlattenedPath::BezierTo(const Point &aCP1,
111
                        const Point &aCP2,
112
                        const Point &aCP3)
113
0
{
114
0
  MOZ_ASSERT(!mCalculatedLength);
115
0
  FlattenBezier(BezierControlPoints(CurrentPoint(), aCP1, aCP2, aCP3), this, kFlatteningTolerance);
116
0
}
117
118
void
119
FlattenedPath::QuadraticBezierTo(const Point &aCP1,
120
                                 const Point &aCP2)
121
0
{
122
0
  MOZ_ASSERT(!mCalculatedLength);
123
0
  // We need to elevate the degree of this quadratic B�zier to cubic, so we're
124
0
  // going to add an intermediate control point, and recompute control point 1.
125
0
  // The first and last control points remain the same.
126
0
  // This formula can be found on http://fontforge.sourceforge.net/bezier.html
127
0
  Point CP0 = CurrentPoint();
128
0
  Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
129
0
  Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
130
0
  Point CP3 = aCP2;
131
0
132
0
  BezierTo(CP1, CP2, CP3);
133
0
}
134
135
void
136
FlattenedPath::Close()
137
0
{
138
0
  MOZ_ASSERT(!mCalculatedLength);
139
0
  LineTo(mLastMove);
140
0
}
141
142
void
143
FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
144
                   float aEndAngle, bool aAntiClockwise)
145
0
{
146
0
  ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
147
0
}
148
149
Float
150
FlattenedPath::ComputeLength()
151
0
{
152
0
  if (!mCalculatedLength) {
153
0
    Point currentPoint;
154
0
155
0
    for (uint32_t i = 0; i < mPathOps.size(); i++) {
156
0
      if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
157
0
        currentPoint = mPathOps[i].mPoint;
158
0
      } else {
159
0
        mCachedLength += Distance(currentPoint, mPathOps[i].mPoint);
160
0
        currentPoint = mPathOps[i].mPoint;
161
0
      }
162
0
    }
163
0
164
0
    mCalculatedLength =  true;
165
0
  }
166
0
167
0
  return mCachedLength;
168
0
}
169
170
Point
171
FlattenedPath::ComputePointAtLength(Float aLength, Point *aTangent)
172
0
{
173
0
  // We track the last point that -wasn't- in the same place as the current
174
0
  // point so if we pass the edge of the path with a bunch of zero length
175
0
  // paths we still get the correct tangent vector.
176
0
  Point lastPointSinceMove;
177
0
  Point currentPoint;
178
0
  for (uint32_t i = 0; i < mPathOps.size(); i++) {
179
0
    if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
180
0
      if (Distance(currentPoint, mPathOps[i].mPoint)) {
181
0
        lastPointSinceMove = currentPoint;
182
0
      }
183
0
      currentPoint = mPathOps[i].mPoint;
184
0
    } else {
185
0
      Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint);
186
0
187
0
      if (segmentLength) {
188
0
        lastPointSinceMove = currentPoint;
189
0
        if (segmentLength > aLength) {
190
0
          Point currentVector = mPathOps[i].mPoint - currentPoint;
191
0
          Point tangent = currentVector / segmentLength;
192
0
          if (aTangent) {
193
0
            *aTangent = tangent;
194
0
          }
195
0
          return currentPoint + tangent * aLength;
196
0
        }
197
0
      }
198
0
199
0
      aLength -= segmentLength;
200
0
      currentPoint = mPathOps[i].mPoint;
201
0
    }
202
0
  }
203
0
204
0
  Point currentVector = currentPoint - lastPointSinceMove;
205
0
  if (aTangent) {
206
0
    if (hypotf(currentVector.x, currentVector.y)) {
207
0
      *aTangent = currentVector / hypotf(currentVector.x, currentVector.y);
208
0
    } else {
209
0
      *aTangent = Point();
210
0
    }
211
0
  }
212
0
  return currentPoint;
213
0
}
214
215
// This function explicitly permits aControlPoints to refer to the same object
216
// as either of the other arguments.
217
static void 
218
SplitBezier(const BezierControlPoints &aControlPoints,
219
            BezierControlPoints *aFirstSegmentControlPoints,
220
            BezierControlPoints *aSecondSegmentControlPoints,
221
            double t)
222
0
{
223
0
  MOZ_ASSERT(aSecondSegmentControlPoints);
224
0
  
225
0
  *aSecondSegmentControlPoints = aControlPoints;
226
0
227
0
  PointD cp1a = aControlPoints.mCP1 + (aControlPoints.mCP2 - aControlPoints.mCP1) * t;
228
0
  PointD cp2a = aControlPoints.mCP2 + (aControlPoints.mCP3 - aControlPoints.mCP2) * t;
229
0
  PointD cp1aa = cp1a + (cp2a - cp1a) * t;
230
0
  PointD cp3a = aControlPoints.mCP3 + (aControlPoints.mCP4 - aControlPoints.mCP3) * t;
231
0
  PointD cp2aa = cp2a + (cp3a - cp2a) * t;
232
0
  PointD cp1aaa = cp1aa + (cp2aa - cp1aa) * t;
233
0
  aSecondSegmentControlPoints->mCP4 = aControlPoints.mCP4;
234
0
235
0
  if(aFirstSegmentControlPoints) {
236
0
    aFirstSegmentControlPoints->mCP1 = aControlPoints.mCP1;
237
0
    aFirstSegmentControlPoints->mCP2 = cp1a;
238
0
    aFirstSegmentControlPoints->mCP3 = cp1aa;
239
0
    aFirstSegmentControlPoints->mCP4 = cp1aaa;
240
0
  }
241
0
  aSecondSegmentControlPoints->mCP1 = cp1aaa;
242
0
  aSecondSegmentControlPoints->mCP2 = cp2aa;
243
0
  aSecondSegmentControlPoints->mCP3 = cp3a;
244
0
}
245
246
static void
247
FlattenBezierCurveSegment(const BezierControlPoints &aControlPoints,
248
                          PathSink *aSink,
249
                          double aTolerance)
250
0
{
251
0
  /* The algorithm implemented here is based on:
252
0
   * http://cis.usouthal.edu/~hain/general/Publications/Bezier/Bezier%20Offset%20Curves.pdf
253
0
   *
254
0
   * The basic premise is that for a small t the third order term in the
255
0
   * equation of a cubic bezier curve is insignificantly small. This can
256
0
   * then be approximated by a quadratic equation for which the maximum
257
0
   * difference from a linear approximation can be much more easily determined.
258
0
   */
259
0
  BezierControlPoints currentCP = aControlPoints;
260
0
261
0
  double t = 0;
262
0
  double currentTolerance = aTolerance;
263
0
  while (t < 1.0) {
264
0
    PointD cp21 = currentCP.mCP2 - currentCP.mCP1;
265
0
    PointD cp31 = currentCP.mCP3 - currentCP.mCP1;
266
0
267
0
    /* To remove divisions and check for divide-by-zero, this is optimized from:
268
0
     * Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y);
269
0
     * t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3))));
270
0
     */
271
0
    double cp21x31 = cp31.x * cp21.y - cp31.y * cp21.x;
272
0
    double h = hypot(cp21.x, cp21.y);
273
0
    if (cp21x31 * h == 0) {
274
0
      break;
275
0
    }
276
0
277
0
    double s3inv = h / cp21x31;
278
0
    t = 2 * sqrt(currentTolerance * std::abs(s3inv) / 3.);
279
0
    currentTolerance *= 1 + aTolerance;
280
0
    // Increase tolerance every iteration to prevent this loop from executing
281
0
    // too many times. This approximates the length of large curves more
282
0
    // roughly. In practice, aTolerance is the constant kFlatteningTolerance
283
0
    // which has value 0.0001. With this value, it takes 6,932 splits to double
284
0
    // currentTolerance (to 0.0002) and 23,028 splits to increase
285
0
    // currentTolerance by an order of magnitude (to 0.001).
286
0
    if (t >= 1.0) {
287
0
      break;
288
0
    }
289
0
290
0
    SplitBezier(currentCP, nullptr, &currentCP, t);
291
0
292
0
    aSink->LineTo(currentCP.mCP1.ToPoint());
293
0
  }
294
0
295
0
  aSink->LineTo(currentCP.mCP4.ToPoint());
296
0
}
297
298
static inline void
299
FindInflectionApproximationRange(BezierControlPoints aControlPoints,
300
                                 double *aMin, double *aMax, double aT,
301
                                 double aTolerance)
302
0
{
303
0
    SplitBezier(aControlPoints, nullptr, &aControlPoints, aT);
304
0
305
0
    PointD cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
306
0
    PointD cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
307
0
308
0
    if (cp21.x == 0. && cp21.y == 0.) {
309
0
      cp21 = aControlPoints.mCP3 - aControlPoints.mCP1;
310
0
    }
311
0
312
0
    if (cp21.x == 0. && cp21.y == 0.) {
313
0
      // In this case s3 becomes lim[n->0] (cp41.x * n) / n - (cp41.y * n) / n = cp41.x - cp41.y.
314
0
315
0
      // Use the absolute value so that Min and Max will correspond with the
316
0
      // minimum and maximum of the range.
317
0
      *aMin = aT - CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
318
0
      *aMax = aT + CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
319
0
      return;
320
0
    }
321
0
322
0
    double s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypot(cp21.x, cp21.y);
323
0
324
0
    if (s3 == 0) {
325
0
      // This means within the precision we have it can be approximated
326
0
      // infinitely by a linear segment. Deal with this by specifying the
327
0
      // approximation range as extending beyond the entire curve.
328
0
      *aMin = -1.0;
329
0
      *aMax = 2.0;
330
0
      return;
331
0
    }
332
0
333
0
    double tf = CubicRoot(std::abs(aTolerance / s3));
334
0
335
0
    *aMin = aT - tf * (1 - aT);
336
0
    *aMax = aT + tf * (1 - aT);
337
0
}
338
339
/* Find the inflection points of a bezier curve. Will return false if the
340
 * curve is degenerate in such a way that it is best approximated by a straight
341
 * line.
342
 *
343
 * The below algorithm was written by Jeff Muizelaar <jmuizelaar@mozilla.com>, explanation follows:
344
 *
345
 * The lower inflection point is returned in aT1, the higher one in aT2. In the
346
 * case of a single inflection point this will be in aT1.
347
 *
348
 * The method is inspired by the algorithm in "analysis of in?ection points for planar cubic bezier curve"
349
 *
350
 * Here are some differences between this algorithm and versions discussed elsewhere in the literature:
351
 *
352
 * zhang et. al compute a0, d0 and e0 incrementally using the follow formula:
353
 *
354
 * Point a0 = CP2 - CP1
355
 * Point a1 = CP3 - CP2
356
 * Point a2 = CP4 - CP1
357
 *
358
 * Point d0 = a1 - a0
359
 * Point d1 = a2 - a1
360
 
361
 * Point e0 = d1 - d0
362
 *
363
 * this avoids any multiplications and may or may not be faster than the approach take below.
364
 *
365
 * "fast, precise flattening of cubic bezier path and ofset curves" by hain et. al
366
 * Point a = CP1 + 3 * CP2 - 3 * CP3 + CP4
367
 * Point b = 3 * CP1 - 6 * CP2 + 3 * CP3
368
 * Point c = -3 * CP1 + 3 * CP2
369
 * Point d = CP1
370
 * the a, b, c, d can be expressed in terms of a0, d0 and e0 defined above as:
371
 * c = 3 * a0
372
 * b = 3 * d0
373
 * a = e0
374
 *
375
 *
376
 * a = 3a = a.y * b.x - a.x * b.y
377
 * b = 3b = a.y * c.x - a.x * c.y
378
 * c = 9c = b.y * c.x - b.x * c.y
379
 *
380
 * The additional multiples of 3 cancel each other out as show below:
381
 *
382
 * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
383
 * x = (-3 * b + sqrt(3 * b * 3 * b - 4 * a * 3 * 9 * c / 3)) / (2 * 3 * a)
384
 * x = 3 * (-b + sqrt(b * b - 4 * a * c)) / (2 * 3 * a)
385
 * x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
386
 *
387
 * I haven't looked into whether the formulation of the quadratic formula in
388
 * hain has any numerical advantages over the one used below.
389
 */
390
static inline void
391
FindInflectionPoints(const BezierControlPoints &aControlPoints,
392
                     double *aT1, double *aT2, uint32_t *aCount)
393
0
{
394
0
  // Find inflection points.
395
0
  // See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
396
0
  // of this approach.
397
0
  PointD A = aControlPoints.mCP2 - aControlPoints.mCP1;
398
0
  PointD B = aControlPoints.mCP3 - (aControlPoints.mCP2 * 2) + aControlPoints.mCP1;
399
0
  PointD C = aControlPoints.mCP4 - (aControlPoints.mCP3 * 3) + (aControlPoints.mCP2 * 3) - aControlPoints.mCP1;
400
0
401
0
  double a = B.x * C.y - B.y * C.x;
402
0
  double b = A.x * C.y - A.y * C.x;
403
0
  double c = A.x * B.y - A.y * B.x;
404
0
405
0
  if (a == 0) {
406
0
    // Not a quadratic equation.
407
0
    if (b == 0) {
408
0
      // Instead of a linear acceleration change we have a constant
409
0
      // acceleration change. This means the equation has no solution
410
0
      // and there are no inflection points, unless the constant is 0.
411
0
      // In that case the curve is a straight line, essentially that means
412
0
      // the easiest way to deal with is is by saying there's an inflection
413
0
      // point at t == 0. The inflection point approximation range found will
414
0
      // automatically extend into infinity.
415
0
      if (c == 0) {
416
0
        *aCount = 1;
417
0
        *aT1 = 0;
418
0
        return;
419
0
      }
420
0
      *aCount = 0;
421
0
      return;
422
0
    }
423
0
    *aT1 = -c / b;
424
0
    *aCount = 1;
425
0
    return;
426
0
  } else {
427
0
    double discriminant = b * b - 4 * a * c;
428
0
429
0
    if (discriminant < 0) {
430
0
      // No inflection points.
431
0
      *aCount = 0;
432
0
    } else if (discriminant == 0) {
433
0
      *aCount = 1;
434
0
      *aT1 = -b / (2 * a);
435
0
    } else {
436
0
      /* Use the following formula for computing the roots:
437
0
       *
438
0
       * q = -1/2 * (b + sign(b) * sqrt(b^2 - 4ac))
439
0
       * t1 = q / a
440
0
       * t2 = c / q
441
0
       */
442
0
      double q = sqrt(discriminant);
443
0
      if (b < 0) {
444
0
        q = b - q;
445
0
      } else {
446
0
        q = b + q;
447
0
      }
448
0
      q *= -1./2;
449
0
450
0
      *aT1 = q / a;
451
0
      *aT2 = c / q;
452
0
      if (*aT1 > *aT2) {
453
0
        std::swap(*aT1, *aT2);
454
0
      }
455
0
      *aCount = 2;
456
0
    }
457
0
  }
458
0
}
459
460
void
461
FlattenBezier(const BezierControlPoints &aControlPoints,
462
              PathSink *aSink, double aTolerance)
463
0
{
464
0
  double t1;
465
0
  double t2;
466
0
  uint32_t count;
467
0
468
0
  FindInflectionPoints(aControlPoints, &t1, &t2, &count);
469
0
470
0
  // Check that at least one of the inflection points is inside [0..1]
471
0
  if (count == 0 || ((t1 < 0.0 || t1 >= 1.0) && (count == 1 || (t2 < 0.0 || t2 >= 1.0))) ) {
472
0
    FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance);
473
0
    return;
474
0
  }
475
0
476
0
  double t1min = t1, t1max = t1, t2min = t2, t2max = t2;
477
0
478
0
  BezierControlPoints remainingCP = aControlPoints;
479
0
480
0
  // For both inflection points, calulate the range where they can be linearly
481
0
  // approximated if they are positioned within [0,1]
482
0
  if (count > 0 && t1 >= 0 && t1 < 1.0) {
483
0
    FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance);
484
0
  }
485
0
  if (count > 1 && t2 >= 0 && t2 < 1.0) {
486
0
    FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
487
0
  }
488
0
  BezierControlPoints nextCPs = aControlPoints;
489
0
  BezierControlPoints prevCPs;
490
0
491
0
  // Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
492
0
  // segments.
493
0
  if (count == 1 && t1min <= 0 && t1max >= 1.0) {
494
0
    // The whole range can be approximated by a line segment.
495
0
    aSink->LineTo(aControlPoints.mCP4.ToPoint());
496
0
    return;
497
0
  }
498
0
499
0
  if (t1min > 0) {
500
0
    // Flatten the Bezier up until the first inflection point's approximation
501
0
    // point.
502
0
    SplitBezier(aControlPoints, &prevCPs,
503
0
                &remainingCP, t1min);
504
0
    FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
505
0
  }
506
0
  if (t1max >= 0 && t1max < 1.0 && (count == 1 || t2min > t1max)) {
507
0
    // The second inflection point's approximation range begins after the end
508
0
    // of the first, approximate the first inflection point by a line and
509
0
    // subsequently flatten up until the end or the next inflection point.
510
0
    SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
511
0
512
0
    aSink->LineTo(nextCPs.mCP1.ToPoint());
513
0
514
0
    if (count == 1 || (count > 1 && t2min >= 1.0)) {
515
0
      // No more inflection points to deal with, flatten the rest of the curve.
516
0
      FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
517
0
    }
518
0
  } else if (count > 1 && t2min > 1.0) {
519
0
    // We've already concluded t2min <= t1max, so if this is true the
520
0
    // approximation range for the first inflection point runs past the
521
0
    // end of the curve, draw a line to the end and we're done.
522
0
    aSink->LineTo(aControlPoints.mCP4.ToPoint());
523
0
    return;
524
0
  }
525
0
526
0
  if (count > 1 && t2min < 1.0 && t2max > 0) {
527
0
    if (t2min > 0 && t2min < t1max) {
528
0
      // In this case the t2 approximation range starts inside the t1
529
0
      // approximation range.
530
0
      SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
531
0
      aSink->LineTo(nextCPs.mCP1.ToPoint());
532
0
    } else if (t2min > 0 && t1max > 0) {
533
0
      SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
534
0
535
0
      // Find a control points describing the portion of the curve between t1max and t2min.
536
0
      double t2mina = (t2min - t1max) / (1 - t1max);
537
0
      SplitBezier(nextCPs, &prevCPs, &nextCPs, t2mina);
538
0
      FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
539
0
    } else if (t2min > 0) {
540
0
      // We have nothing interesting before t2min, find that bit and flatten it.
541
0
      SplitBezier(aControlPoints, &prevCPs, &nextCPs, t2min);
542
0
      FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
543
0
    }
544
0
    if (t2max < 1.0) {
545
0
      // Flatten the portion of the curve after t2max
546
0
      SplitBezier(aControlPoints, nullptr, &nextCPs, t2max);
547
0
548
0
      // Draw a line to the start, this is the approximation between t2min and
549
0
      // t2max.
550
0
      aSink->LineTo(nextCPs.mCP1.ToPoint());
551
0
      FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
552
0
    } else {
553
0
      // Our approximation range extends beyond the end of the curve.
554
0
      aSink->LineTo(aControlPoints.mCP4.ToPoint());
555
0
      return;
556
0
    }
557
0
  }
558
0
}
559
560
} // namespace gfx
561
} // namespace mozilla