diff options
Diffstat (limited to 'libs/hwui/PathTessellator.cpp')
-rw-r--r-- | libs/hwui/PathTessellator.cpp | 355 |
1 files changed, 200 insertions, 155 deletions
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 3970913814c6..310b1075f5d5 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#define LOG_TAG "PathTessellator" +#define LOG_TAG "OpenGLRenderer" #define LOG_NDEBUG 1 -#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define ATRACE_TAG ATRACE_TAG_VIEW #define VERTEX_DEBUG 0 @@ -24,11 +24,11 @@ #define DEBUG_DUMP_ALPHA_BUFFER() \ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ ALOGD("point %d at %f %f, alpha %f", \ - i, buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); \ + i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ } #define DEBUG_DUMP_BUFFER() \ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ - ALOGD("point %d at %f %f", i, buffer[i].position[0], buffer[i].position[1]); \ + ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \ } #else #define DEBUG_DUMP_ALPHA_BUFFER() @@ -53,25 +53,31 @@ namespace android { namespace uirenderer { -#define THRESHOLD 0.5f +#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f) #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f -void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, - bool forceExpand) { - if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) { - float outset = paint->getStrokeWidth() * 0.5f; - if (outset == 0) outset = 0.5f; // account for hairline - bounds.outset(outset, outset); - } -} - -inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) { - Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]); -} +// temporary error thresholds +#define ERROR_DEPTH 20 +#define ERROR_SCALE 1e10 +#define ERROR_SQR_INV_THRESH 1e-20 -inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) { - AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha); +void PathTessellator::extractTessellationScales(const Matrix4& transform, + float* scaleX, float* scaleY) { + if (CC_LIKELY(transform.isPureTranslate())) { + *scaleX = 1.0f; + *scaleY = 1.0f; + } else { + float m00 = transform.data[Matrix4::kScaleX]; + float m01 = transform.data[Matrix4::kSkewY]; + float m10 = transform.data[Matrix4::kSkewX]; + float m11 = transform.data[Matrix4::kScaleY]; + *scaleX = sqrt(m00 * m00 + m01 * m01); + *scaleY = sqrt(m10 * m10 + m11 * m11); + + LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE, + "scales %e x %e too large for tessellation", *scaleX, *scaleY); + } } /** @@ -93,24 +99,23 @@ inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& norma */ struct PaintInfo { public: - PaintInfo(const SkPaint* paint, const mat4 *transform) : + PaintInfo(const SkPaint* paint, const mat4& transform) : style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), - inverseScaleX(1.0f), inverseScaleY(1.0f), halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { // compute inverse scales - if (CC_UNLIKELY(!transform->isPureTranslate())) { - float m00 = transform->data[Matrix4::kScaleX]; - float m01 = transform->data[Matrix4::kSkewY]; - float m10 = transform->data[Matrix4::kSkewX]; - float m11 = transform->data[Matrix4::kScaleY]; - float scaleX = sqrt(m00 * m00 + m01 * m01); - float scaleY = sqrt(m10 * m10 + m11 * m11); + if (CC_LIKELY(transform.isPureTranslate())) { + inverseScaleX = 1.0f; + inverseScaleY = 1.0f; + } else { + float scaleX, scaleY; + PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f; inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f; } if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY && 2 * halfStrokeWidth < inverseScaleX) { + // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline. maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX; halfStrokeWidth = 0.0f; } @@ -159,6 +164,17 @@ public: } return 0; } + + /** + * Outset the bounds of point data (for line endpoints or points) to account for AA stroke + * geometry. + */ + void expandBoundsForStroke(Rect* bounds) const { + float outset = halfStrokeWidth; + if (outset == 0) outset = 0.5f; + bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(), + outset * inverseScaleY + Vertex::GeometryFudgeFactor()); + } }; void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { @@ -170,9 +186,9 @@ void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& int srcAindex = 0; int srcBindex = perimeter.size() - 1; while (srcAindex <= srcBindex) { - copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]); + buffer[currentIndex++] = perimeter[srcAindex]; if (srcAindex == srcBindex) break; - copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]); + buffer[currentIndex++] = perimeter[srcBindex]; srcAindex++; srcBindex--; } @@ -192,25 +208,25 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver int currentIndex = 0; const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(totalOffset); Vertex::set(&buffer[currentIndex++], - current->position[0] + totalOffset.x, - current->position[1] + totalOffset.y); + current->x + totalOffset.x, + current->y + totalOffset.y); Vertex::set(&buffer[currentIndex++], - current->position[0] - totalOffset.x, - current->position[1] - totalOffset.y); + current->x - totalOffset.x, + current->y - totalOffset.y); last = current; current = next; @@ -218,8 +234,8 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver } // wrap around to beginning - copyVertex(&buffer[currentIndex++], &buffer[0]); - copyVertex(&buffer[currentIndex++], &buffer[1]); + buffer[currentIndex++] = buffer[0]; + buffer[currentIndex++] = buffer[1]; DEBUG_DUMP_BUFFER(); } @@ -229,7 +245,7 @@ static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& cente vec2 strokeOffset = normal; paintInfo.scaleOffsetForStrokeWidth(strokeOffset); - vec2 referencePoint(center.position[0], center.position[1]); + vec2 referencePoint(center.x, center.y); if (paintInfo.cap == SkPaint::kSquare_Cap) { referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1); } @@ -255,11 +271,11 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, if (extra > 0) { // tessellate both round caps float beginTheta = atan2( - - (vertices[0].position[0] - vertices[1].position[0]), - vertices[0].position[1] - vertices[1].position[1]); + - (vertices[0].x - vertices[1].x), + vertices[0].y - vertices[1].y); float endTheta = atan2( - - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]), - vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]); + - (vertices[lastIndex].x - vertices[lastIndex - 1].x), + vertices[lastIndex].y - vertices[lastIndex - 1].y); const float dTheta = PI / (extra + 1); const float radialScale = 2.0f / (1 + cos(dTheta)); @@ -275,37 +291,37 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta)); paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); Vertex::set(&buffer[capOffset], - vertices[0].position[0] + beginRadialOffset.x, - vertices[0].position[1] + beginRadialOffset.y); + vertices[0].x + beginRadialOffset.x, + vertices[0].y + beginRadialOffset.y); endTheta += dTheta; vec2 endRadialOffset(cos(endTheta), sin(endTheta)); paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); Vertex::set(&buffer[allocSize - 1 - capOffset], - vertices[lastIndex].position[0] + endRadialOffset.x, - vertices[lastIndex].position[1] + endRadialOffset.y); + vertices[lastIndex].x + endRadialOffset.x, + vertices[lastIndex].y + endRadialOffset.y); } } int currentIndex = extra; const Vertex* last = &(vertices[0]); const Vertex* current = &(vertices[1]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true); for (unsigned int i = 1; i < vertices.size() - 1; i++) { const Vertex* next = &(vertices[i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(strokeOffset); - vec2 center(current->position[0], current->position[1]); + vec2 center(current->x, current->y); Vertex::set(&buffer[currentIndex++], center + strokeOffset); Vertex::set(&buffer[currentIndex++], center - strokeOffset); @@ -329,7 +345,7 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices) */ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, - VertexBuffer& vertexBuffer) { + VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) { AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); // generate alpha points - fill Alpha vertex gaps in between each point with @@ -337,13 +353,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver int currentIndex = 0; const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); // AA point offset from original point is that point's normal, such that each side is offset @@ -351,13 +367,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); AlphaVertex::set(&buffer[currentIndex++], - current->position[0] + totalOffset.x, - current->position[1] + totalOffset.y, + current->x + totalOffset.x, + current->y + totalOffset.y, 0.0f); AlphaVertex::set(&buffer[currentIndex++], - current->position[0] - totalOffset.x, - current->position[1] - totalOffset.y, - 1.0f); + current->x - totalOffset.x, + current->y - totalOffset.y, + maxAlpha); last = current; current = next; @@ -365,8 +381,8 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver } // wrap around to beginning - copyAlphaVertex(&buffer[currentIndex++], &buffer[0]); - copyAlphaVertex(&buffer[currentIndex++], &buffer[1]); + buffer[currentIndex++] = buffer[0]; + buffer[currentIndex++] = buffer[1]; // zig zag between all previous points on the inside of the hull to create a // triangle strip that fills the hull, repeating the first inner point to @@ -374,9 +390,9 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver int srcAindex = 0; int srcBindex = perimeter.size() - 1; while (srcAindex <= srcBindex) { - copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]); + buffer[currentIndex++] = buffer[srcAindex * 2 + 1]; if (srcAindex == srcBindex) break; - copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]); + buffer[currentIndex++] = buffer[srcBindex * 2 + 1]; srcAindex++; srcBindex--; } @@ -416,7 +432,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& // determine referencePoint, the center point for the 4 primary cap vertices const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1); - vec2 referencePoint(point->position[0], point->position[1]); + vec2 referencePoint(point->x, point->y); if (paintInfo.cap == SkPaint::kSquare_Cap) { // To account for square cap, move the primary cap vertices (that create the AA edge) by the // stroke offset vector (rotated to be parallel to the stroke) @@ -471,8 +487,8 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& if (isFirst && i == extra - extraOffset) { //copy most recent two points to first two points - copyAlphaVertex(&buffer[0], &buffer[capPerimIndex - 2]); - copyAlphaVertex(&buffer[1], &buffer[capPerimIndex - 1]); + buffer[0] = buffer[capPerimIndex - 2]; + buffer[1] = buffer[capPerimIndex - 1]; capPerimIndex = 2; // start writing the rest of the round cap at index 2 } @@ -482,28 +498,28 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4; int capFillIndex = startCapFillIndex; for (int i = 0; i < extra + 2; i += 2) { - copyAlphaVertex(&buffer[capFillIndex++], &buffer[1 + i]); + buffer[capFillIndex++] = buffer[1 + i]; // TODO: to support odd numbers of divisions, break here on the last iteration - copyAlphaVertex(&buffer[capFillIndex++], &buffer[startCapFillIndex - 3 - i]); + buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i]; } } else { int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2); for (int i = 0; i < extra + 2; i += 2) { - copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 1 + i]); + buffer[capFillIndex++] = buffer[capIndex + 1 + i]; // TODO: to support odd numbers of divisions, break here on the last iteration - copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 3 + 2 * extra - i]); + buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i]; } } return; } if (isFirst) { - copyAlphaVertex(&buffer[0], &buffer[postCapIndex + 2]); - copyAlphaVertex(&buffer[1], &buffer[postCapIndex + 3]); - copyAlphaVertex(&buffer[postCapIndex + 4], &buffer[1]); // degenerate tris (the only two!) - copyAlphaVertex(&buffer[postCapIndex + 5], &buffer[postCapIndex + 1]); + buffer[0] = buffer[postCapIndex + 2]; + buffer[1] = buffer[postCapIndex + 3]; + buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) + buffer[postCapIndex + 5] = buffer[postCapIndex + 1]; } else { - copyAlphaVertex(&buffer[6 * vertices.size()], &buffer[postCapIndex + 1]); - copyAlphaVertex(&buffer[6 * vertices.size() + 1], &buffer[postCapIndex + 3]); + buffer[6 * vertices.size()] = buffer[postCapIndex + 1]; + buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3]; } } @@ -576,8 +592,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, const Vertex* last = &(vertices[0]); const Vertex* current = &(vertices[1]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); // TODO: use normal from bezier traversal for cap, instead of from vertices @@ -585,8 +601,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, for (unsigned int i = 1; i < vertices.size() - 1; i++) { const Vertex* next = &(vertices[i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); @@ -598,30 +614,30 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, innerOffset -= AAOffset; AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + outerOffset.x, - current->position[1] + outerOffset.y, + current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->position[0] - outerOffset.x, - current->position[1] - outerOffset.y, + current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); current = next; @@ -646,13 +662,13 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); @@ -664,30 +680,30 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V innerOffset -= AAOffset; AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + outerOffset.x, - current->position[1] + outerOffset.y, + current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->position[0] - outerOffset.x, - current->position[1] - outerOffset.y, + current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); last = current; @@ -696,23 +712,23 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V } // wrap each strip around to beginning, creating degenerate tris to bridge strips - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]); - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); + buffer[currentAAOuterIndex++] = buffer[0]; + buffer[currentAAOuterIndex++] = buffer[1]; + buffer[currentAAOuterIndex++] = buffer[1]; - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]); - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); + buffer[currentStrokeIndex++] = buffer[offset]; + buffer[currentStrokeIndex++] = buffer[offset + 1]; + buffer[currentStrokeIndex++] = buffer[offset + 1]; - copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]); - copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]); + buffer[currentAAInnerIndex++] = buffer[2 * offset]; + buffer[currentAAInnerIndex++] = buffer[2 * offset + 1]; // don't need to create last degenerate tri DEBUG_DUMP_ALPHA_BUFFER(); } void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, - const mat4 *transform, VertexBuffer& vertexBuffer) { + const mat4& transform, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -733,7 +749,8 @@ 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; bool wasClosed = approximatePathOutlineVertices(path, forceClose, - threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, tempVertices); + threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, + OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices); if (!tempVertices.size()) { // path was empty, return without allocating vertex buffer @@ -743,7 +760,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, #if VERTEX_DEBUG for (unsigned int i = 0; i < tempVertices.size(); i++) { ALOGD("orig path: point at %f %f", - tempVertices[i].position[0], tempVertices[i].position[1]); + tempVertices[i].x, tempVertices[i].y); } #endif @@ -771,21 +788,25 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer); } } + + Rect bounds(path.getBounds()); + paintInfo.expandBoundsForStroke(&bounds); + vertexBuffer.setBounds(bounds); } -static void expandRectToCoverVertex(SkRect& rect, float x, float y) { - rect.fLeft = fminf(rect.fLeft, x); - rect.fTop = fminf(rect.fTop, y); - rect.fRight = fmaxf(rect.fRight, x); - rect.fBottom = fmaxf(rect.fBottom, y); +static void expandRectToCoverVertex(Rect& rect, float x, float y) { + rect.left = fminf(rect.left, x); + rect.top = fminf(rect.top, y); + rect.right = fmaxf(rect.right, x); + rect.bottom = fmaxf(rect.bottom, y); } -static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) { - expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]); +static void expandRectToCoverVertex(Rect& rect, const Vertex& vertex) { + expandRectToCoverVertex(rect, vertex.x, vertex.y); } template <class TYPE> static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, - const float* points, int count, SkRect& bounds) { + const float* points, int count, Rect& bounds) { bounds.set(points[0], points[1], points[0], points[1]); int numPoints = count / 2; @@ -799,8 +820,8 @@ static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint); } -void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint, + const mat4& transform, VertexBuffer& vertexBuffer) { const PaintInfo paintInfo(paint, transform); // determine point shape @@ -818,10 +839,12 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* Vector<Vertex> outlineVertices; approximatePathOutlineVertices(path, true, paintInfo.inverseScaleX * paintInfo.inverseScaleX, - paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices); + paintInfo.inverseScaleY * paintInfo.inverseScaleY, + OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices); if (!outlineVertices.size()) return; + Rect bounds; // tessellate, then duplicate outline across points int numPoints = count / 2; VertexBuffer tempBuffer; @@ -829,15 +852,18 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* getFillVerticesFromPerimeter(outlineVertices, tempBuffer); instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds); } else { - getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer); + // note: pass maxAlpha directly, since we want fill to be alpha modulated + getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha); instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds); } - expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke + // expand bounds from vertex coords to pixel data + paintInfo.expandBoundsForStroke(&bounds); + vertexBuffer.setBounds(bounds); } -void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, + const mat4& transform, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -857,6 +883,7 @@ void PathTessellator::tessellateLines(const float* points, int count, SkPaint* p tempVertices.push(); tempVertices.push(); Vertex* tempVerticesData = tempVertices.editArray(); + Rect bounds; bounds.set(points[0], points[1], points[0], points[1]); for (int i = 0; i < count; i += 4) { Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]); @@ -873,20 +900,27 @@ void PathTessellator::tessellateLines(const float* points, int count, SkPaint* p expandRectToCoverVertex(bounds, tempVerticesData[1]); } - expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke - // since multiple objects tessellated into buffer, separate them with degen tris if (paintInfo.isAA) { vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize); } else { vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize); } + + // expand bounds from vertex coords to pixel data + paintInfo.expandBoundsForStroke(&bounds); + vertexBuffer.setBounds(bounds); } /////////////////////////////////////////////////////////////////////////////// // Simple path line approximation /////////////////////////////////////////////////////////////////////////////// +bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared, + Vector<Vertex>& outputVertices) { + return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices); +} + void pushToVector(Vector<Vertex>& vertices, float x, float y) { // TODO: make this not yuck vertices.push(); @@ -895,9 +929,13 @@ void pushToVector(Vector<Vertex>& vertices, float x, float y) { } bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex>& outputVertices) { ATRACE_CALL(); + LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH, + "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY); + // TODO: to support joins other than sharp miter, join vertices should be labelled in the // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case. SkPath::Iter iter(path, forceClose); @@ -922,7 +960,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, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); break; case SkPath::kCubic_Verb: ALOGV("kCubic_Verb"); @@ -931,7 +969,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, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); break; default: break; @@ -939,8 +977,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo } int size = outputVertices.size(); - if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] && - outputVertices[0].position[1] == outputVertices[size - 1].position[1]) { + if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && + outputVertices[0].y == outputVertices[size - 1].y) { outputVertices.pop(); return true; } @@ -954,7 +992,11 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex>& outputVertices, int depth) { + LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d", + sqrInvScaleX, sqrInvScaleY, outputVertices.size()); + float dx = p2x - p1x; float dy = p2y - p1y; float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); @@ -962,8 +1004,7 @@ void PathTessellator::recursiveCubicBezierVertices( float d = d1 + d2; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors - - if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { @@ -987,11 +1028,11 @@ void PathTessellator::recursiveCubicBezierVertices( recursiveCubicBezierVertices( p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); recursiveCubicBezierVertices( mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); } } @@ -999,12 +1040,16 @@ void PathTessellator::recursiveQuadraticBezierVertices( float ax, float ay, float bx, float by, float cx, float cy, - float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { + float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, + Vector<Vertex>& outputVertices, int depth) { + LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d", + sqrInvScaleX, sqrInvScaleY, outputVertices.size()); + float dx = bx - ax; float dy = by - ay; float d = (cx - bx) * dy - (cy - by) * dx; - if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { @@ -1018,9 +1063,9 @@ void PathTessellator::recursiveQuadraticBezierVertices( float my = (acy + bcy) * 0.5f; recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, - sqrInvScaleX, sqrInvScaleY, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); } } |