gfx/2d/Path.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial