summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
authorChris Craik <ccraik@google.com>2014-05-05 19:09:33 -0700
committerChris Craik <ccraik@google.com>2014-05-15 16:36:12 -0700
commitdeeda3d337aed1eee218b89a7aba5992ced371f0 (patch)
tree15e13e84727baebce58b735e34ef5d198fd84389 /libs/hwui
parentfe4c1e225d147fe9cb5d7c121b7d6d11a312844e (diff)
downloadframeworks_base-deeda3d337aed1eee218b89a7aba5992ced371f0.zip
frameworks_base-deeda3d337aed1eee218b89a7aba5992ced371f0.tar.gz
frameworks_base-deeda3d337aed1eee218b89a7aba5992ced371f0.tar.bz2
Round rect outline clipping
Change-Id: Iee9cf4f719f6f1917507b69189ad114fa365917b
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/DeferredDisplayList.cpp11
-rw-r--r--libs/hwui/DeferredDisplayList.h1
-rw-r--r--libs/hwui/OpenGLRenderer.cpp47
-rw-r--r--libs/hwui/OpenGLRenderer.h2
-rw-r--r--libs/hwui/Outline.h13
-rw-r--r--libs/hwui/Program.h28
-rw-r--r--libs/hwui/ProgramCache.cpp50
-rw-r--r--libs/hwui/Rect.h2
-rw-r--r--libs/hwui/RenderNode.cpp4
-rw-r--r--libs/hwui/Snapshot.cpp50
-rw-r--r--libs/hwui/Snapshot.h42
-rw-r--r--libs/hwui/StatefulBaseRenderer.cpp20
-rw-r--r--libs/hwui/StatefulBaseRenderer.h10
-rw-r--r--libs/hwui/utils/MathUtils.h4
14 files changed, 250 insertions, 34 deletions
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 45b6624..3016814 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -28,6 +28,7 @@
#include "DeferredDisplayList.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"
+#include "utils/MathUtils.h"
#if DEBUG_DEFER
#define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
@@ -146,10 +147,6 @@ private:
mergeid_t mMergeId;
};
-// compare alphas approximately, with a small margin
-#define NEQ_FALPHA(lhs, rhs) \
- fabs((float)lhs - (float)rhs) > 0.001f
-
class MergingDrawBatch : public DrawBatch {
public:
MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
@@ -196,7 +193,11 @@ public:
const DeferredDisplayState* lhs = state;
const DeferredDisplayState* rhs = mOps[0].state;
- if (NEQ_FALPHA(lhs->mAlpha, rhs->mAlpha)) return false;
+ if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false;
+
+ // 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;
/* Clipping compatibility check
*
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index fca3588..48489c2 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -64,6 +64,7 @@ public:
mat4 mMatrix;
DrawModifiers mDrawModifiers;
float mAlpha;
+ const RoundRectClipState* mRoundRectClipState;
};
class OpStatePair {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 4df97e6..7993c0f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -711,6 +711,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
+ mSnapshot->roundRectClipState = NULL;
}
}
@@ -844,6 +845,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight());
+ mSnapshot->roundRectClipState = NULL;
endTiling();
debugOverdraw(false, false);
@@ -872,8 +874,6 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
// Change the ortho projection
glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
-
-
return true;
}
@@ -892,7 +892,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
bool clipRequired = false;
calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
- &clipRequired, false); // safely ignore return, should never be rejected
+ &clipRequired, NULL, false); // safely ignore return, should never be rejected
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
if (fboLayer) {
@@ -1367,6 +1367,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
state.mMatrix.load(*currentMatrix);
state.mDrawModifiers = mDrawModifiers;
state.mAlpha = currentSnapshot()->alpha;
+
+ // always store/restore, since it's just a pointer
+ state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
return false;
}
@@ -1374,6 +1377,7 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool
setMatrix(state.mMatrix);
mSnapshot->alpha = state.mAlpha;
mDrawModifiers = state.mDrawModifiers;
+ mSnapshot->roundRectClipState = state.mRoundRectClipState;
if (state.mClipValid && !skipClipRestore) {
mSnapshot->setClip(state.mClip.left, state.mClip.top,
@@ -1449,7 +1453,7 @@ void OpenGLRenderer::setStencilFromClip() {
mCaches.stencil.enableWrite();
- // Clear the stencil but first make sure we restrict drawing
+ // Clear and update the stencil, but first make sure we restrict drawing
// to the region's bounds
bool resetScissor = mCaches.enableScissor();
if (resetScissor) {
@@ -1457,7 +1461,10 @@ void OpenGLRenderer::setStencilFromClip() {
setScissorFromClip();
}
mCaches.stencil.clear();
- if (resetScissor) mCaches.disableScissor();
+
+ // stash and disable the outline clip state, since stencil doesn't account for outline
+ bool storedSkipOutlineClip = mSkipOutlineClip;
+ mSkipOutlineClip = true;
SkPaint paint;
paint.setColor(0xff000000);
@@ -1470,6 +1477,8 @@ void OpenGLRenderer::setStencilFromClip() {
// The last parameter is important: we are not drawing in the color buffer
// so we don't want to dirty the current layer, if any
drawRegionRects(*(currentSnapshot()->clipRegion), paint, false);
+ if (resetScissor) mCaches.disableScissor();
+ mSkipOutlineClip = storedSkipOutlineClip;
mCaches.stencil.enableTest();
@@ -1494,7 +1503,6 @@ void OpenGLRenderer::setStencilFromClip() {
*/
bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom,
const SkPaint* paint) {
- bool clipRequired = false;
bool snapOut = paint && paint->isAntiAlias();
if (paint && paint->getStyle() != SkPaint::kFill_Style) {
@@ -1505,13 +1513,17 @@ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right,
bottom += outset;
}
- if (calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, snapOut)) {
+ bool clipRequired = false;
+ bool roundRectClipRequired = false;
+ if (calculateQuickRejectForScissor(left, top, right, bottom,
+ &clipRequired, &roundRectClipRequired, snapOut)) {
return true;
}
if (!isRecording()) {
// not quick rejected, so enable the scissor if clipRequired
mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+ mSkipOutlineClip = !roundRectClipRequired;
}
return false;
}
@@ -1668,6 +1680,18 @@ void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool sw
void OpenGLRenderer::setupDrawProgram() {
useProgram(mCaches.programCache.get(mDescription));
+ if (mDescription.hasRoundRectClip) {
+ // TODO: avoid doing this repeatedly, stashing state pointer in program
+ const RoundRectClipState* state = mSnapshot->roundRectClipState;
+ const Rect& innerRect = state->outlineInnerRect;
+ glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"),
+ innerRect.left, innerRect.top,
+ innerRect.right, innerRect.bottom);
+ glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"),
+ state->outlineRadius);
+ glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"),
+ 1, GL_FALSE, &state->matrix.data[0]);
+ }
}
void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
@@ -2902,7 +2926,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
bool clipRequired = false;
const bool rejected = calculateQuickRejectForScissor(x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, false);
+ x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, NULL, false);
if (rejected) {
if (transform && !transform->isIdentity()) {
@@ -3433,6 +3457,13 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
ProgramDescription& description, bool swapSrcDst) {
+
+ if (mSnapshot->roundRectClipState != NULL /*&& !mSkipOutlineClip*/) {
+ blend = true;
+ mDescription.hasRoundRectClip = true;
+ }
+ mSkipOutlineClip = true;
+
if (mCountOverdraw) {
if (!mCaches.blend) glEnable(GL_BLEND);
if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index b58b817..f70ae58 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -993,6 +993,8 @@ private:
bool mCountOverdraw;
float mOverdraw;
+ bool mSkipOutlineClip;
+
friend class DisplayListRenderer;
friend class Layer;
friend class TextSetupFunctor;
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 530be30..5c24335 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -58,11 +58,24 @@ public:
mShouldClip = clip;
}
+ bool getShouldClip() const {
+ return mShouldClip;
+ }
+
bool willClip() const {
// only round rect outlines can be used for clipping
return mShouldClip && (mType == kOutlineType_RoundRect);
}
+ bool getAsRoundRect(Rect* outRect, float* outRadius) const {
+ if (mType == kOutlineType_RoundRect) {
+ outRect->set(mBounds);
+ *outRadius = mRadius;
+ return true;
+ }
+ return false;
+ }
+
const SkPath* getPath() const {
if (mType == kOutlineType_None) return NULL;
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 33c91b3..3e191d0 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -45,17 +45,18 @@ namespace uirenderer {
#define COLOR_COMPONENT_THRESHOLD 1.0f
#define COLOR_COMPONENT_INV_THRESHOLD 0.0f
-#define PROGRAM_KEY_TEXTURE 0x1
-#define PROGRAM_KEY_A8_TEXTURE 0x2
-#define PROGRAM_KEY_BITMAP 0x4
-#define PROGRAM_KEY_GRADIENT 0x8
-#define PROGRAM_KEY_BITMAP_FIRST 0x10
-#define PROGRAM_KEY_COLOR_MATRIX 0x20
-#define PROGRAM_KEY_COLOR_BLEND 0x40
-#define PROGRAM_KEY_BITMAP_NPOT 0x80
-#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
-
-#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
+#define PROGRAM_KEY_TEXTURE 0x01
+#define PROGRAM_KEY_A8_TEXTURE 0x02
+#define PROGRAM_KEY_BITMAP 0x04
+#define PROGRAM_KEY_GRADIENT 0x08
+#define PROGRAM_KEY_BITMAP_FIRST 0x10
+#define PROGRAM_KEY_COLOR_MATRIX 0x20
+#define PROGRAM_KEY_COLOR_BLEND 0x40
+#define PROGRAM_KEY_BITMAP_NPOT 0x80
+
+#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
+
+#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
// Encode the xfermodes on 6 bits
@@ -83,6 +84,7 @@ namespace uirenderer {
#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
#define PROGRAM_EMULATE_STENCIL 43
+#define PROGRAM_HAS_ROUND_RECT_CLIP 44
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -158,6 +160,7 @@ struct ProgramDescription {
bool hasDebugHighlight;
bool emulateStencil;
+ bool hasRoundRectClip;
/**
* Resets this description. All fields are reset back to the default
@@ -198,6 +201,8 @@ struct ProgramDescription {
gamma = 2.2f;
hasDebugHighlight = false;
+ emulateStencil = false;
+ hasRoundRectClip = false;
}
/**
@@ -264,6 +269,7 @@ struct ProgramDescription {
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL;
+ if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP;
return key;
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 6d50410..f451690 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -58,6 +58,8 @@ const char* gVS_Header_Uniforms_HasGradient =
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform mediump vec2 textureDimension;\n";
+const char* gVS_Header_Uniforms_HasRoundRectClip =
+ "uniform mat4 roundRectInvTransform;\n";
const char* gVS_Header_Varyings_HasTexture =
"varying vec2 outTexCoords;\n";
const char* gVS_Header_Varyings_HasColors =
@@ -85,6 +87,8 @@ const char* gVS_Header_Varyings_HasGradient[6] = {
"varying highp vec2 sweep;\n"
"varying vec2 ditherTexCoords;\n",
};
+const char* gVS_Header_Varyings_HasRoundRectClip =
+ "varying vec2 roundRectPos;\n";
const char* gVS_Main =
"\nvoid main(void) {\n";
const char* gVS_Main_OutTexCoords =
@@ -115,9 +119,12 @@ const char* gVS_Main_OutGradient[6] = {
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
- " gl_Position = projection * transform * position;\n";
+ " vec4 transformedPosition = projection * transform * position;\n"
+ " gl_Position = transformedPosition;\n";
const char* gVS_Main_AAVertexShape =
" alpha = vtxAlpha;\n";
+const char* gVS_Main_HasRoundRectClip =
+ " roundRectPos = (roundRectInvTransform * transformedPosition).xy;\n";
const char* gVS_Footer =
"}\n\n";
@@ -160,6 +167,10 @@ const char* gFS_Uniforms_ColorOp[3] = {
const char* gFS_Uniforms_Gamma =
"uniform float gamma;\n";
+const char* gFS_Uniforms_HasRoundRectClip =
+ "uniform vec4 roundRectInnerRectLTRB;\n"
+ "uniform float roundRectRadius;\n";
+
const char* gFS_Main =
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
@@ -318,6 +329,15 @@ const char* gFS_Main_ApplyColorOp[3] = {
// PorterDuff
" fragColor = blendColors(colorBlend, fragColor);\n"
};
+
+// Note: LTRB -> xyzw
+const char* gFS_Main_FragColor_HasRoundRectClip =
+ " mediump vec2 fragToLT = roundRectInnerRectLTRB.xy - roundRectPos;\n"
+ " mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTRB.zw;\n"
+ " mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0));\n"
+ " mediump float linearDist = roundRectRadius - length(dist);\n"
+ " gl_FragColor *= clamp(linearDist, 0.0, 1.0);\n";
+
const char* gFS_Main_DebugHighlight =
" gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n";
const char* gFS_Main_EmulateStencil =
@@ -462,6 +482,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Header_Uniforms_HasRoundRectClip);
+ }
// Varyings
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
@@ -478,6 +501,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasBitmap) {
shader.append(gVS_Header_Varyings_HasBitmap);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Header_Varyings_HasRoundRectClip);
+ }
// Begin the shader
shader.append(gVS_Main); {
@@ -500,6 +526,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasGradient) {
shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Main_HasRoundRectClip);
+ }
}
// End the shader
shader.append(gVS_Footer);
@@ -546,6 +575,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasBitmap) {
shader.append(gVS_Header_Varyings_HasBitmap);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gVS_Header_Varyings_HasRoundRectClip);
+ }
// Uniforms
int modulateOp = MODULATE_OP_NO_MODULATE;
@@ -568,11 +600,18 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasGammaCorrection) {
shader.append(gFS_Uniforms_Gamma);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gFS_Uniforms_HasRoundRectClip);
+ }
// Optimization for common cases
- if (!description.isAA && !blendFramebuffer && !description.hasColors &&
- description.colorOp == ProgramDescription::kColorNone &&
- !description.hasDebugHighlight && !description.emulateStencil) {
+ if (!description.isAA
+ && !blendFramebuffer
+ && !description.hasColors
+ && description.colorOp == ProgramDescription::kColorNone
+ && !description.hasDebugHighlight
+ && !description.emulateStencil
+ && !description.hasRoundRectClip) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -722,6 +761,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasColors) {
shader.append(gFS_Main_FragColor_HasColors);
}
+ if (description.hasRoundRectClip) {
+ shader.append(gFS_Main_FragColor_HasRoundRectClip);
+ }
if (description.hasDebugHighlight) {
shader.append(gFS_Main_DebugHighlight);
}
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index f38d8b7..2ddbbd7 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -234,7 +234,7 @@ public:
bottom = ceilf(bottom);
}
- void dump(const char* label) const {
+ void dump(const char* label = NULL) const {
ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fba482d..ad48f44 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -653,6 +653,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
+ if (mProperties.getOutline().willClip()) {
+ renderer.setClippingOutline(alloc, &(mProperties.getOutline()));
+ }
+
Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
buildZSortedChildList(zTranslatedNodes);
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 029b56d..80f7eca 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -20,6 +20,8 @@
#include <SkCanvas.h>
+#include "utils/MathUtils.h"
+
namespace android {
namespace uirenderer {
@@ -34,7 +36,8 @@ Snapshot::Snapshot()
, fbo(0)
, invisible(false)
, empty(false)
- , alpha(1.0f) {
+ , alpha(1.0f)
+ , roundRectClipState(NULL) {
transform = &mTransformRoot;
clipRect = &mClipRectRoot;
region = NULL;
@@ -53,8 +56,8 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
, invisible(s->invisible)
, empty(false)
, alpha(s->alpha)
+ , roundRectClipState(s->roundRectClipState)
, mViewportData(s->mViewportData) {
-
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
mTransformRoot.load(*s->transform);
transform = &mTransformRoot;
@@ -204,6 +207,49 @@ void Snapshot::resetTransform(float x, float y, float z) {
}
///////////////////////////////////////////////////////////////////////////////
+// Clipping outline
+///////////////////////////////////////////////////////////////////////////////
+
+void Snapshot::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
+ Rect bounds;
+ float radius;
+ if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported
+
+ if (!MathUtils::isPositive(radius)) return; // leave clipping up to rect clipping
+
+ RoundRectClipState* state = new (allocator) RoundRectClipState;
+
+ // store the inverse drawing matrix
+ Matrix4 outlineDrawingMatrix;
+ outlineDrawingMatrix.load(getOrthoMatrix());
+ outlineDrawingMatrix.multiply(*transform);
+ state->matrix.loadInverse(outlineDrawingMatrix);
+
+ // compute area under rounded corners - only draws overlapping these rects need to be clipped
+ for (int i = 0 ; i < 4; i++) {
+ state->dangerRects[i] = bounds;
+ }
+ state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
+ state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
+ state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
+ state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
+ for (int i = 0; i < 4; i++) {
+ transform->mapRect(state->dangerRects[i]);
+
+ // round danger rects out as though they are AA geometry (since they essentially are)
+ state->dangerRects[i].snapGeometryToPixelBoundaries(true);
+ }
+
+ // store RR area
+ bounds.inset(radius);
+ state->outlineInnerRect = bounds;
+ state->outlineRadius = radius;
+
+ // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
+ roundRectClipState = state;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Queries
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index e9ab1ff..435736c 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -20,6 +20,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <utils/LinearAllocator.h>
#include <utils/RefBase.h>
#include <ui/Region.h>
@@ -27,12 +28,40 @@
#include "Layer.h"
#include "Matrix.h"
+#include "Outline.h"
#include "Rect.h"
+#include "utils/Macros.h"
namespace android {
namespace uirenderer {
/**
+ * Temporary structure holding information for a single outline clip.
+ *
+ * These structures are treated as immutable once created, and only exist for a single frame, which
+ * is why they may only be allocated with a LinearAllocator.
+ */
+class RoundRectClipState {
+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);
+ }
+
+ bool areaRequiresRoundRectClip(const Rect& rect) const {
+ return rect.intersects(dangerRects[0])
+ || rect.intersects(dangerRects[1])
+ || rect.intersects(dangerRects[2])
+ || rect.intersects(dangerRects[3]);
+ }
+
+ Matrix4 matrix;
+ Rect dangerRects[4];
+ Rect outlineInnerRect;
+ float outlineRadius;
+};
+
+/**
* A snapshot holds information about the current state of the rendering
* surface. A snapshot is usually created whenever the user calls save()
* and discarded when the user calls restore(). Once a snapshot is created,
@@ -133,6 +162,11 @@ public:
const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
/**
+ * Sets (and replaces) the current clipping outline
+ */
+ void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
+
+ /**
* Indicates whether this snapshot should be ignored. A snapshot
* is typicalled ignored if its layer is invisible or empty.
*/
@@ -225,6 +259,14 @@ public:
*/
float alpha;
+ /**
+ * Current clipping round rect.
+ *
+ * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips,
+ * never modified.
+ */
+ const RoundRectClipState* roundRectClipState;
+
void dump() const;
private:
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index aa83e20..7d299f0 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
#include <SkCanvas.h>
#include "StatefulBaseRenderer.h"
@@ -180,6 +182,10 @@ bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
return !mSnapshot->clipRect->isEmpty();
}
+void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
+ mSnapshot->setClippingOutline(allocator, outline);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Quick Rejection
///////////////////////////////////////////////////////////////////////////////
@@ -195,7 +201,9 @@ bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
* See Rect::snapGeometryToPixelBoundaries()
*/
bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
- float right, float bottom, bool* clipRequired, bool snapOut) const {
+ float right, float bottom,
+ bool* clipRequired, bool* roundRectClipRequired,
+ bool snapOut) const {
if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
return true;
}
@@ -210,7 +218,15 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top,
if (!clipRect.intersects(r)) return true;
// clip is required if geometry intersects clip rect
- if (clipRequired) *clipRequired = !clipRect.contains(r);
+ if (clipRequired) {
+ *clipRequired = !clipRect.contains(r);
+ }
+
+ // round rect clip is required if RR clip exists, and geometry intersects its corners
+ if (roundRectClipRequired) {
+ *roundRectClipRequired = mSnapshot->roundRectClipState != NULL
+ && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
+ }
return false;
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 9fbf2ca..2e7f279 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -88,6 +88,14 @@ public:
virtual bool clipPath(const SkPath* path, SkRegion::Op op);
virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+ /**
+ * Does not support different clipping Ops (that is, every call to setClippingOutline is
+ * effectively using SkRegion::kReplaceOp)
+ *
+ * The clipping outline is independent from the regular clip.
+ */
+ void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
+
protected:
const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
@@ -106,7 +114,7 @@ protected:
// Clip
bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
- bool* clipRequired, bool snapOut) const;
+ bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const;
/**
* Called just after a restore has occurred. The 'removed' snapshot popped from the stack,
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 1a7082b..997acde2 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -34,6 +34,10 @@ public:
return value >= gNonZeroEpsilon;
}
+ inline static bool areEqual(float valueA, float valueB) {
+ return isZero(valueA - valueB);
+ }
+
inline static int min(int a, int b) {
return a < b ? a : b;
}