Loading libs/hwui/PathTessellator.cpp +35 −25 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ namespace android { namespace uirenderer { #define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f) #define OUTLINE_REFINE_THRESHOLD 0.5f #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f #define MAX_DEPTH 15 Loading Loading @@ -739,9 +739,10 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, // force close if we're filling the path, since fill path expects closed perimeter. bool forceClose = paintInfo.style != SkPaint::kStroke_Style; PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY, OUTLINE_REFINE_THRESHOLD); bool wasClosed = approximatePathOutlineVertices(path, forceClose, threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices); approximationInfo, tempVertices); if (!tempVertices.size()) { // path was empty, return without allocating vertex buffer Loading Loading @@ -819,10 +820,9 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP // calculate outline Vector<Vertex> outlineVertices; approximatePathOutlineVertices(path, true, paintInfo.inverseScaleX * paintInfo.inverseScaleX, paintInfo.inverseScaleY * paintInfo.inverseScaleY, OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices); PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY, OUTLINE_REFINE_THRESHOLD); approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices); if (!outlineVertices.size()) return; Loading Loading @@ -899,9 +899,10 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa // Simple path line approximation /////////////////////////////////////////////////////////////////////////////// bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared, bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold, Vector<Vertex>& outputVertices) { return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices); PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold); return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices); } void pushToVector(Vector<Vertex>& vertices, float x, float y) { Loading Loading @@ -946,8 +947,7 @@ private: }; bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices) { const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices) { ATRACE_CALL(); // TODO: to support joins other than sharp miter, join vertices should be labelled in the Loading Loading @@ -978,7 +978,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[0].x(), pts[0].y(), pts[2].x(), pts[2].y(), pts[1].x(), pts[1].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); approximationInfo, outputVertices); clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); break; Loading @@ -989,7 +989,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[1].x(), pts[1].y(), pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); approximationInfo, outputVertices); clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); clockwiseEnforcer.addPoint(pts[3]); Loading @@ -998,14 +998,14 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo ALOGV("kConic_Verb"); SkAutoConicToQuads converter; const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(), thresholdSquared); approximationInfo.thresholdForConicQuads); for (int i = 0; i < converter.countQuads(); ++i) { const int offset = 2 * i; recursiveQuadraticBezierVertices( quads[offset].x(), quads[offset].y(), quads[offset+2].x(), quads[offset+2].y(), quads[offset+1].x(), quads[offset+1].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); approximationInfo, outputVertices); } clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); Loading @@ -1031,12 +1031,23 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo /////////////////////////////////////////////////////////////////////////////// // Bezier approximation // // All the inputs and outputs here are in path coordinates. // We convert the error threshold from screen coordinates into path coordinates. /////////////////////////////////////////////////////////////////////////////// // Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates. // TODO: Document the math behind this algorithm. static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) { // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX); return info.thresholdSquared * scale; } void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices, int depth) { float dx = p2x - p1x; float dy = p2y - p1y; Loading @@ -1044,9 +1055,8 @@ void PathTessellator::recursiveCubicBezierVertices( float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx); float d = d1 + d2; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors if (depth >= MAX_DEPTH || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { || d * d <= getThreshold(approximationInfo, dx, dy)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { Loading @@ -1070,11 +1080,11 @@ void PathTessellator::recursiveCubicBezierVertices( recursiveCubicBezierVertices( p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); recursiveCubicBezierVertices( mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); } } Loading @@ -1082,15 +1092,15 @@ void PathTessellator::recursiveQuadraticBezierVertices( float ax, float ay, float bx, float by, float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices, int depth) { float dx = bx - ax; float dy = by - ay; // d is the cross product of vector (B-A) and (C-B). float d = (cx - bx) * dy - (cy - by) * dx; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors if (depth >= MAX_DEPTH || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { || d * d <= getThreshold(approximationInfo, dx, dy)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { Loading @@ -1104,9 +1114,9 @@ void PathTessellator::recursiveQuadraticBezierVertices( float my = (acy + bcy) * 0.5f; recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); } } Loading libs/hwui/PathTessellator.h +25 −6 Original line number Diff line number Diff line Loading @@ -27,6 +27,26 @@ namespace android { namespace uirenderer { /** * Structure used for threshold values in outline path tessellation. * * TODO: PaintInfo should store one of this object, and initialized all values in constructor * depending on its type (point, line or path). */ struct PathApproximationInfo { PathApproximationInfo(float invScaleX, float invScaleY, float pixelThreshold) : thresholdSquared(pixelThreshold * pixelThreshold) , sqrInvScaleX(invScaleX * invScaleX) , sqrInvScaleY(invScaleY * invScaleY) , thresholdForConicQuads(pixelThreshold * MathUtils::min(invScaleX, invScaleY) / 2.0f) { }; const float thresholdSquared; const float sqrInvScaleX; const float sqrInvScaleY; const float thresholdForConicQuads; }; class PathTessellator { public: /** Loading Loading @@ -85,16 +105,15 @@ public: * Approximates a convex outline into a clockwise Vector of 2d vertices. * * @param path The outline to be approximated * @param thresholdSquared The threshold of acceptable error (in pixels) when approximating * @param threshold The threshold of acceptable error (in pixels) when approximating * @param outputVertices An empty Vector which will be populated with the output */ static bool approximatePathOutlineVertices(const SkPath &path, float thresholdSquared, static bool approximatePathOutlineVertices(const SkPath &path, float threshold, Vector<Vertex> &outputVertices); private: static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex> &outputVertices); const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices); /* endpoints a & b, Loading @@ -104,7 +123,7 @@ private: float ax, float ay, float bx, float by, float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices, int depth = 0); /* Loading @@ -116,7 +135,7 @@ private: float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices, int depth = 0); }; Loading libs/hwui/TessellationCache.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -226,9 +226,9 @@ static void tessellateShadows( // tessellate caster outline into a 2d polygon Vector<Vertex> casterVertices2d; const float casterRefinementThresholdSquared = 4.0f; const float casterRefinementThreshold = 2.0f; PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThresholdSquared, casterVertices2d); casterRefinementThreshold, casterVertices2d); // Shadow requires CCW for now. TODO: remove potential double-reverse reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size()); Loading Loading
libs/hwui/PathTessellator.cpp +35 −25 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ namespace android { namespace uirenderer { #define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f) #define OUTLINE_REFINE_THRESHOLD 0.5f #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f #define MAX_DEPTH 15 Loading Loading @@ -739,9 +739,10 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, // force close if we're filling the path, since fill path expects closed perimeter. bool forceClose = paintInfo.style != SkPaint::kStroke_Style; PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY, OUTLINE_REFINE_THRESHOLD); bool wasClosed = approximatePathOutlineVertices(path, forceClose, threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices); approximationInfo, tempVertices); if (!tempVertices.size()) { // path was empty, return without allocating vertex buffer Loading Loading @@ -819,10 +820,9 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP // calculate outline Vector<Vertex> outlineVertices; approximatePathOutlineVertices(path, true, paintInfo.inverseScaleX * paintInfo.inverseScaleX, paintInfo.inverseScaleY * paintInfo.inverseScaleY, OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices); PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY, OUTLINE_REFINE_THRESHOLD); approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices); if (!outlineVertices.size()) return; Loading Loading @@ -899,9 +899,10 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa // Simple path line approximation /////////////////////////////////////////////////////////////////////////////// bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared, bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold, Vector<Vertex>& outputVertices) { return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices); PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold); return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices); } void pushToVector(Vector<Vertex>& vertices, float x, float y) { Loading Loading @@ -946,8 +947,7 @@ private: }; bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices) { const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices) { ATRACE_CALL(); // TODO: to support joins other than sharp miter, join vertices should be labelled in the Loading Loading @@ -978,7 +978,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[0].x(), pts[0].y(), pts[2].x(), pts[2].y(), pts[1].x(), pts[1].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); approximationInfo, outputVertices); clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); break; Loading @@ -989,7 +989,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[1].x(), pts[1].y(), pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); approximationInfo, outputVertices); clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); clockwiseEnforcer.addPoint(pts[3]); Loading @@ -998,14 +998,14 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo ALOGV("kConic_Verb"); SkAutoConicToQuads converter; const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(), thresholdSquared); approximationInfo.thresholdForConicQuads); for (int i = 0; i < converter.countQuads(); ++i) { const int offset = 2 * i; recursiveQuadraticBezierVertices( quads[offset].x(), quads[offset].y(), quads[offset+2].x(), quads[offset+2].y(), quads[offset+1].x(), quads[offset+1].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); approximationInfo, outputVertices); } clockwiseEnforcer.addPoint(pts[1]); clockwiseEnforcer.addPoint(pts[2]); Loading @@ -1031,12 +1031,23 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo /////////////////////////////////////////////////////////////////////////////// // Bezier approximation // // All the inputs and outputs here are in path coordinates. // We convert the error threshold from screen coordinates into path coordinates. /////////////////////////////////////////////////////////////////////////////// // Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates. // TODO: Document the math behind this algorithm. static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) { // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX); return info.thresholdSquared * scale; } void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices, int depth) { float dx = p2x - p1x; float dy = p2y - p1y; Loading @@ -1044,9 +1055,8 @@ void PathTessellator::recursiveCubicBezierVertices( float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx); float d = d1 + d2; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors if (depth >= MAX_DEPTH || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { || d * d <= getThreshold(approximationInfo, dx, dy)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { Loading @@ -1070,11 +1080,11 @@ void PathTessellator::recursiveCubicBezierVertices( recursiveCubicBezierVertices( p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); recursiveCubicBezierVertices( mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); } } Loading @@ -1082,15 +1092,15 @@ void PathTessellator::recursiveQuadraticBezierVertices( float ax, float ay, float bx, float by, float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices, int depth) { float dx = bx - ax; float dy = by - ay; // d is the cross product of vector (B-A) and (C-B). float d = (cx - bx) * dy - (cy - by) * dx; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors if (depth >= MAX_DEPTH || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { || d * d <= getThreshold(approximationInfo, dx, dy)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { Loading @@ -1104,9 +1114,9 @@ void PathTessellator::recursiveQuadraticBezierVertices( float my = (acy + bcy) * 0.5f; recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); approximationInfo, outputVertices, depth + 1); } } Loading
libs/hwui/PathTessellator.h +25 −6 Original line number Diff line number Diff line Loading @@ -27,6 +27,26 @@ namespace android { namespace uirenderer { /** * Structure used for threshold values in outline path tessellation. * * TODO: PaintInfo should store one of this object, and initialized all values in constructor * depending on its type (point, line or path). */ struct PathApproximationInfo { PathApproximationInfo(float invScaleX, float invScaleY, float pixelThreshold) : thresholdSquared(pixelThreshold * pixelThreshold) , sqrInvScaleX(invScaleX * invScaleX) , sqrInvScaleY(invScaleY * invScaleY) , thresholdForConicQuads(pixelThreshold * MathUtils::min(invScaleX, invScaleY) / 2.0f) { }; const float thresholdSquared; const float sqrInvScaleX; const float sqrInvScaleY; const float thresholdForConicQuads; }; class PathTessellator { public: /** Loading Loading @@ -85,16 +105,15 @@ public: * Approximates a convex outline into a clockwise Vector of 2d vertices. * * @param path The outline to be approximated * @param thresholdSquared The threshold of acceptable error (in pixels) when approximating * @param threshold The threshold of acceptable error (in pixels) when approximating * @param outputVertices An empty Vector which will be populated with the output */ static bool approximatePathOutlineVertices(const SkPath &path, float thresholdSquared, static bool approximatePathOutlineVertices(const SkPath &path, float threshold, Vector<Vertex> &outputVertices); private: static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex> &outputVertices); const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices); /* endpoints a & b, Loading @@ -104,7 +123,7 @@ private: float ax, float ay, float bx, float by, float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices, int depth = 0); /* Loading @@ -116,7 +135,7 @@ private: float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices, int depth = 0); }; Loading
libs/hwui/TessellationCache.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -226,9 +226,9 @@ static void tessellateShadows( // tessellate caster outline into a 2d polygon Vector<Vertex> casterVertices2d; const float casterRefinementThresholdSquared = 4.0f; const float casterRefinementThreshold = 2.0f; PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThresholdSquared, casterVertices2d); casterRefinementThreshold, casterVertices2d); // Shadow requires CCW for now. TODO: remove potential double-reverse reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size()); Loading