2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #ifndef RENDERNODEPROPERTIES_H
17 #define RENDERNODEPROPERTIES_H
20 #include "DeviceInfo.h"
22 #include "RevealClip.h"
24 #include "utils/MathUtils.h"
29 #include <SkXfermode.h>
34 #include <cutils/compiler.h>
35 #include <androidfw/ResourceTypes.h>
36 #include <utils/Log.h>
43 namespace uirenderer {
47 class RenderProperties;
49 // The __VA_ARGS__ will be executed if a & b are not equal
50 #define RP_SET(a, b, ...) ((a) != (b) ? ((a) = (b), ##__VA_ARGS__, true) : false)
51 #define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
53 // Keep in sync with View.java:LAYER_TYPE_*
54 enum class LayerType {
56 // Although we cannot build the software layer directly (must be done at
57 // record time), this information is used when applying alpha.
60 // TODO: LayerTypeSurfaceTexture? Maybe?
64 CLIP_TO_BOUNDS = 0x1 << 0,
65 CLIP_TO_CLIP_BOUNDS = 0x1 << 1,
68 class ANDROID_API LayerProperties {
70 bool setType(LayerType type) {
71 if (RP_SET(mType, type)) {
78 bool setOpaque(bool opaque) {
79 return RP_SET(mOpaque, opaque);
86 bool setAlpha(uint8_t alpha) {
87 return RP_SET(mAlpha, alpha);
90 uint8_t alpha() const {
94 bool setXferMode(SkXfermode::Mode mode) {
95 return RP_SET(mMode, mode);
98 SkXfermode::Mode xferMode() const {
102 bool setColorFilter(SkColorFilter* filter);
104 SkColorFilter* colorFilter() const {
108 // Sets alpha, xfermode, and colorfilter from an SkPaint
109 // paint may be NULL, in which case defaults will be set
110 bool setFromPaint(const SkPaint* paint);
112 bool needsBlending() const {
113 return !opaque() || alpha() < 255;
116 LayerProperties& operator=(const LayerProperties& other);
123 // Private since external users should go through properties().effectiveLayerType()
124 LayerType type() const {
128 friend class RenderProperties;
130 LayerType mType = LayerType::None;
131 // Whether or not that Layer's content is opaque, doesn't include alpha
134 SkXfermode::Mode mMode;
135 SkColorFilter* mColorFilter = nullptr;
139 * Data structure that holds the properties for a RenderNode
141 class ANDROID_API RenderProperties {
144 virtual ~RenderProperties();
146 static bool setFlag(int flag, bool newValue, int* outFlags) {
148 if (!(flag & *outFlags)) {
154 if (flag & *outFlags) {
163 * Set internal layer state based on whether this layer
165 * Additionally, returns true if child RenderNodes with functors will need to use a layer
166 * to support clipping.
168 bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) {
169 // parent may have already dictated that a descendant layer is needed
170 bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer
172 // Round rect clipping forces layer for functors
173 || CC_UNLIKELY(getOutline().willRoundRectClip())
174 || CC_UNLIKELY(getRevealClip().willClip())
176 // Complex matrices forces layer, due to stencil clipping
177 || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate())
178 || CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate())
179 || CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate());
181 mComputedFields.mNeedLayerForFunctors = (willHaveFunctor && functorsNeedLayer);
183 // If on a layer, will have consumed the need for isolating functors from stencil.
184 // Thus, it's safe to reset the flag until some descendent sets it.
185 return CC_LIKELY(effectiveLayerType() == LayerType::None) && functorsNeedLayer;
188 RenderProperties& operator=(const RenderProperties& other);
190 bool setClipToBounds(bool clipToBounds) {
191 return setFlag(CLIP_TO_BOUNDS, clipToBounds, &mPrimitiveFields.mClippingFlags);
194 bool setClipBounds(const Rect& clipBounds) {
195 bool ret = setFlag(CLIP_TO_CLIP_BOUNDS, true, &mPrimitiveFields.mClippingFlags);
196 return RP_SET(mPrimitiveFields.mClipBounds, clipBounds) || ret;
199 bool setClipBoundsEmpty() {
200 return setFlag(CLIP_TO_CLIP_BOUNDS, false, &mPrimitiveFields.mClippingFlags);
203 bool setProjectBackwards(bool shouldProject) {
204 return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
207 bool setProjectionReceiver(bool shouldReceive) {
208 return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive);
211 bool isProjectionReceiver() const {
212 return mPrimitiveFields.mProjectionReceiver;
215 bool setStaticMatrix(const SkMatrix* matrix) {
216 delete mStaticMatrix;
218 mStaticMatrix = new SkMatrix(*matrix);
220 mStaticMatrix = nullptr;
226 const SkMatrix* getStaticMatrix() const {
227 return mStaticMatrix;
230 bool setAnimationMatrix(const SkMatrix* matrix) {
231 delete mAnimationMatrix;
233 mAnimationMatrix = new SkMatrix(*matrix);
235 mAnimationMatrix = nullptr;
240 bool setAlpha(float alpha) {
241 alpha = MathUtils::clampAlpha(alpha);
242 return RP_SET(mPrimitiveFields.mAlpha, alpha);
245 float getAlpha() const {
246 return mPrimitiveFields.mAlpha;
249 bool setHasOverlappingRendering(bool hasOverlappingRendering) {
250 return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
253 bool hasOverlappingRendering() const {
254 return mPrimitiveFields.mHasOverlappingRendering;
257 bool setElevation(float elevation) {
258 return RP_SET(mPrimitiveFields.mElevation, elevation);
259 // Don't dirty matrix/pivot, since they don't respect Z
262 float getElevation() const {
263 return mPrimitiveFields.mElevation;
266 bool setTranslationX(float translationX) {
267 return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
270 float getTranslationX() const {
271 return mPrimitiveFields.mTranslationX;
274 bool setTranslationY(float translationY) {
275 return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
278 float getTranslationY() const {
279 return mPrimitiveFields.mTranslationY;
282 bool setTranslationZ(float translationZ) {
283 return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
284 // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
287 float getTranslationZ() const {
288 return mPrimitiveFields.mTranslationZ;
292 bool setX(float value) {
293 return setTranslationX(value - getLeft());
298 return getLeft() + getTranslationX();
302 bool setY(float value) {
303 return setTranslationY(value - getTop());
308 return getTop() + getTranslationY();
312 bool setZ(float value) {
313 return setTranslationZ(value - getElevation());
317 return getElevation() + getTranslationZ();
320 bool setRotation(float rotation) {
321 return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
324 float getRotation() const {
325 return mPrimitiveFields.mRotation;
328 bool setRotationX(float rotationX) {
329 return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
332 float getRotationX() const {
333 return mPrimitiveFields.mRotationX;
336 bool setRotationY(float rotationY) {
337 return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
340 float getRotationY() const {
341 return mPrimitiveFields.mRotationY;
344 bool setScaleX(float scaleX) {
345 return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
348 float getScaleX() const {
349 return mPrimitiveFields.mScaleX;
352 bool setScaleY(float scaleY) {
353 return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
356 float getScaleY() const {
357 return mPrimitiveFields.mScaleY;
360 bool setPivotX(float pivotX) {
361 if (RP_SET(mPrimitiveFields.mPivotX, pivotX)
362 || !mPrimitiveFields.mPivotExplicitlySet) {
363 mPrimitiveFields.mMatrixOrPivotDirty = true;
364 mPrimitiveFields.mPivotExplicitlySet = true;
370 /* Note that getPivotX and getPivotY are adjusted by updateMatrix(),
371 * so the value returned may be stale if the RenderProperties has been
372 * modified since the last call to updateMatrix()
374 float getPivotX() const {
375 return mPrimitiveFields.mPivotX;
378 bool setPivotY(float pivotY) {
379 if (RP_SET(mPrimitiveFields.mPivotY, pivotY)
380 || !mPrimitiveFields.mPivotExplicitlySet) {
381 mPrimitiveFields.mMatrixOrPivotDirty = true;
382 mPrimitiveFields.mPivotExplicitlySet = true;
388 float getPivotY() const {
389 return mPrimitiveFields.mPivotY;
392 bool isPivotExplicitlySet() const {
393 return mPrimitiveFields.mPivotExplicitlySet;
396 bool setCameraDistance(float distance) {
397 if (distance != getCameraDistance()) {
398 mPrimitiveFields.mMatrixOrPivotDirty = true;
399 mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance);
405 float getCameraDistance() const {
406 // TODO: update getCameraLocationZ() to be const
407 return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
410 bool setLeft(int left) {
411 if (RP_SET(mPrimitiveFields.mLeft, left)) {
412 mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
413 if (!mPrimitiveFields.mPivotExplicitlySet) {
414 mPrimitiveFields.mMatrixOrPivotDirty = true;
421 int getLeft() const {
422 return mPrimitiveFields.mLeft;
425 bool setTop(int top) {
426 if (RP_SET(mPrimitiveFields.mTop, top)) {
427 mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
428 if (!mPrimitiveFields.mPivotExplicitlySet) {
429 mPrimitiveFields.mMatrixOrPivotDirty = true;
437 return mPrimitiveFields.mTop;
440 bool setRight(int right) {
441 if (RP_SET(mPrimitiveFields.mRight, right)) {
442 mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
443 if (!mPrimitiveFields.mPivotExplicitlySet) {
444 mPrimitiveFields.mMatrixOrPivotDirty = true;
451 int getRight() const {
452 return mPrimitiveFields.mRight;
455 bool setBottom(int bottom) {
456 if (RP_SET(mPrimitiveFields.mBottom, bottom)) {
457 mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
458 if (!mPrimitiveFields.mPivotExplicitlySet) {
459 mPrimitiveFields.mMatrixOrPivotDirty = true;
466 int getBottom() const {
467 return mPrimitiveFields.mBottom;
470 bool setLeftTop(int left, int top) {
471 bool leftResult = setLeft(left);
472 bool topResult = setTop(top);
473 return leftResult || topResult;
476 bool setLeftTopRightBottom(int left, int top, int right, int bottom) {
477 if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop
478 || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
479 mPrimitiveFields.mLeft = left;
480 mPrimitiveFields.mTop = top;
481 mPrimitiveFields.mRight = right;
482 mPrimitiveFields.mBottom = bottom;
483 mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
484 mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
485 if (!mPrimitiveFields.mPivotExplicitlySet) {
486 mPrimitiveFields.mMatrixOrPivotDirty = true;
493 bool offsetLeftRight(int offset) {
495 mPrimitiveFields.mLeft += offset;
496 mPrimitiveFields.mRight += offset;
502 bool offsetTopBottom(int offset) {
504 mPrimitiveFields.mTop += offset;
505 mPrimitiveFields.mBottom += offset;
511 int getWidth() const {
512 return mPrimitiveFields.mWidth;
515 int getHeight() const {
516 return mPrimitiveFields.mHeight;
519 const SkMatrix* getAnimationMatrix() const {
520 return mAnimationMatrix;
523 bool hasTransformMatrix() const {
524 return getTransformMatrix() && !getTransformMatrix()->isIdentity();
527 // May only call this if hasTransformMatrix() is true
528 bool isTransformTranslateOnly() const {
529 return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask;
532 const SkMatrix* getTransformMatrix() const {
533 LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!");
534 return mComputedFields.mTransformMatrix;
537 int getClippingFlags() const {
538 return mPrimitiveFields.mClippingFlags;
541 bool getClipToBounds() const {
542 return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS;
545 const Rect& getClipBounds() const {
546 return mPrimitiveFields.mClipBounds;
549 void getClippingRectForFlags(uint32_t flags, Rect* outRect) const {
550 if (flags & CLIP_TO_BOUNDS) {
551 outRect->set(0, 0, getWidth(), getHeight());
552 if (flags & CLIP_TO_CLIP_BOUNDS) {
553 outRect->doIntersect(mPrimitiveFields.mClipBounds);
556 outRect->set(mPrimitiveFields.mClipBounds);
560 bool getHasOverlappingRendering() const {
561 return mPrimitiveFields.mHasOverlappingRendering;
564 const Outline& getOutline() const {
565 return mPrimitiveFields.mOutline;
568 const RevealClip& getRevealClip() const {
569 return mPrimitiveFields.mRevealClip;
572 bool getProjectBackwards() const {
573 return mPrimitiveFields.mProjectBackwards;
576 void debugOutputProperties(const int level) const;
580 Outline& mutableOutline() {
581 return mPrimitiveFields.mOutline;
584 RevealClip& mutableRevealClip() {
585 return mPrimitiveFields.mRevealClip;
588 const LayerProperties& layerProperties() const {
589 return mLayerProperties;
592 LayerProperties& mutateLayerProperties() {
593 return mLayerProperties;
596 // Returns true if damage calculations should be clipped to bounds
597 // TODO: Figure out something better for getZ(), as children should still be
598 // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
599 // for this RP's getZ() anyway, this can be optimized when we have a
600 // Z damage estimate instead of INT_MAX
601 bool getClipDamageToBounds() const {
602 return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
605 bool hasShadow() const {
607 && getOutline().getPath() != nullptr
608 && getOutline().getAlpha() != 0.0f;
611 bool fitsOnLayer() const {
612 const DeviceInfo* deviceInfo = DeviceInfo::get();
613 return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
614 && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
617 bool promotedToLayer() const {
618 return mLayerProperties.mType == LayerType::None
620 && (mComputedFields.mNeedLayerForFunctors
621 || (!MathUtils::isZero(mPrimitiveFields.mAlpha)
622 && mPrimitiveFields.mAlpha < 1
623 && mPrimitiveFields.mHasOverlappingRendering));
626 LayerType effectiveLayerType() const {
627 return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
631 // Rendering properties
632 struct PrimitiveFields {
633 int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0;
634 int mWidth = 0, mHeight = 0;
635 int mClippingFlags = CLIP_TO_BOUNDS;
637 float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0;
638 float mElevation = 0;
639 float mRotation = 0, mRotationX = 0, mRotationY = 0;
640 float mScaleX = 1, mScaleY = 1;
641 float mPivotX = 0, mPivotY = 0;
642 bool mHasOverlappingRendering = false;
643 bool mPivotExplicitlySet = false;
644 bool mMatrixOrPivotDirty = false;
645 bool mProjectBackwards = false;
646 bool mProjectionReceiver = false;
649 RevealClip mRevealClip;
652 SkMatrix* mStaticMatrix;
653 SkMatrix* mAnimationMatrix;
654 LayerProperties mLayerProperties;
657 * These fields are all generated from other properties and are not set directly.
659 struct ComputedFields {
664 * Stores the total transformation of the DisplayList based upon its scalar
665 * translate/rotate/scale properties.
667 * In the common translation-only case, the matrix isn't necessarily allocated,
668 * and the mTranslation properties are used directly.
670 SkMatrix* mTransformMatrix;
672 Sk3DView mTransformCamera;
674 // Force layer on for functors to enable render features they don't yet support (clipping)
675 bool mNeedLayerForFunctors = false;
679 } /* namespace uirenderer */
680 } /* namespace android */
682 #endif /* RENDERNODEPROPERTIES_H */