summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/CanvasState.cpp3
-rw-r--r--libs/hwui/CanvasState.h1
-rw-r--r--libs/hwui/DeferredDisplayList.cpp1
-rw-r--r--libs/hwui/DeferredDisplayList.h1
-rw-r--r--libs/hwui/Matrix.h6
-rw-r--r--libs/hwui/OpenGLRenderer.cpp46
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h1
-rw-r--r--libs/hwui/PathTessellator.cpp53
-rw-r--r--libs/hwui/PathTessellator.h2
-rw-r--r--libs/hwui/RenderNode.cpp34
-rw-r--r--libs/hwui/ShadowTessellator.cpp65
-rw-r--r--libs/hwui/ShadowTessellator.h17
-rw-r--r--libs/hwui/Snapshot.cpp42
-rw-r--r--libs/hwui/Snapshot.h27
-rw-r--r--libs/hwui/TessellationCache.cpp17
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;