Add private circular reveal API on View/RenderNode
Change-Id: I139c8e12b354083149a665f6768f3f6931a8dd15
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 549b786..9e367fc 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -21,6 +21,8 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <SkPath.h>
+#include <SkPathOps.h>
#include <SkXfermode.h>
#include <private/hwui/DrawGlInfo.h>
@@ -1550,20 +1552,27 @@
*/
class DrawShadowOp : public DrawOp {
public:
- DrawShadowOp(const mat4& transformXY, const mat4& transformZ, float alpha, const SkPath* outline,
- float fallbackWidth, float fallbackHeight)
- : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ),
- mAlpha(alpha), mOutline(outline),
- mFallbackWidth(fallbackWidth), mFallbackHeight(fallbackHeight) {}
+ DrawShadowOp(const mat4& transformXY, const mat4& transformZ, float alpha,
+ float fallbackWidth, float fallbackHeight,
+ const SkPath* outline, const SkPath* revealClip)
+ : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ), mAlpha(alpha),
+ mFallbackWidth(fallbackWidth), mFallbackHeight(fallbackHeight),
+ mOutline(outline), mRevealClip(revealClip) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- if (mOutline->isEmpty()) {
- SkPath fakeOutline;
- fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
- return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, &fakeOutline);
+ SkPath casterPerimeter;
+ if (!mOutline || mOutline->isEmpty()) {
+ casterPerimeter.addRect(0, 0, mFallbackWidth, mFallbackHeight);
+ } else {
+ casterPerimeter = *mOutline;
}
- return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, mOutline);
+ if (mRevealClip) {
+ // intersect the outline with the convex reveal clip
+ Op(casterPerimeter, *mRevealClip, kIntersect_PathOp, &casterPerimeter);
+ }
+
+ return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, &casterPerimeter);
}
virtual void output(int level, uint32_t logFlags) const {
@@ -1576,9 +1585,12 @@
const mat4 mTransformXY;
const mat4 mTransformZ;
const float mAlpha;
- const SkPath* mOutline;
const float mFallbackWidth;
const float mFallbackHeight;
+
+ // these point at convex SkPaths owned by RenderProperties, or null
+ const SkPath* mOutline;
+ const SkPath* mRevealClip;
};
class DrawLayerOp : public DrawOp {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cdd789d..d808735 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1919,6 +1919,7 @@
// will be performed by the display list itself
if (displayList && displayList->isRenderable()) {
// compute 3d ordering
+ displayList->updateProperties();
displayList->computeOrdering();
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
status = startFrame();
@@ -3202,7 +3203,7 @@
}
status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
- float casterAlpha, const SkPath* casterOutline) {
+ float casterAlpha, const SkPath* casterPerimeter) {
if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
// TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3214,7 +3215,7 @@
// tessellate caster outline into a 2d polygon
Vector<Vertex> casterVertices2d;
const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
- PathTessellator::approximatePathOutlineVertices(*casterOutline,
+ PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
casterRefinementThresholdSquared, casterVertices2d);
if (casterVertices2d.size() == 0) {
@@ -3256,7 +3257,7 @@
// We only have ortho projection, so we can just ignore the Z in caster for
// simple rejection calculation.
Rect localClip = mSnapshot->getLocalClip();
- Rect casterBounds(casterOutline->getBounds());
+ Rect casterBounds(casterPerimeter->getBounds());
casterTransformXY.mapRect(casterBounds);
bool isCasterOpaque = (casterAlpha == 1.0f);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 059c64f..054767e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -210,7 +210,7 @@
virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
- float casterAlpha, const SkPath* casterOutline);
+ float casterAlpha, const SkPath* casterPerimeter);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 4e496e7..530be30 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -58,7 +58,6 @@
mShouldClip = clip;
}
-
bool willClip() const {
// only round rect outlines can be used for clipping
return mShouldClip && (mType == kOutlineType_RoundRect);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d3daec8..e39e5ae 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -152,18 +152,21 @@
}
SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
- 0, 0, properties().getWidth(), properties().getHeight(), properties().getAlpha() * 255, saveFlags);
+ 0, 0, properties().getWidth(), properties().getHeight(),
+ properties().getAlpha() * 255, saveFlags);
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
}
if (clipToBoundsNeeded) {
- ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
- properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op);
+ ClipRectOp* op = new (handler.allocator()) ClipRectOp(
+ 0, 0, properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op);
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
- if (CC_UNLIKELY(properties().getOutline().willClip())) {
- ClipPathOp* op = new (handler.allocator()) ClipPathOp(properties().getOutline().getPath(),
- SkRegion::kIntersect_Op);
+
+ if (CC_UNLIKELY(properties().hasClippingPath())) {
+ // TODO: optimize for round rect/circle clipping
+ const SkPath* path = properties().getClippingPath();
+ ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op);
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
}
@@ -408,10 +411,15 @@
mat4 shadowMatrixZ(casterOp->mTransformFromParent);
caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+ const SkPath* outlinePath = caster->properties().getOutline().getPath();
+ const RevealClip& revealClip = caster->properties().getRevealClip();
+ const SkPath* revealClipPath = revealClip.hasConvexClip()
+ ? revealClip.getPath() : NULL; // only pass the reveal clip's path if it's convex
+
DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
- shadowMatrixXY, shadowMatrixZ,
- caster->properties().getAlpha(), caster->properties().getOutline().getPath(),
- caster->properties().getWidth(), caster->properties().getHeight());
+ shadowMatrixXY, shadowMatrixZ, caster->properties().getAlpha(),
+ caster->properties().getWidth(), caster->properties().getHeight(),
+ outlinePath, revealClipPath);
handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
@@ -498,7 +506,8 @@
setViewProperties<T>(renderer, handler, level + 1);
- bool quickRejected = properties().getClipToBounds() && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
+ bool quickRejected = properties().getClipToBounds()
+ && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
buildZSortedChildList(zTranslatedNodes);
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ca291a6..3975a76 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -22,6 +22,8 @@
#include <SkCanvas.h>
#include <SkMatrix.h>
+#include <SkPath.h>
+#include <SkPathOps.h>
#include "Matrix.h"
@@ -51,13 +53,15 @@
RenderProperties::ComputedFields::ComputedFields()
: mTransformMatrix(NULL)
, mTransformCamera(NULL)
- , mTransformMatrix3D(NULL) {
+ , mTransformMatrix3D(NULL)
+ , mClipPath(NULL) {
}
RenderProperties::ComputedFields::~ComputedFields() {
delete mTransformMatrix;
delete mTransformCamera;
delete mTransformMatrix3D;
+ delete mClipPath;
}
RenderProperties::RenderProperties()
@@ -80,6 +84,7 @@
// Update the computed fields
updateMatrix();
+ updateClipPath();
}
return *this;
}
@@ -181,5 +186,39 @@
}
}
+void RenderProperties::updateClipPath() {
+ const SkPath* outlineClipPath = mPrimitiveFields.mOutline.willClip()
+ ? mPrimitiveFields.mOutline.getPath() : NULL;
+ const SkPath* revealClipPath = mPrimitiveFields.mRevealClip.getPath();
+
+ if (!outlineClipPath && !revealClipPath) {
+ // mComputedFields.mClipPath doesn't need to be updated, since it won't be used
+ return;
+ }
+
+ if (mComputedFields.mClipPath == NULL) {
+ mComputedFields.mClipPath = new SkPath();
+ }
+ SkPath* clipPath = mComputedFields.mClipPath;
+ mComputedFields.mClipPathOp = SkRegion::kIntersect_Op;
+
+ if (outlineClipPath && revealClipPath) {
+ SkPathOp op = kIntersect_PathOp;
+ if (mPrimitiveFields.mRevealClip.isInverseClip()) {
+ op = kDifference_PathOp; // apply difference step in the Op below, instead of draw time
+ }
+
+ Op(*outlineClipPath, *revealClipPath, op, clipPath);
+ } else if (outlineClipPath) {
+ *clipPath = *outlineClipPath;
+ } else {
+ *clipPath = *revealClipPath;
+ if (mPrimitiveFields.mRevealClip.isInverseClip()) {
+ // apply difference step at draw time
+ mComputedFields.mClipPathOp = SkRegion::kDifference_Op;
+ }
+ }
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 504196d..061e469 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -22,8 +22,10 @@
#include <SkCamera.h>
#include <SkMatrix.h>
+#include <SkRegion.h>
#include "Rect.h"
+#include "RevealClip.h"
#include "Outline.h"
#define TRANSLATION 0x0001
@@ -34,7 +36,6 @@
class SkBitmap;
class SkPaint;
-class SkRegion;
namespace android {
namespace uirenderer {
@@ -415,6 +416,10 @@
return mPrimitiveFields.mOutline;
}
+ const RevealClip& getRevealClip() const {
+ return mPrimitiveFields.mRevealClip;
+ }
+
bool getProjectBackwards() const {
return mPrimitiveFields.mProjectBackwards;
}
@@ -423,10 +428,29 @@
ANDROID_API void updateMatrix();
+ ANDROID_API void updateClipPath();
+
+ // signals that mComputedFields.mClipPath is up to date, and should be used for clipping
+ bool hasClippingPath() const {
+ return mPrimitiveFields.mOutline.willClip() || mPrimitiveFields.mRevealClip.willClip();
+ }
+
+ const SkPath* getClippingPath() const {
+ return hasClippingPath() ? mComputedFields.mClipPath : NULL;
+ }
+
+ SkRegion::Op getClippingPathOp() const {
+ return mComputedFields.mClipPathOp;
+ }
+
Outline& mutableOutline() {
return mPrimitiveFields.mOutline;
}
+ RevealClip& mutableRevealClip() {
+ return mPrimitiveFields.mRevealClip;
+ }
+
private:
void onTranslationUpdate() {
mPrimitiveFields.mMatrixDirty = true;
@@ -442,6 +466,7 @@
PrimitiveFields();
Outline mOutline;
+ RevealClip mRevealClip;
bool mClipToBounds;
bool mProjectBackwards;
bool mProjectionReceiver;
@@ -483,6 +508,8 @@
Matrix4* mTransformMatrix;
Sk3DView* mTransformCamera;
SkMatrix* mTransformMatrix3D;
+ SkPath* mClipPath; // TODO: remove this, create new ops for efficient/special case clipping
+ SkRegion::Op mClipPathOp;
} mComputedFields;
};
diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h
new file mode 100644
index 0000000..ece8498
--- /dev/null
+++ b/libs/hwui/RevealClip.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef REVEALCLIP_H
+#define REVEALCLIP_H
+
+#include <SkPath.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class RevealClip {
+public:
+ RevealClip()
+ : mShouldClip(false)
+ , mInverseClip(false)
+ , mX(0)
+ , mY(0)
+ , mRadius(0) {}
+
+ void set(bool shouldClip, bool inverseClip, float x, float y, float radius) {
+ mShouldClip = shouldClip;
+ mInverseClip = inverseClip;
+ mX = x;
+ mY = y;
+ mRadius = radius;
+
+ mPath.rewind();
+ if (mShouldClip) {
+ mPath.addCircle(x, y, radius);
+ }
+ }
+
+ bool hasConvexClip() const {
+ return mShouldClip && !mInverseClip;
+ }
+
+ bool isInverseClip() const {
+ return mInverseClip;
+ }
+
+ bool willClip() const {
+ return mShouldClip;
+ }
+
+ const SkPath* getPath() const {
+ if (!mShouldClip) return NULL;
+
+ return &mPath;
+ }
+
+private:
+ bool mShouldClip;
+ bool mInverseClip;
+ float mX;
+ float mY;
+ float mRadius;
+ SkPath mPath;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* REVEALCLIP_H */