diff options
author | Chris Craik <ccraik@google.com> | 2014-05-16 00:37:28 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-05-16 00:37:29 +0000 |
commit | c9ee304c91b5572f0c1cd95e9bd1e4a84e28587d (patch) | |
tree | bfa9e5965d749b815ac7e55cea6b7b204db70cca /libs | |
parent | 6c536eac59adf6e11694ba3800222ad03fe93d1f (diff) | |
parent | deeda3d337aed1eee218b89a7aba5992ced371f0 (diff) | |
download | frameworks_base-c9ee304c91b5572f0c1cd95e9bd1e4a84e28587d.zip frameworks_base-c9ee304c91b5572f0c1cd95e9bd1e4a84e28587d.tar.gz frameworks_base-c9ee304c91b5572f0c1cd95e9bd1e4a84e28587d.tar.bz2 |
Merge "Round rect outline clipping"
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/DeferredDisplayList.cpp | 11 | ||||
-rw-r--r-- | libs/hwui/DeferredDisplayList.h | 1 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 47 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 2 | ||||
-rw-r--r-- | libs/hwui/Outline.h | 13 | ||||
-rw-r--r-- | libs/hwui/Program.h | 28 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 50 | ||||
-rw-r--r-- | libs/hwui/Rect.h | 2 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/Snapshot.cpp | 50 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 42 | ||||
-rw-r--r-- | libs/hwui/StatefulBaseRenderer.cpp | 20 | ||||
-rw-r--r-- | libs/hwui/StatefulBaseRenderer.h | 10 | ||||
-rw-r--r-- | libs/hwui/utils/MathUtils.h | 4 |
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 d4ff4a3..f0645a9 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -667,6 +667,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; } |