diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/CanvasState.cpp | 3 | ||||
-rw-r--r-- | libs/hwui/CanvasState.h | 1 | ||||
-rw-r--r-- | libs/hwui/DeferredDisplayList.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/DeferredDisplayList.h | 1 | ||||
-rw-r--r-- | libs/hwui/Matrix.h | 6 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 46 | ||||
-rwxr-xr-x | libs/hwui/OpenGLRenderer.h | 1 | ||||
-rw-r--r-- | libs/hwui/PathTessellator.cpp | 53 | ||||
-rw-r--r-- | libs/hwui/PathTessellator.h | 2 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 34 | ||||
-rw-r--r-- | libs/hwui/ShadowTessellator.cpp | 65 | ||||
-rw-r--r-- | libs/hwui/ShadowTessellator.h | 17 | ||||
-rw-r--r-- | libs/hwui/Snapshot.cpp | 42 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 27 | ||||
-rw-r--r-- | libs/hwui/TessellationCache.cpp | 17 |
15 files changed, 190 insertions, 126 deletions
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index e88e9f6..e22b0d3 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -189,6 +189,9 @@ void CanvasState::setClippingRoundRect(LinearAllocator& allocator, mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); } +void CanvasState::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) { + mSnapshot->setProjectionPathMask(allocator, path); +} /////////////////////////////////////////////////////////////////////////////// // Quick Rejection diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h index 8e4a4d3..9354e94 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -130,6 +130,7 @@ public: void setClippingOutline(LinearAllocator& allocator, const Outline* outline); void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius, bool highPriority = true); + void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path); /** * Returns true if drawing in the rectangle (left, top, right, bottom) diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 6fcf958..b077a85 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -195,6 +195,7 @@ public: // Identical round rect clip state means both ops will clip in the same way, or not at all. // As the state objects are const, we can compare their pointers to determine mergeability if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false; + if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false; /* Clipping compatibility check * diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 3d0ca6d..160c1ad 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -63,6 +63,7 @@ public: mat4 mMatrix; float mAlpha; const RoundRectClipState* mRoundRectClipState; + const ProjectionPathMask* mProjectionPathMask; }; class OpStatePair { diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index a760135..c152789 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -134,6 +134,12 @@ public: uint8_t getType() const; + void multiplyInverse(const Matrix4& v) { + Matrix4 inv; + inv.loadInverse(v); + multiply(inv); + } + void multiply(const Matrix4& v) { Matrix4 u; u.loadMultiply(*this, v); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 54bcd7e..d87a3e6 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -40,6 +40,7 @@ #include <SkCanvas.h> #include <SkColor.h> +#include <SkPathOps.h> #include <SkShader.h> #include <SkTypeface.h> @@ -1193,8 +1194,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef state.mMatrix.load(*currentMatrix); state.mAlpha = currentSnapshot()->alpha; - // always store/restore, since it's just a pointer + // always store/restore, since these are just pointers state.mRoundRectClipState = currentSnapshot()->roundRectClipState; + state.mProjectionPathMask = currentSnapshot()->projectionPathMask; return false; } @@ -1202,6 +1204,7 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool setMatrix(state.mMatrix); writableSnapshot()->alpha = state.mAlpha; writableSnapshot()->roundRectClipState = state.mRoundRectClipState; + writableSnapshot()->projectionPathMask = state.mProjectionPathMask; if (state.mClipValid && !skipClipRestore) { writableSnapshot()->setClip(state.mClip.left, state.mClip.top, @@ -1755,6 +1758,7 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) { VertexBuffer vertexBuffer; // TODO: try clipping large paths to viewport + PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer); drawVertexBuffer(vertexBuffer, paint); } @@ -1861,19 +1865,41 @@ void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p || PaintUtils::paintWillNotDraw(*p)) { return; } + if (p->getPathEffect() != nullptr) { mCaches.textureState().activateTexture(0); PathTexture* texture = mCaches.pathCache.getCircle(radius, p); drawShape(x - radius, y - radius, texture, p); + return; + } + + SkPath path; + if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { + path.addCircle(x, y, radius + p->getStrokeWidth() / 2); } else { - SkPath path; - if (p->getStyle() == SkPaint::kStrokeAndFill_Style) { - path.addCircle(x, y, radius + p->getStrokeWidth() / 2); - } else { - path.addCircle(x, y, radius); - } - drawConvexPath(path, p); + path.addCircle(x, y, radius); + } + + if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) { + // mask ripples with projection mask + SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask); + + Matrix4 screenSpaceTransform; + currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform); + + Matrix4 totalTransform; + totalTransform.loadInverse(screenSpaceTransform); + totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform); + + SkMatrix skTotalTransform; + totalTransform.copyTo(skTotalTransform); + maskPath.transform(skTotalTransform); + + // Mask the ripple path by the projection mask, now that it's + // in local space. Note that this can create CCW paths. + Op(path, maskPath, kIntersect_PathOp, &path); } + drawConvexPath(path, p); } void OpenGLRenderer::drawOval(float left, float top, float right, float bottom, @@ -2146,6 +2172,10 @@ void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator, mState.setClippingRoundRect(allocator, rect, radius, highPriority); } +void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) { + mState.setProjectionPathMask(allocator, path); +} + void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 218818d..8dae82c 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -399,6 +399,7 @@ public: void setClippingOutline(LinearAllocator& allocator, const Outline* outline); void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius, bool highPriority = true); + void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path); inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); } inline const mat4* currentTransform() const { return mState.currentTransform(); } diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index c1f61d6..e7c6c05 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -37,6 +37,7 @@ #include <SkPath.h> #include <SkPaint.h> +#include <SkPoint.h> #include <SkGeometry.h> // WARNING: Internal Skia Header #include <stdlib.h> @@ -912,6 +913,39 @@ void pushToVector(Vector<Vertex>& vertices, float x, float y) { Vertex::set(newVertex, x, y); } +class ClockwiseEnforcer { +public: + void addPoint(const SkPoint& point) { + double x = point.x(); + double y = point.y(); + + if (initialized) { + sum += (x + lastX) * (y - lastY); + } else { + initialized = true; + } + + lastX = x; + lastY = y; + } + void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) { + if (sum < 0) { + // negative sum implies CounterClockwise + const int size = vertices.size(); + for (int i = 0; i < size / 2; i++) { + Vertex tmp = vertices[i]; + int k = size - 1 - i; + vertices.replaceAt(vertices[k], i); + vertices.replaceAt(tmp, k); + } + } + } +private: + bool initialized = false; + double lastX, lastY; + double sum = 0; +}; + bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices) { @@ -922,18 +956,22 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo SkPath::Iter iter(path, forceClose); SkPoint pts[4]; SkPath::Verb v; + ClockwiseEnforcer clockwiseEnforcer; while (SkPath::kDone_Verb != (v = iter.next(pts))) { switch (v) { case SkPath::kMove_Verb: pushToVector(outputVertices, pts[0].x(), pts[0].y()); ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y()); + clockwiseEnforcer.addPoint(pts[0]); break; case SkPath::kClose_Verb: ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y()); + clockwiseEnforcer.addPoint(pts[0]); break; case SkPath::kLine_Verb: ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y()); pushToVector(outputVertices, pts[1].x(), pts[1].y()); + clockwiseEnforcer.addPoint(pts[1]); break; case SkPath::kQuad_Verb: ALOGV("kQuad_Verb"); @@ -942,6 +980,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[2].x(), pts[2].y(), pts[1].x(), pts[1].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + clockwiseEnforcer.addPoint(pts[1]); + clockwiseEnforcer.addPoint(pts[2]); break; case SkPath::kCubic_Verb: ALOGV("kCubic_Verb"); @@ -951,6 +991,9 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + clockwiseEnforcer.addPoint(pts[1]); + clockwiseEnforcer.addPoint(pts[2]); + clockwiseEnforcer.addPoint(pts[3]); break; case SkPath::kConic_Verb: { ALOGV("kConic_Verb"); @@ -965,6 +1008,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo quads[offset+1].x(), quads[offset+1].y(), sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); } + clockwiseEnforcer.addPoint(pts[1]); + clockwiseEnforcer.addPoint(pts[2]); break; } default: @@ -972,13 +1017,17 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo } } + bool wasClosed = false; int size = outputVertices.size(); if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && outputVertices[0].y == outputVertices[size - 1].y) { outputVertices.pop(); - return true; + wasClosed = true; } - return false; + + // ensure output vector is clockwise + clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices); + return wasClosed; } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h index 8ac9a3b..ccae65b 100644 --- a/libs/hwui/PathTessellator.h +++ b/libs/hwui/PathTessellator.h @@ -82,7 +82,7 @@ public: const mat4& transform, VertexBuffer& vertexBuffer); /** - * Approximates a convex, CW outline into a Vector of 2d vertices. + * 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 diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 9146b68..c2f7234 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -768,31 +768,9 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties(); renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY()); - // If the projection reciever has an outline, we mask each of the projected rendernodes to it - // Either with clipRect, or special saveLayer masking - if (projectionReceiverOutline != nullptr) { - const SkRect& outlineBounds = projectionReceiverOutline->getBounds(); - if (projectionReceiverOutline->isRect(nullptr)) { - // mask to the rect outline simply with clipRect - ClipRectOp* clipOp = new (alloc) ClipRectOp( - outlineBounds.left(), outlineBounds.top(), - outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op); - handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); - } else { - // wrap the projected RenderNodes with a SaveLayer that will mask to the outline - SaveLayerOp* op = new (alloc) SaveLayerOp( - outlineBounds.left(), outlineBounds.top(), - outlineBounds.right(), outlineBounds.bottom(), - 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag); - op->setMask(projectionReceiverOutline); - handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); - - /* TODO: add optimizations here to take advantage of placement/size of projected - * children (which may shrink saveLayer area significantly). This is dependent on - * passing actual drawing/dirtying bounds of projected content down to native. - */ - } - } + // If the projection reciever has an outline, we mask projected content to it + // (which we know, apriori, are all tessellated paths) + renderer.setProjectionPathMask(alloc, projectionReceiverOutline); // draw projected nodes for (size_t i = 0; i < mProjectedNodes.size(); i++) { @@ -807,10 +785,8 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& renderer.restoreToCount(restoreTo); } - if (projectionReceiverOutline != nullptr) { - handler(new (alloc) RestoreToCountOp(restoreTo), - PROPERTY_SAVECOUNT, properties().getClipToBounds()); - } + handler(new (alloc) RestoreToCountOp(restoreTo), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); } /** diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index fb28531..024ff10 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -158,71 +158,6 @@ Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) } return result; } -/** - * Test whether the polygon is order in clockwise. - * - * @param polygon the polygon as a Vector2 array - * @param len the number of points of the polygon - */ -bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) { - if (len < 2 || polygon == nullptr) { - return true; - } - double sum = 0; - double p1x = polygon[len - 1].x; - double p1y = polygon[len - 1].y; - for (int i = 0; i < len; i++) { - - double p2x = polygon[i].x; - double p2y = polygon[i].y; - sum += p1x * p2y - p2x * p1y; - p1x = p2x; - p1y = p2y; - } - return sum < 0; -} - -bool ShadowTessellator::isClockwisePath(const SkPath& path) { - SkPath::Iter iter(path, false); - SkPoint pts[4]; - SkPath::Verb v; - - Vector<Vector2> arrayForDirection; - while (SkPath::kDone_Verb != (v = iter.next(pts))) { - switch (v) { - case SkPath::kMove_Verb: - arrayForDirection.add((Vector2){pts[0].x(), pts[0].y()}); - break; - case SkPath::kLine_Verb: - arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()}); - break; - case SkPath::kConic_Verb: - case SkPath::kQuad_Verb: - arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()}); - arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()}); - break; - case SkPath::kCubic_Verb: - arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()}); - arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()}); - arrayForDirection.add((Vector2){pts[3].x(), pts[3].y()}); - break; - default: - break; - } - } - - return isClockwise(arrayForDirection.array(), arrayForDirection.size()); -} - -void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) { - int n = len / 2; - for (int i = 0; i < n; i++) { - Vertex tmp = polygon[i]; - int k = len - 1 - i; - polygon[i] = polygon[k]; - polygon[k] = tmp; - } -} int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, float divisor) { diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index c04d8ef..5f4c9c5 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -83,23 +83,6 @@ public: static bool isClockwise(const Vector2* polygon, int len); static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2); - /** - * Determine whether the path is clockwise, using the control points. - * - * TODO: Given the skia is using inverted Y coordinate, shadow system needs - * to convert to the same coordinate to avoid the extra reverse. - * - * @param path The path to be examined. - */ - static bool isClockwisePath(const SkPath &path); - - /** - * Reverse the vertex array. - * - * @param polygon The vertex array to be reversed. - * @param len The length of the vertex array. - */ - static void reverseVertexArray(Vertex* polygon, int len); static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, float divisor); diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 9e7faee..beb2e1d 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -36,6 +36,7 @@ Snapshot::Snapshot() , empty(false) , alpha(1.0f) , roundRectClipState(nullptr) + , projectionPathMask(nullptr) , mClipArea(&mClipAreaRoot) { transform = &mTransformRoot; region = nullptr; @@ -54,6 +55,7 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) , empty(false) , alpha(s->alpha) , roundRectClipState(s->roundRectClipState) + , projectionPathMask(s->projectionPathMask) , mClipArea(nullptr) , mViewportData(s->mViewportData) , mRelativeLightCenter(s->mRelativeLightCenter) { @@ -141,6 +143,34 @@ void Snapshot::resetTransform(float x, float y, float z) { transform->loadTranslate(x, y, z); } +void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const { + // build (reverse ordered) list of the stack of snapshots, terminated with a NULL + Vector<const Snapshot*> snapshotList; + snapshotList.push(nullptr); + const Snapshot* current = this; + do { + snapshotList.push(current); + current = current->previous.get(); + } while (current); + + // traverse the list, adding in each transform that contributes to the total transform + outTransform->loadIdentity(); + for (size_t i = snapshotList.size() - 1; i > 0; i--) { + // iterate down the stack + const Snapshot* current = snapshotList[i]; + const Snapshot* next = snapshotList[i - 1]; + if (current->flags & kFlagIsFboLayer) { + // if we've hit a layer, translate by the layer's draw offset + outTransform->translate(current->layer->layer.left, current->layer->layer.top); + } + if (!next || (next->flags & kFlagIsFboLayer)) { + // if this snapshot is last, or if this snapshot is last before an + // FBO layer (which reset the transform), apply it + outTransform->multiply(*(current->transform)); + } + } +} + /////////////////////////////////////////////////////////////////////////////// // Clipping round rect /////////////////////////////////////////////////////////////////////////////// @@ -191,6 +221,18 @@ void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& boun roundRectClipState = state; } +void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) { + if (path) { + ProjectionPathMask* mask = new (allocator) ProjectionPathMask; + mask->projectionMask = path; + buildScreenSpaceTransform(&(mask->projectionMaskTransform)); + + projectionPathMask = mask; + } else { + projectionPathMask = nullptr; + } +} + /////////////////////////////////////////////////////////////////////////////// // Queries /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 4d704ab..af6ad72 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -63,6 +63,17 @@ public: float radius; }; +class ProjectionPathMask { +public: + /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/ + static void* operator new(size_t size, LinearAllocator& allocator) { + return allocator.alloc(size); + } + + const SkPath* projectionMask; + Matrix4 projectionMaskTransform; +}; + /** * A snapshot holds information about the current state of the rendering * surface. A snapshot is usually created whenever the user calls save() @@ -190,6 +201,11 @@ public: float radius, bool highPriority); /** + * Sets (and replaces) the current projection mask + */ + void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path); + + /** * Indicates whether this snapshot should be ignored. A snapshot * is typically ignored if its layer is invisible or empty. */ @@ -201,6 +217,12 @@ public: bool hasPerspectiveTransform() const; /** + * Fills outTransform with the current, total transform to screen space, + * across layer boundaries. + */ + void buildScreenSpaceTransform(Matrix4* outTransform) const; + + /** * Dirty flags. */ int flags; @@ -272,6 +294,11 @@ public: */ const RoundRectClipState* roundRectClipState; + /** + * Current projection masking path - used exclusively to mask tessellated circles. + */ + const ProjectionPathMask* projectionPathMask; + void dump() const; private: diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index fc173f7..704a691 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -207,6 +207,16 @@ static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* t transformXY->mapPoint(point.x, point.y); } +static void reverseVertexArray(Vertex* polygon, int len) { + int n = len / 2; + for (int i = 0; i < n; i++) { + Vertex tmp = polygon[i]; + int k = len - 1 - i; + polygon[i] = polygon[k]; + polygon[k] = tmp; + } +} + static void tessellateShadows( const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque, const SkPath* casterPerimeter, @@ -219,10 +229,9 @@ static void tessellateShadows( const float casterRefinementThresholdSquared = 4.0f; PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThresholdSquared, casterVertices2d); - if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) { - ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(), - casterVertices2d.size()); - } + + // Shadow requires CCW for now. TODO: remove potential double-reverse + reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size()); if (casterVertices2d.size() == 0) return; |