| Index: src/core/SkGeometry.cpp
|
| diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
|
| index ac02afc6334644487cc55cb88aa0c6757203e7ab..7896b0db40d8debb0d682b13767bb8f1d151ad5b 100644
|
| --- a/src/core/SkGeometry.cpp
|
| +++ b/src/core/SkGeometry.cpp
|
| @@ -1555,3 +1555,89 @@ SkScalar SkConic::TransformW(const SkPoint pts[], SkScalar w,
|
| w = SkScalarSqrt((w1 * w1) / (w0 * w2));
|
| return w;
|
| }
|
| +
|
| +int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir,
|
| + const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) {
|
| + // rotate by x,y so that uStart is (1.0)
|
| + SkScalar x = SkPoint::DotProduct(uStart, uStop);
|
| + SkScalar y = SkPoint::CrossProduct(uStart, uStop);
|
| +
|
| + SkScalar absY = SkScalarAbs(y);
|
| +
|
| + // check for (effectively) coincident vectors
|
| + // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
|
| + // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
|
| + if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) ||
|
| + (y <= 0 && kCCW_SkRotationDirection == dir))) {
|
| + return 0;
|
| + }
|
| +
|
| + if (dir == kCCW_SkRotationDirection) {
|
| + y = -y;
|
| + }
|
| +
|
| + // We decide to use 1-conic per quadrant of a circle. What quadrant does [xy] lie in?
|
| + // 0 == [0 .. 90)
|
| + // 1 == [90 ..180)
|
| + // 2 == [180..270)
|
| + // 3 == [270..360)
|
| + //
|
| + int quadrant = 0;
|
| + if (0 == y) {
|
| + quadrant = 2; // 180
|
| + SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
|
| + } else if (0 == x) {
|
| + SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
|
| + quadrant = y > 0 ? 1 : 3; // 90 : 270
|
| + } else {
|
| + if (y < 0) {
|
| + quadrant += 2;
|
| + }
|
| + if ((x < 0) != (y < 0)) {
|
| + quadrant += 1;
|
| + }
|
| + }
|
| +
|
| + const SkPoint quadrantPts[] = {
|
| + { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }
|
| + };
|
| + const SkScalar quadrantWeight = SK_ScalarRoot2Over2;
|
| +
|
| + int conicCount = quadrant;
|
| + for (int i = 0; i < conicCount; ++i) {
|
| + dst[i].set(&quadrantPts[i * 2], quadrantWeight);
|
| + }
|
| +
|
| + // Now compute any remaing (sub-90-degree) arc for the last conic
|
| + const SkPoint finalP = { x, y };
|
| + const SkPoint& lastQ = quadrantPts[quadrant * 2]; // will already be a unit-vector
|
| + const SkScalar dot = SkVector::DotProduct(lastQ, finalP);
|
| + SkASSERT(0 <= dot && dot <= SK_Scalar1);
|
| +
|
| + if (dot < 1 - SK_ScalarNearlyZero) {
|
| + SkVector offCurve = { lastQ.x() + x, lastQ.y() + y };
|
| + // compute the bisector vector, and then rescale to be the off-curve point.
|
| + // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get
|
| + // length = sqrt(2 / (1 + cos(theta)). We already have cos() when to computed the dot.
|
| + // This is nice, since our computed weight is cos(theta/2) as well!
|
| + //
|
| + const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2);
|
| + offCurve.setLength(SkScalarInvert(cosThetaOver2));
|
| + dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2);
|
| + conicCount += 1;
|
| + }
|
| +
|
| + // now handle counter-clockwise and the initial unitStart rotation
|
| + SkMatrix matrix;
|
| + matrix.setSinCos(uStart.fY, uStart.fX);
|
| + if (dir == kCCW_SkRotationDirection) {
|
| + matrix.preScale(SK_Scalar1, -SK_Scalar1);
|
| + }
|
| + if (userMatrix) {
|
| + matrix.postConcat(*userMatrix);
|
| + }
|
| + for (int i = 0; i < conicCount; ++i) {
|
| + matrix.mapPoints(dst[i].fPts, 3);
|
| + }
|
| + return conicCount;
|
| +}
|
|
|