diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Animator.cpp | 159 | ||||
-rw-r--r-- | libs/hwui/Animator.h | 112 | ||||
-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/PixelBuffer.h | 19 | ||||
-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 | 38 | ||||
-rw-r--r-- | libs/hwui/RenderNode.h | 28 | ||||
-rw-r--r-- | libs/hwui/Snapshot.cpp | 50 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 44 | ||||
-rw-r--r-- | libs/hwui/StatefulBaseRenderer.cpp | 20 | ||||
-rw-r--r-- | libs/hwui/StatefulBaseRenderer.h | 10 | ||||
-rw-r--r-- | libs/hwui/TreeInfo.h | 4 | ||||
-rw-r--r-- | libs/hwui/font/Font.cpp | 3 | ||||
-rw-r--r-- | libs/hwui/utils/MathUtils.h | 4 |
20 files changed, 449 insertions, 196 deletions
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index a033f86..b80f7e9 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -27,31 +27,64 @@ namespace android { namespace uirenderer { /************************************************************ - * Base animator + * BaseRenderNodeAnimator ************************************************************/ -BaseAnimator::BaseAnimator() - : mInterpolator(0) - , mPlayState(PENDING) +BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) + : mFinalValue(finalValue) + , mDeltaValue(0) + , mFromValue(0) + , mInterpolator(0) + , mPlayState(NEEDS_START) , mStartTime(0) - , mDuration(300) { + , mDelayUntil(0) + , mDuration(300) + , mStartDelay(0) { } -BaseAnimator::~BaseAnimator() { +BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { setInterpolator(NULL); } -void BaseAnimator::setInterpolator(Interpolator* interpolator) { +void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) { delete mInterpolator; mInterpolator = interpolator; } -void BaseAnimator::setDuration(nsecs_t duration) { +void BaseRenderNodeAnimator::setStartValue(float value) { + LOG_ALWAYS_FATAL_IF(mPlayState != NEEDS_START, + "Cannot set the start value after the animator has started!"); + mFromValue = value; + mDeltaValue = (mFinalValue - mFromValue); + mPlayState = PENDING; +} + +void BaseRenderNodeAnimator::setupStartValueIfNecessary(RenderNode* target, TreeInfo& info) { + if (mPlayState == NEEDS_START) { + setStartValue(getValue(target)); + mPlayState = PENDING; + } +} + +void BaseRenderNodeAnimator::setDuration(nsecs_t duration) { mDuration = duration; } -bool BaseAnimator::animateFrame(TreeInfo& info) { +void BaseRenderNodeAnimator::setStartDelay(nsecs_t startDelay) { + mStartDelay = startDelay; +} + +bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { + if (mPlayState == PENDING && mStartDelay > 0 && mDelayUntil == 0) { + mDelayUntil = info.frameTimeMs + mStartDelay; + return false; + } + + if (mDelayUntil > info.frameTimeMs) { + return false; + } + if (mPlayState == PENDING) { mPlayState = RUNNING; mStartTime = info.frameTimeMs; @@ -59,7 +92,6 @@ bool BaseAnimator::animateFrame(TreeInfo& info) { if (!mInterpolator) { setInterpolator(Interpolator::createDefaultInterpolator()); } - onAnimationStarted(); } float fraction = 1.0f; @@ -71,17 +103,16 @@ bool BaseAnimator::animateFrame(TreeInfo& info) { } } fraction = mInterpolator->interpolate(fraction); - onAnimationUpdated(fraction); + setValue(target, mFromValue + (mDeltaValue * fraction)); if (mPlayState == FINISHED) { - onAnimationFinished(); callOnFinishedListener(info); return true; } return false; } -void BaseAnimator::callOnFinishedListener(TreeInfo& info) { +void BaseRenderNodeAnimator::callOnFinishedListener(TreeInfo& info) { if (mListener.get()) { if (!info.animationHook) { mListener->onAnimationFinished(this); @@ -92,70 +123,49 @@ void BaseAnimator::callOnFinishedListener(TreeInfo& info) { } /************************************************************ - * BaseRenderNodeAnimator - ************************************************************/ - -BaseRenderNodeAnimator::BaseRenderNodeAnimator( - BaseRenderNodeAnimator::DeltaValueType deltaType, float delta) - : mTarget(0) - , mDeltaValueType(deltaType) - , mDeltaValue(delta) - , mFromValue(-1) { -} - -bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { - mTarget = target; - bool finished = animateFrame(info); - mTarget = NULL; - return finished; -} - -void BaseRenderNodeAnimator::onAnimationStarted() { - mFromValue = getValue(); - - if (mDeltaValueType == BaseRenderNodeAnimator::ABSOLUTE) { - mDeltaValue = (mDeltaValue - mFromValue); - mDeltaValueType = BaseRenderNodeAnimator::DELTA; - } -} - -void BaseRenderNodeAnimator::onAnimationUpdated(float fraction) { - float value = mFromValue + (mDeltaValue * fraction); - setValue(value); -} - -/************************************************************ * RenderPropertyAnimator ************************************************************/ +struct RenderPropertyAnimator::PropertyAccessors { + RenderNode::DirtyPropertyMask dirtyMask; + GetFloatProperty getter; + SetFloatProperty setter; +}; + // Maps RenderProperty enum to accessors const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = { - {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, - {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, - {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, - {&RenderProperties::getScaleX, &RenderProperties::setScaleX }, - {&RenderProperties::getScaleY, &RenderProperties::setScaleY }, - {&RenderProperties::getRotation, &RenderProperties::setRotation }, - {&RenderProperties::getRotationX, &RenderProperties::setRotationX }, - {&RenderProperties::getRotationY, &RenderProperties::setRotationY }, - {&RenderProperties::getX, &RenderProperties::setX }, - {&RenderProperties::getY, &RenderProperties::setY }, - {&RenderProperties::getZ, &RenderProperties::setZ }, - {&RenderProperties::getAlpha, &RenderProperties::setAlpha }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, + {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX }, + {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY }, + {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation }, + {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX }, + {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY }, + {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX }, + {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY }, + {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ }, + {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha }, }; -RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, - DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) - , mPropertyAccess(PROPERTY_ACCESSOR_LUT[property]) { +RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue) + : BaseRenderNodeAnimator(finalValue) + , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) { +} + +void RenderPropertyAnimator::onAttached(RenderNode* target) { + if (target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { + setStartValue((target->stagingProperties().*mPropertyAccess->getter)()); + } + (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); } -float RenderPropertyAnimator::getValue() const { - return (target()->animatorProperties().*mPropertyAccess.getter)(); +float RenderPropertyAnimator::getValue(RenderNode* target) const { + return (target->properties().*mPropertyAccess->getter)(); } -void RenderPropertyAnimator::setValue(float value) { - (target()->animatorProperties().*mPropertyAccess.setter)(value); +void RenderPropertyAnimator::setValue(RenderNode* target, float value) { + (target->animatorProperties().*mPropertyAccess->setter)(value); } /************************************************************ @@ -163,16 +173,16 @@ void RenderPropertyAnimator::setValue(float value) { ************************************************************/ CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator( - CanvasPropertyPrimitive* property, DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) + CanvasPropertyPrimitive* property, float finalValue) + : BaseRenderNodeAnimator(finalValue) , mProperty(property) { } -float CanvasPropertyPrimitiveAnimator::getValue() const { +float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const { return mProperty->value; } -void CanvasPropertyPrimitiveAnimator::setValue(float value) { +void CanvasPropertyPrimitiveAnimator::setValue(RenderNode* target, float value) { mProperty->value = value; } @@ -181,14 +191,13 @@ void CanvasPropertyPrimitiveAnimator::setValue(float value) { ************************************************************/ CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator( - CanvasPropertyPaint* property, PaintField field, - DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) + CanvasPropertyPaint* property, PaintField field, float finalValue) + : BaseRenderNodeAnimator(finalValue) , mProperty(property) , mField(field) { } -float CanvasPropertyPaintAnimator::getValue() const { +float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const { switch (mField) { case STROKE_WIDTH: return mProperty->value.getStrokeWidth(); @@ -204,7 +213,7 @@ static uint8_t to_uint8(float value) { return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c ); } -void CanvasPropertyPaintAnimator::setValue(float value) { +void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) { switch (mField) { case STROKE_WIDTH: mProperty->value.setStrokeWidth(value); diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 52a1807..7741617 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -33,90 +33,63 @@ class RenderProperties; class AnimationListener : public VirtualLightRefBase { public: - ANDROID_API virtual void onAnimationFinished(BaseAnimator*) = 0; + ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0; protected: ANDROID_API virtual ~AnimationListener() {} }; -// Helper class to contain generic animator helpers -class BaseAnimator : public VirtualLightRefBase { - PREVENT_COPY_AND_ASSIGN(BaseAnimator); +class BaseRenderNodeAnimator : public VirtualLightRefBase { + PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator); public: - ANDROID_API void setInterpolator(Interpolator* interpolator); ANDROID_API void setDuration(nsecs_t durationInMs); ANDROID_API nsecs_t duration() { return mDuration; } + ANDROID_API void setStartDelay(nsecs_t startDelayInMs); + ANDROID_API nsecs_t startDelay() { return mStartDelay; } ANDROID_API void setListener(AnimationListener* listener) { mListener = listener; } + ANDROID_API virtual void onAttached(RenderNode* target) {} + + // Guaranteed to happen before the staging push + void setupStartValueIfNecessary(RenderNode* target, TreeInfo& info); + + bool animate(RenderNode* target, TreeInfo& info); + bool isFinished() { return mPlayState == FINISHED; } + float finalValue() { return mFinalValue; } protected: - BaseAnimator(); - virtual ~BaseAnimator(); - - // This is the main animation entrypoint that subclasses should call - // to generate the onAnimation* lifecycle events - // Returns true if the animation has finished, false otherwise - bool animateFrame(TreeInfo& info); + BaseRenderNodeAnimator(float finalValue); + virtual ~BaseRenderNodeAnimator(); - // Called when PlayState switches from PENDING to RUNNING - virtual void onAnimationStarted() {} - virtual void onAnimationUpdated(float fraction) = 0; - virtual void onAnimationFinished() {} + void setStartValue(float value); + virtual float getValue(RenderNode* target) const = 0; + virtual void setValue(RenderNode* target, float value) = 0; private: void callOnFinishedListener(TreeInfo& info); enum PlayState { + NEEDS_START, PENDING, RUNNING, FINISHED, }; + float mFinalValue; + float mDeltaValue; + float mFromValue; + Interpolator* mInterpolator; PlayState mPlayState; - long mStartTime; - long mDuration; + nsecs_t mStartTime; + nsecs_t mDelayUntil; + nsecs_t mDuration; + nsecs_t mStartDelay; - sp<AnimationListener> mListener; -}; - -class BaseRenderNodeAnimator : public BaseAnimator { -public: - // Since the UI thread doesn't necessarily know what the current values - // actually are and thus can't do the calculations, this is used to inform - // the animator how to lazy-resolve the input value - enum DeltaValueType { - // The delta value represents an absolute value endpoint - // mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue) - // in onAnimationStarted() - ABSOLUTE = 0, - // The final value represents an offset from the current value - // No recalculation is needed - DELTA, - }; - - bool animate(RenderNode* target, TreeInfo& info); - -protected: - BaseRenderNodeAnimator(DeltaValueType deltaType, float deltaValue); - - RenderNode* target() const { return mTarget; } - virtual float getValue() const = 0; - virtual void setValue(float value) = 0; - -private: - virtual void onAnimationStarted(); - virtual void onAnimationUpdated(float fraction); - - // mTarget is only valid inside animate() - RenderNode* mTarget; - - BaseRenderNodeAnimator::DeltaValueType mDeltaValueType; - float mDeltaValue; - float mFromValue; + sp<AnimationListener> mListener; }; class RenderPropertyAnimator : public BaseRenderNodeAnimator { @@ -136,23 +109,20 @@ public: ALPHA, }; - ANDROID_API RenderPropertyAnimator(RenderProperty property, - DeltaValueType deltaType, float deltaValue); + ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue); + + ANDROID_API virtual void onAttached(RenderNode* target); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: typedef void (RenderProperties::*SetFloatProperty)(float value); typedef float (RenderProperties::*GetFloatProperty)() const; - struct PropertyAccessors { - GetFloatProperty getter; - SetFloatProperty setter; - }; - - PropertyAccessors mPropertyAccess; + struct PropertyAccessors; + const PropertyAccessors* mPropertyAccess; static const PropertyAccessors PROPERTY_ACCESSOR_LUT[]; }; @@ -160,10 +130,10 @@ private: class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { public: ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, - DeltaValueType deltaType, float deltaValue); + float finalValue); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: sp<CanvasPropertyPrimitive> mProperty; }; @@ -176,10 +146,10 @@ public: }; ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, - PaintField field, DeltaValueType deltaType, float deltaValue); + PaintField field, float finalValue); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: sp<CanvasPropertyPaint> mProperty; PaintField mField; 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 921c77b..826d988 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -713,6 +713,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; } } @@ -846,6 +847,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); @@ -874,8 +876,6 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - - return true; } @@ -894,7 +894,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) { @@ -1369,6 +1369,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; } @@ -1376,6 +1379,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, @@ -1451,7 +1455,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) { @@ -1459,7 +1463,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); @@ -1472,6 +1479,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(); @@ -1496,7 +1505,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) { @@ -1507,13 +1515,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; } @@ -1670,6 +1682,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() { @@ -2904,7 +2928,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()) { @@ -3435,6 +3459,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/PixelBuffer.h b/libs/hwui/PixelBuffer.h index 9725a61..04225a2 100644 --- a/libs/hwui/PixelBuffer.h +++ b/libs/hwui/PixelBuffer.h @@ -175,6 +175,25 @@ public: return 0; } + /** + * Returns the alpha channel offset in the specified format. + * + * Supported formats: + * GL_ALPHA + * GL_RGBA + */ + static uint32_t formatAlphaOffset(GLenum format) { + switch (format) { + case GL_ALPHA: + return 0; + case GL_RGBA: + return 3; + } + + ALOGE("unsupported format: %d",format); + return 0; + } + protected: /** * Creates a new render buffer in the specified format and dimensions. 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..f0645a9 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -53,7 +53,7 @@ void RenderNode::outputLogBuffer(int fd) { } RenderNode::RenderNode() - : mNeedsPropertiesSync(false) + : mDirtyPropertyFields(0) , mNeedsDisplayListDataSync(false) , mDisplayListData(0) , mStagingDisplayListData(0) @@ -109,23 +109,37 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) { prepareSubTree(info, mDisplayListData); } -static bool is_finished(const sp<BaseRenderNodeAnimator>& animator) { - return animator->isFinished(); -} +class PushAnimatorsFunctor { +public: + PushAnimatorsFunctor(RenderNode* target, TreeInfo& info) + : mTarget(target), mInfo(info) {} -void RenderNode::pushStagingChanges(TreeInfo& info) { - if (mNeedsPropertiesSync) { - mNeedsPropertiesSync = false; - mProperties = mStagingProperties; + bool operator() (const sp<BaseRenderNodeAnimator>& animator) { + animator->setupStartValueIfNecessary(mTarget, mInfo); + return animator->isFinished(); } +private: + RenderNode* mTarget; + TreeInfo& mInfo; +}; + +void RenderNode::pushStagingChanges(TreeInfo& info) { + // Push the animators first so that setupStartValueIfNecessary() is called + // before properties() is trampled by stagingProperties(), as they are + // required by some animators. if (mNeedsAnimatorsSync) { mAnimators.resize(mStagingAnimators.size()); std::vector< sp<BaseRenderNodeAnimator> >::iterator it; + PushAnimatorsFunctor functor(this, info); // hint: this means copy_if_not() it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(), - mAnimators.begin(), is_finished); + mAnimators.begin(), functor); mAnimators.resize(std::distance(mAnimators.begin(), it)); } + if (mDirtyPropertyFields) { + mDirtyPropertyFields = 0; + mProperties = mStagingProperties; + } if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData @@ -144,7 +158,7 @@ public: AnimateFunctor(RenderNode* target, TreeInfo& info) : mTarget(target), mInfo(info) {} - bool operator() (sp<BaseRenderNodeAnimator>& animator) { + bool operator() (const sp<BaseRenderNodeAnimator>& animator) { return animator->animate(mTarget, mInfo); } private: @@ -653,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/RenderNode.h b/libs/hwui/RenderNode.h index bc62ee1..1811a7b 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -82,6 +82,22 @@ class DrawDisplayListOp; */ class RenderNode : public VirtualLightRefBase { public: + enum DirtyPropertyMask { + GENERIC = 1 << 1, + TRANSLATION_X = 1 << 2, + TRANSLATION_Y = 1 << 3, + TRANSLATION_Z = 1 << 4, + SCALE_X = 1 << 5, + SCALE_Y = 1 << 6, + ROTATION = 1 << 7, + ROTATION_X = 1 << 8, + ROTATION_Y = 1 << 9, + X = 1 << 10, + Y = 1 << 11, + Z = 1 << 12, + ALPHA = 1 << 13, + }; + ANDROID_API RenderNode(); ANDROID_API virtual ~RenderNode(); @@ -123,6 +139,14 @@ public: } } + bool isPropertyFieldDirty(DirtyPropertyMask field) const { + return mDirtyPropertyFields & field; + } + + void setPropertyFieldsDirty(uint32_t fields) { + mDirtyPropertyFields |= fields; + } + const RenderProperties& properties() { return mProperties; } @@ -136,7 +160,6 @@ public: } RenderProperties& mutateStagingProperties() { - mNeedsPropertiesSync = true; return mStagingProperties; } @@ -152,6 +175,7 @@ public: // UI thread only! ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator) { + animator->onAttached(this); mStagingAnimators.insert(animator); mNeedsAnimatorsSync = true; } @@ -227,7 +251,7 @@ private: String8 mName; - bool mNeedsPropertiesSync; + uint32_t mDirtyPropertyFields; RenderProperties mProperties; RenderProperties mStagingProperties; 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 1a517fc..5426e89 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, @@ -107,7 +136,7 @@ public: * Returns the current clip in local coordinates. The clip rect is * transformed by the inverse transform matrix. */ - const Rect& getLocalClip(); + ANDROID_API const Rect& getLocalClip(); /** * Returns the current clip in render target coordinates. @@ -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/TreeInfo.h b/libs/hwui/TreeInfo.h index fc5994c..d4a23b8 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -21,12 +21,12 @@ namespace android { namespace uirenderer { -class BaseAnimator; +class BaseRenderNodeAnimator; class AnimationListener; class AnimationHook { public: - virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) = 0; + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) = 0; protected: ~AnimationHook() {} }; diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 08e9a1a..a71db5b 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -217,6 +217,7 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer(); uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat()); + uint32_t alpha_channel_offset = PixelBuffer::formatAlphaOffset(pixelBuffer->getFormat()); uint32_t cacheWidth = cacheTexture->getWidth(); uint32_t srcStride = formatSize * cacheWidth; uint32_t startY = glyph->mStartY * srcStride; @@ -231,7 +232,7 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth); } else { for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) { - bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize]; + bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize + alpha_channel_offset]; } } } 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; } |