summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/Animator.cpp159
-rw-r--r--libs/hwui/Animator.h112
-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/PixelBuffer.h19
-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.cpp38
-rw-r--r--libs/hwui/RenderNode.h28
-rw-r--r--libs/hwui/Snapshot.cpp50
-rw-r--r--libs/hwui/Snapshot.h44
-rw-r--r--libs/hwui/StatefulBaseRenderer.cpp20
-rw-r--r--libs/hwui/StatefulBaseRenderer.h10
-rw-r--r--libs/hwui/TreeInfo.h4
-rw-r--r--libs/hwui/font/Font.cpp3
-rw-r--r--libs/hwui/utils/MathUtils.h4
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;
}