diff options
author | Derek Sollenberger <djsollen@google.com> | 2017-12-05 15:39:30 -0500 |
---|---|---|
committer | Derek Sollenberger <djsollen@google.com> | 2017-12-05 15:39:30 -0500 |
commit | 12f9b0664219f4c8adf5013496feb5c718acbef5 (patch) | |
tree | f53520786e88ba405d6b19e2be616a352918fa77 /libs/hwui/utils/VectorDrawableUtils.cpp | |
parent | 1beccb0fc230d9e2030ad951d483fb0026ea2d49 (diff) |
Update VectorDrawables to use Skia's drawArc implementation.
Using Skia enables drawArc to issue conic draw calls as well as
fixes some of the issues around a scaled path containing a drawArc
being reported as concave.
Bug: 69622768
Test: hwui_unit_tests
Change-Id: I4faab5403ec4ee34e1ad6fae256ae9ad3c6bb05b
Diffstat (limited to 'libs/hwui/utils/VectorDrawableUtils.cpp')
-rw-r--r-- | libs/hwui/utils/VectorDrawableUtils.cpp | 140 |
1 files changed, 8 insertions, 132 deletions
diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp index 1931d646fa4a..6b8f3154dd36 100644 --- a/libs/hwui/utils/VectorDrawableUtils.cpp +++ b/libs/hwui/utils/VectorDrawableUtils.cpp @@ -96,132 +96,6 @@ void VectorDrawableUtils::interpolatePaths(PathData* outData, const PathData& fr } } -/** - * Converts an arc to cubic Bezier segments and records them in p. - * - * @param p The target for the cubic Bezier segments - * @param cx The x coordinate center of the ellipse - * @param cy The y coordinate center of the ellipse - * @param a The radius of the ellipse in the horizontal direction - * @param b The radius of the ellipse in the vertical direction - * @param e1x E(eta1) x coordinate of the starting point of the arc - * @param e1y E(eta2) y coordinate of the starting point of the arc - * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane - * @param start The start angle of the arc on the ellipse - * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse - */ -static void arcToBezier(SkPath* p, double cx, double cy, double a, double b, double e1x, double e1y, - double theta, double start, double sweep) { - // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html - // and http://www.spaceroots.org/documents/ellipse/node22.html - - // Maximum of 45 degrees per cubic Bezier segment - int numSegments = ceil(fabs(sweep * 4 / M_PI)); - - double eta1 = start; - double cosTheta = cos(theta); - double sinTheta = sin(theta); - double cosEta1 = cos(eta1); - double sinEta1 = sin(eta1); - double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1); - double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1); - - double anglePerSegment = sweep / numSegments; - for (int i = 0; i < numSegments; i++) { - double eta2 = eta1 + anglePerSegment; - double sinEta2 = sin(eta2); - double cosEta2 = cos(eta2); - double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2); - double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2); - double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2; - double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2; - double tanDiff2 = tan((eta2 - eta1) / 2); - double alpha = sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3; - double q1x = e1x + alpha * ep1x; - double q1y = e1y + alpha * ep1y; - double q2x = e2x - alpha * ep2x; - double q2y = e2y - alpha * ep2y; - - p->cubicTo((float)q1x, (float)q1y, (float)q2x, (float)q2y, (float)e2x, (float)e2y); - eta1 = eta2; - e1x = e2x; - e1y = e2y; - ep1x = ep2x; - ep1y = ep2y; - } -} - -inline double toRadians(float theta) { - return theta * M_PI / 180; -} - -static void drawArc(SkPath* p, float x0, float y0, float x1, float y1, float a, float b, - float theta, bool isMoreThanHalf, bool isPositiveArc) { - /* Convert rotation angle from degrees to radians */ - double thetaD = toRadians(theta); - /* Pre-compute rotation matrix entries */ - double cosTheta = cos(thetaD); - double sinTheta = sin(thetaD); - /* Transform (x0, y0) and (x1, y1) into unit space */ - /* using (inverse) rotation, followed by (inverse) scale */ - double x0p = (x0 * cosTheta + y0 * sinTheta) / a; - double y0p = (-x0 * sinTheta + y0 * cosTheta) / b; - double x1p = (x1 * cosTheta + y1 * sinTheta) / a; - double y1p = (-x1 * sinTheta + y1 * cosTheta) / b; - - /* Compute differences and averages */ - double dx = x0p - x1p; - double dy = y0p - y1p; - double xm = (x0p + x1p) / 2; - double ym = (y0p + y1p) / 2; - /* Solve for intersecting unit circles */ - double dsq = dx * dx + dy * dy; - if (dsq == 0.0) { - VECTOR_DRAWABLE_LOGD("Points are coincident"); - return; /* Points are coincident */ - } - double disc = 1.0 / dsq - 1.0 / 4.0; - if (disc < 0.0) { - VECTOR_DRAWABLE_LOGD("Points are too far apart %f", dsq); - float adjust = (float)(sqrt(dsq) / 1.99999); - drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta, isMoreThanHalf, isPositiveArc); - return; /* Points are too far apart */ - } - double s = sqrt(disc); - double sdx = s * dx; - double sdy = s * dy; - double cx; - double cy; - if (isMoreThanHalf == isPositiveArc) { - cx = xm - sdy; - cy = ym + sdx; - } else { - cx = xm + sdy; - cy = ym - sdx; - } - - double eta0 = atan2((y0p - cy), (x0p - cx)); - - double eta1 = atan2((y1p - cy), (x1p - cx)); - - double sweep = (eta1 - eta0); - if (isPositiveArc != (sweep >= 0)) { - if (sweep > 0) { - sweep -= 2 * M_PI; - } else { - sweep += 2 * M_PI; - } - } - - cx *= a; - cy *= b; - double tcx = cx; - cx = cx * cosTheta - cy * sinTheta; - cy = tcx * sinTheta + cy * cosTheta; - - arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep); -} - // Use the given verb, and points in the range [start, end) to insert a command into the SkPath. void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, const std::vector<float>* points, size_t start, size_t end) { @@ -424,18 +298,20 @@ void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, break; case 'a': // Draws an elliptical arc // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - drawArc(outPath, currentX, currentY, points->at(k + 5) + currentX, - points->at(k + 6) + currentY, points->at(k + 0), points->at(k + 1), - points->at(k + 2), points->at(k + 3) != 0, points->at(k + 4) != 0); + outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + (SkPath::ArcSize) (points->at(k + 3) != 0), + (SkPath::Direction) (points->at(k + 4) == 0), + points->at(k + 5) + currentX, points->at(k + 6) + currentY); currentX += points->at(k + 5); currentY += points->at(k + 6); ctrlPointX = currentX; ctrlPointY = currentY; break; case 'A': // Draws an elliptical arc - drawArc(outPath, currentX, currentY, points->at(k + 5), points->at(k + 6), - points->at(k + 0), points->at(k + 1), points->at(k + 2), - points->at(k + 3) != 0, points->at(k + 4) != 0); + outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), + (SkPath::ArcSize) (points->at(k + 3) != 0), + (SkPath::Direction) (points->at(k + 4) == 0), + points->at(k + 5), points->at(k + 6)); currentX = points->at(k + 5); currentY = points->at(k + 6); ctrlPointX = currentX; |