summaryrefslogtreecommitdiffstats
path: root/libs/hwui/RenderProperties.h
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/RenderProperties.h')
-rw-r--r--libs/hwui/RenderProperties.h623
1 files changed, 623 insertions, 0 deletions
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
new file mode 100644
index 0000000..0c8d07f
--- /dev/null
+++ b/libs/hwui/RenderProperties.h
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERNODEPROPERTIES_H
+#define RENDERNODEPROPERTIES_H
+
+#include <algorithm>
+#include <stddef.h>
+#include <vector>
+#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+#include <SkRegion.h>
+
+#include "Animator.h"
+#include "Rect.h"
+#include "RevealClip.h"
+#include "Outline.h"
+#include "utils/MathUtils.h"
+
+class SkBitmap;
+class SkColorFilter;
+class SkPaint;
+
+namespace android {
+namespace uirenderer {
+
+class Matrix4;
+class RenderNode;
+class RenderProperties;
+
+// The __VA_ARGS__ will be executed if a & b are not equal
+#define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false)
+#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
+
+// Keep in sync with View.java:LAYER_TYPE_*
+enum LayerType {
+ kLayerTypeNone = 0,
+ // Although we cannot build the software layer directly (must be done at
+ // record time), this information is used when applying alpha.
+ kLayerTypeSoftware = 1,
+ kLayerTypeRenderLayer = 2,
+ // TODO: LayerTypeSurfaceTexture? Maybe?
+};
+
+enum ClippingFlags {
+ CLIP_TO_BOUNDS = 0x1 << 0,
+ CLIP_TO_CLIP_BOUNDS = 0x1 << 1,
+};
+
+class ANDROID_API LayerProperties {
+public:
+ bool setType(LayerType type) {
+ if (RP_SET(mType, type)) {
+ reset();
+ return true;
+ }
+ return false;
+ }
+
+ LayerType type() const {
+ return mType;
+ }
+
+ bool setOpaque(bool opaque) {
+ return RP_SET(mOpaque, opaque);
+ }
+
+ bool opaque() const {
+ return mOpaque;
+ }
+
+ bool setAlpha(uint8_t alpha) {
+ return RP_SET(mAlpha, alpha);
+ }
+
+ uint8_t alpha() const {
+ return mAlpha;
+ }
+
+ bool setXferMode(SkXfermode::Mode mode) {
+ return RP_SET(mMode, mode);
+ }
+
+ SkXfermode::Mode xferMode() const {
+ return mMode;
+ }
+
+ bool setColorFilter(SkColorFilter* filter);
+
+ SkColorFilter* colorFilter() const {
+ return mColorFilter;
+ }
+
+ // Sets alpha, xfermode, and colorfilter from an SkPaint
+ // paint may be NULL, in which case defaults will be set
+ bool setFromPaint(const SkPaint* paint);
+
+ bool needsBlending() const {
+ return !opaque() || alpha() < 255;
+ }
+
+ LayerProperties& operator=(const LayerProperties& other);
+
+private:
+ LayerProperties();
+ ~LayerProperties();
+ void reset();
+
+ friend class RenderProperties;
+
+ LayerType mType;
+ // Whether or not that Layer's content is opaque, doesn't include alpha
+ bool mOpaque;
+ uint8_t mAlpha;
+ SkXfermode::Mode mMode;
+ SkColorFilter* mColorFilter;
+};
+
+/*
+ * Data structure that holds the properties for a RenderNode
+ */
+class ANDROID_API RenderProperties {
+public:
+ RenderProperties();
+ virtual ~RenderProperties();
+
+ static bool setFlag(int flag, bool newValue, int* outFlags) {
+ if (newValue) {
+ if (!(flag & *outFlags)) {
+ *outFlags |= flag;
+ return true;
+ }
+ return false;
+ } else {
+ if (flag & *outFlags) {
+ *outFlags &= ~flag;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ RenderProperties& operator=(const RenderProperties& other);
+
+ bool setClipToBounds(bool clipToBounds) {
+ return setFlag(CLIP_TO_BOUNDS, clipToBounds, &mPrimitiveFields.mClippingFlags);
+ }
+
+ bool setClipBounds(const Rect& clipBounds) {
+ bool ret = setFlag(CLIP_TO_CLIP_BOUNDS, true, &mPrimitiveFields.mClippingFlags);
+ return RP_SET(mPrimitiveFields.mClipBounds, clipBounds) || ret;
+ }
+
+ bool setClipBoundsEmpty() {
+ return setFlag(CLIP_TO_CLIP_BOUNDS, false, &mPrimitiveFields.mClippingFlags);
+ }
+
+ bool setProjectBackwards(bool shouldProject) {
+ return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
+ }
+
+ bool setProjectionReceiver(bool shouldRecieve) {
+ return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
+ }
+
+ bool isProjectionReceiver() const {
+ return mPrimitiveFields.mProjectionReceiver;
+ }
+
+ bool setStaticMatrix(const SkMatrix* matrix) {
+ delete mStaticMatrix;
+ if (matrix) {
+ mStaticMatrix = new SkMatrix(*matrix);
+ } else {
+ mStaticMatrix = NULL;
+ }
+ return true;
+ }
+
+ // Can return NULL
+ const SkMatrix* getStaticMatrix() const {
+ return mStaticMatrix;
+ }
+
+ bool setAnimationMatrix(const SkMatrix* matrix) {
+ delete mAnimationMatrix;
+ if (matrix) {
+ mAnimationMatrix = new SkMatrix(*matrix);
+ } else {
+ mAnimationMatrix = NULL;
+ }
+ return true;
+ }
+
+ bool setAlpha(float alpha) {
+ alpha = MathUtils::clampAlpha(alpha);
+ return RP_SET(mPrimitiveFields.mAlpha, alpha);
+ }
+
+ float getAlpha() const {
+ return mPrimitiveFields.mAlpha;
+ }
+
+ bool setHasOverlappingRendering(bool hasOverlappingRendering) {
+ return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
+ }
+
+ bool hasOverlappingRendering() const {
+ return mPrimitiveFields.mHasOverlappingRendering;
+ }
+
+ bool setElevation(float elevation) {
+ return RP_SET(mPrimitiveFields.mElevation, elevation);
+ // Don't dirty matrix/pivot, since they don't respect Z
+ }
+
+ float getElevation() const {
+ return mPrimitiveFields.mElevation;
+ }
+
+ bool setTranslationX(float translationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
+ }
+
+ float getTranslationX() const {
+ return mPrimitiveFields.mTranslationX;
+ }
+
+ bool setTranslationY(float translationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
+ }
+
+ float getTranslationY() const {
+ return mPrimitiveFields.mTranslationY;
+ }
+
+ bool setTranslationZ(float translationZ) {
+ return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
+ // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
+ }
+
+ float getTranslationZ() const {
+ return mPrimitiveFields.mTranslationZ;
+ }
+
+ // Animation helper
+ bool setX(float value) {
+ return setTranslationX(value - getLeft());
+ }
+
+ // Animation helper
+ float getX() const {
+ return getLeft() + getTranslationX();
+ }
+
+ // Animation helper
+ bool setY(float value) {
+ return setTranslationY(value - getTop());
+ }
+
+ // Animation helper
+ float getY() const {
+ return getTop() + getTranslationY();
+ }
+
+ // Animation helper
+ bool setZ(float value) {
+ return setTranslationZ(value - getElevation());
+ }
+
+ float getZ() const {
+ return getElevation() + getTranslationZ();
+ }
+
+ bool setRotation(float rotation) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
+ }
+
+ float getRotation() const {
+ return mPrimitiveFields.mRotation;
+ }
+
+ bool setRotationX(float rotationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
+ }
+
+ float getRotationX() const {
+ return mPrimitiveFields.mRotationX;
+ }
+
+ bool setRotationY(float rotationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
+ }
+
+ float getRotationY() const {
+ return mPrimitiveFields.mRotationY;
+ }
+
+ bool setScaleX(float scaleX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
+ }
+
+ float getScaleX() const {
+ return mPrimitiveFields.mScaleX;
+ }
+
+ bool setScaleY(float scaleY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
+ }
+
+ float getScaleY() const {
+ return mPrimitiveFields.mScaleY;
+ }
+
+ bool setPivotX(float pivotX) {
+ if (RP_SET(mPrimitiveFields.mPivotX, pivotX)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
+ }
+
+ /* Note that getPivotX and getPivotY are adjusted by updateMatrix(),
+ * so the value returned may be stale if the RenderProperties has been
+ * modified since the last call to updateMatrix()
+ */
+ float getPivotX() const {
+ return mPrimitiveFields.mPivotX;
+ }
+
+ bool setPivotY(float pivotY) {
+ if (RP_SET(mPrimitiveFields.mPivotY, pivotY)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
+ }
+
+ float getPivotY() const {
+ return mPrimitiveFields.mPivotY;
+ }
+
+ bool isPivotExplicitlySet() const {
+ return mPrimitiveFields.mPivotExplicitlySet;
+ }
+
+ bool setCameraDistance(float distance) {
+ if (distance != getCameraDistance()) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance);
+ return true;
+ }
+ return false;
+ }
+
+ float getCameraDistance() const {
+ // TODO: update getCameraLocationZ() to be const
+ return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
+ }
+
+ bool setLeft(int left) {
+ if (RP_SET(mPrimitiveFields.mLeft, left)) {
+ mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getLeft() const {
+ return mPrimitiveFields.mLeft;
+ }
+
+ bool setTop(int top) {
+ if (RP_SET(mPrimitiveFields.mTop, top)) {
+ mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getTop() const {
+ return mPrimitiveFields.mTop;
+ }
+
+ bool setRight(int right) {
+ if (RP_SET(mPrimitiveFields.mRight, right)) {
+ mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getRight() const {
+ return mPrimitiveFields.mRight;
+ }
+
+ bool setBottom(int bottom) {
+ if (RP_SET(mPrimitiveFields.mBottom, bottom)) {
+ mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ float getBottom() const {
+ return mPrimitiveFields.mBottom;
+ }
+
+ bool setLeftTop(int left, int top) {
+ bool leftResult = setLeft(left);
+ bool topResult = setTop(top);
+ return leftResult || topResult;
+ }
+
+ bool setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop
+ || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
+ mPrimitiveFields.mLeft = left;
+ mPrimitiveFields.mTop = top;
+ mPrimitiveFields.mRight = right;
+ mPrimitiveFields.mBottom = bottom;
+ mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
+ mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
+ if (!mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool offsetLeftRight(int offset) {
+ if (offset != 0) {
+ mPrimitiveFields.mLeft += offset;
+ mPrimitiveFields.mRight += offset;
+ return true;
+ }
+ return false;
+ }
+
+ bool offsetTopBottom(int offset) {
+ if (offset != 0) {
+ mPrimitiveFields.mTop += offset;
+ mPrimitiveFields.mBottom += offset;
+ return true;
+ }
+ return false;
+ }
+
+ int getWidth() const {
+ return mPrimitiveFields.mWidth;
+ }
+
+ int getHeight() const {
+ return mPrimitiveFields.mHeight;
+ }
+
+ const SkMatrix* getAnimationMatrix() const {
+ return mAnimationMatrix;
+ }
+
+ bool hasTransformMatrix() const {
+ return getTransformMatrix() && !getTransformMatrix()->isIdentity();
+ }
+
+ // May only call this if hasTransformMatrix() is true
+ bool isTransformTranslateOnly() const {
+ return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask;
+ }
+
+ const SkMatrix* getTransformMatrix() const {
+ LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!");
+ return mComputedFields.mTransformMatrix;
+ }
+
+ int getClippingFlags() const {
+ return mPrimitiveFields.mClippingFlags;
+ }
+
+ bool getClipToBounds() const {
+ return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS;
+ }
+
+ void getClippingRectForFlags(uint32_t flags, Rect* outRect) const {
+ if (flags & CLIP_TO_BOUNDS) {
+ outRect->set(0, 0, getWidth(), getHeight());
+ if (flags & CLIP_TO_CLIP_BOUNDS) {
+ outRect->intersect(mPrimitiveFields.mClipBounds);
+ }
+ } else {
+ outRect->set(mPrimitiveFields.mClipBounds);
+ }
+ }
+
+ bool getHasOverlappingRendering() const {
+ return mPrimitiveFields.mHasOverlappingRendering;
+ }
+
+ const Outline& getOutline() const {
+ return mPrimitiveFields.mOutline;
+ }
+
+ const RevealClip& getRevealClip() const {
+ return mPrimitiveFields.mRevealClip;
+ }
+
+ bool getProjectBackwards() const {
+ return mPrimitiveFields.mProjectBackwards;
+ }
+
+ void debugOutputProperties(const int level) const;
+
+ void updateMatrix();
+
+ Outline& mutableOutline() {
+ return mPrimitiveFields.mOutline;
+ }
+
+ RevealClip& mutableRevealClip() {
+ return mPrimitiveFields.mRevealClip;
+ }
+
+ const LayerProperties& layerProperties() const {
+ return mLayerProperties;
+ }
+
+ LayerProperties& mutateLayerProperties() {
+ return mLayerProperties;
+ }
+
+ // Returns true if damage calculations should be clipped to bounds
+ // TODO: Figure out something better for getZ(), as children should still be
+ // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
+ // for this RP's getZ() anyway, this can be optimized when we have a
+ // Z damage estimate instead of INT_MAX
+ bool getClipDamageToBounds() const {
+ return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
+ }
+
+private:
+ // Rendering properties
+ struct PrimitiveFields {
+ PrimitiveFields();
+
+ Outline mOutline;
+ RevealClip mRevealClip;
+ int mClippingFlags;
+ bool mProjectBackwards;
+ bool mProjectionReceiver;
+ float mAlpha;
+ bool mHasOverlappingRendering;
+ float mElevation;
+ float mTranslationX, mTranslationY, mTranslationZ;
+ float mRotation, mRotationX, mRotationY;
+ float mScaleX, mScaleY;
+ float mPivotX, mPivotY;
+ int mLeft, mTop, mRight, mBottom;
+ int mWidth, mHeight;
+ bool mPivotExplicitlySet;
+ bool mMatrixOrPivotDirty;
+ Rect mClipBounds;
+ } mPrimitiveFields;
+
+ SkMatrix* mStaticMatrix;
+ SkMatrix* mAnimationMatrix;
+ LayerProperties mLayerProperties;
+
+ /**
+ * These fields are all generated from other properties and are not set directly.
+ */
+ struct ComputedFields {
+ ComputedFields();
+ ~ComputedFields();
+
+ /**
+ * Stores the total transformation of the DisplayList based upon its scalar
+ * translate/rotate/scale properties.
+ *
+ * In the common translation-only case, the matrix isn't necessarily allocated,
+ * and the mTranslation properties are used directly.
+ */
+ SkMatrix* mTransformMatrix;
+
+ Sk3DView mTransformCamera;
+ } mComputedFields;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODEPROPERTIES_H */