Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      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
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     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.
     15  */
     16 
     17 #pragma once
     18 
     19 #include "DeviceInfo.h"
     20 #include "Outline.h"
     21 #include "Rect.h"
     22 #include "RevealClip.h"
     23 #include "utils/MathUtils.h"
     24 #include "utils/PaintUtils.h"
     25 
     26 #include <SkBlendMode.h>
     27 #include <SkCamera.h>
     28 #include <SkColor.h>
     29 #include <SkMatrix.h>
     30 #include <SkRegion.h>
     31 
     32 #include <androidfw/ResourceTypes.h>
     33 #include <cutils/compiler.h>
     34 #include <stddef.h>
     35 #include <utils/Log.h>
     36 #include <algorithm>
     37 #include <ostream>
     38 #include <vector>
     39 
     40 class SkBitmap;
     41 class SkColorFilter;
     42 class SkPaint;
     43 
     44 namespace android {
     45 namespace uirenderer {
     46 
     47 class Matrix4;
     48 class RenderNode;
     49 class RenderProperties;
     50 
     51 // The __VA_ARGS__ will be executed if a & b are not equal
     52 #define RP_SET(a, b, ...) ((a) != (b) ? ((a) = (b), ##__VA_ARGS__, true) : false)
     53 #define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
     54 
     55 // Keep in sync with View.java:LAYER_TYPE_*
     56 enum class LayerType {
     57     None = 0,
     58     // We cannot build the software layer directly (must be done at record time) and all management
     59     // of software layers is handled in Java.
     60     Software = 1,
     61     RenderLayer = 2,
     62 };
     63 
     64 enum ClippingFlags {
     65     CLIP_TO_BOUNDS = 0x1 << 0,
     66     CLIP_TO_CLIP_BOUNDS = 0x1 << 1,
     67 };
     68 
     69 class ANDROID_API LayerProperties {
     70 public:
     71     bool setType(LayerType type) {
     72         if (RP_SET(mType, type)) {
     73             reset();
     74             return true;
     75         }
     76         return false;
     77     }
     78 
     79     bool setOpaque(bool opaque) { return RP_SET(mOpaque, opaque); }
     80 
     81     bool opaque() const { return mOpaque; }
     82 
     83     bool setAlpha(uint8_t alpha) { return RP_SET(mAlpha, alpha); }
     84 
     85     uint8_t alpha() const { return mAlpha; }
     86 
     87     bool setXferMode(SkBlendMode mode) { return RP_SET(mMode, mode); }
     88 
     89     SkBlendMode xferMode() const { return mMode; }
     90 
     91     SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
     92 
     93     // Sets alpha, xfermode, and colorfilter from an SkPaint
     94     // paint may be NULL, in which case defaults will be set
     95     bool setFromPaint(const SkPaint* paint);
     96 
     97     bool needsBlending() const { return !opaque() || alpha() < 255; }
     98 
     99     LayerProperties& operator=(const LayerProperties& other);
    100 
    101     // Strongly recommend using effectiveLayerType instead
    102     LayerType type() const { return mType; }
    103 
    104 private:
    105     LayerProperties();
    106     ~LayerProperties();
    107     void reset();
    108     bool setColorFilter(SkColorFilter* filter);
    109 
    110     friend class RenderProperties;
    111 
    112     LayerType mType = LayerType::None;
    113     // Whether or not that Layer's content is opaque, doesn't include alpha
    114     bool mOpaque;
    115     uint8_t mAlpha;
    116     SkBlendMode mMode;
    117     sk_sp<SkColorFilter> mColorFilter;
    118 };
    119 
    120 /*
    121  * Data structure that holds the properties for a RenderNode
    122  */
    123 class ANDROID_API RenderProperties {
    124 public:
    125     RenderProperties();
    126     virtual ~RenderProperties();
    127 
    128     static bool setFlag(int flag, bool newValue, int* outFlags) {
    129         if (newValue) {
    130             if (!(flag & *outFlags)) {
    131                 *outFlags |= flag;
    132                 return true;
    133             }
    134             return false;
    135         } else {
    136             if (flag & *outFlags) {
    137                 *outFlags &= ~flag;
    138                 return true;
    139             }
    140             return false;
    141         }
    142     }
    143 
    144     /**
    145      * Set internal layer state based on whether this layer
    146      *
    147      * Additionally, returns true if child RenderNodes with functors will need to use a layer
    148      * to support clipping.
    149      */
    150     bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) {
    151         // parent may have already dictated that a descendant layer is needed
    152         bool functorsNeedLayer =
    153                 ancestorDictatesFunctorsNeedLayer
    154                 || CC_UNLIKELY(isClipMayBeComplex())
    155 
    156                 // Round rect clipping forces layer for functors
    157                 || CC_UNLIKELY(getOutline().willRoundRectClip()) ||
    158                 CC_UNLIKELY(getRevealClip().willClip())
    159 
    160                 // Complex matrices forces layer, due to stencil clipping
    161                 || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate()) ||
    162                 CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate()) ||
    163                 CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate());
    164 
    165         mComputedFields.mNeedLayerForFunctors = (willHaveFunctor && functorsNeedLayer);
    166 
    167         // If on a layer, will have consumed the need for isolating functors from stencil.
    168         // Thus, it's safe to reset the flag until some descendent sets it.
    169         return CC_LIKELY(effectiveLayerType() == LayerType::None) && functorsNeedLayer;
    170     }
    171 
    172     RenderProperties& operator=(const RenderProperties& other);
    173 
    174     bool setClipToBounds(bool clipToBounds) {
    175         return setFlag(CLIP_TO_BOUNDS, clipToBounds, &mPrimitiveFields.mClippingFlags);
    176     }
    177 
    178     bool setClipBounds(const Rect& clipBounds) {
    179         bool ret = setFlag(CLIP_TO_CLIP_BOUNDS, true, &mPrimitiveFields.mClippingFlags);
    180         return RP_SET(mPrimitiveFields.mClipBounds, clipBounds) || ret;
    181     }
    182 
    183     bool setClipBoundsEmpty() {
    184         return setFlag(CLIP_TO_CLIP_BOUNDS, false, &mPrimitiveFields.mClippingFlags);
    185     }
    186 
    187     bool setProjectBackwards(bool shouldProject) {
    188         return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
    189     }
    190 
    191     bool setProjectionReceiver(bool shouldReceive) {
    192         return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive);
    193     }
    194 
    195     bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; }
    196 
    197     bool setClipMayBeComplex(bool isClipMayBeComplex) {
    198         return RP_SET(mPrimitiveFields.mClipMayBeComplex, isClipMayBeComplex);
    199     }
    200 
    201     bool isClipMayBeComplex() const { return mPrimitiveFields.mClipMayBeComplex; }
    202 
    203     bool setStaticMatrix(const SkMatrix* matrix) {
    204         delete mStaticMatrix;
    205         if (matrix) {
    206             mStaticMatrix = new SkMatrix(*matrix);
    207         } else {
    208             mStaticMatrix = nullptr;
    209         }
    210         return true;
    211     }
    212 
    213     // Can return NULL
    214     const SkMatrix* getStaticMatrix() const { return mStaticMatrix; }
    215 
    216     bool setAnimationMatrix(const SkMatrix* matrix) {
    217         delete mAnimationMatrix;
    218         if (matrix) {
    219             mAnimationMatrix = new SkMatrix(*matrix);
    220         } else {
    221             mAnimationMatrix = nullptr;
    222         }
    223         return true;
    224     }
    225 
    226     bool setAlpha(float alpha) {
    227         alpha = MathUtils::clampAlpha(alpha);
    228         return RP_SET(mPrimitiveFields.mAlpha, alpha);
    229     }
    230 
    231     float getAlpha() const { return mPrimitiveFields.mAlpha; }
    232 
    233     bool setHasOverlappingRendering(bool hasOverlappingRendering) {
    234         return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
    235     }
    236 
    237     bool hasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; }
    238 
    239     bool setElevation(float elevation) {
    240         return RP_SET(mPrimitiveFields.mElevation, elevation);
    241         // Don't dirty matrix/pivot, since they don't respect Z
    242     }
    243 
    244     float getElevation() const { return mPrimitiveFields.mElevation; }
    245 
    246     bool setTranslationX(float translationX) {
    247         return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
    248     }
    249 
    250     float getTranslationX() const { return mPrimitiveFields.mTranslationX; }
    251 
    252     bool setTranslationY(float translationY) {
    253         return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
    254     }
    255 
    256     float getTranslationY() const { return mPrimitiveFields.mTranslationY; }
    257 
    258     bool setTranslationZ(float translationZ) {
    259         return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
    260         // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
    261     }
    262 
    263     float getTranslationZ() const { return mPrimitiveFields.mTranslationZ; }
    264 
    265     // Animation helper
    266     bool setX(float value) { return setTranslationX(value - getLeft()); }
    267 
    268     // Animation helper
    269     float getX() const { return getLeft() + getTranslationX(); }
    270 
    271     // Animation helper
    272     bool setY(float value) { return setTranslationY(value - getTop()); }
    273 
    274     // Animation helper
    275     float getY() const { return getTop() + getTranslationY(); }
    276 
    277     // Animation helper
    278     bool setZ(float value) { return setTranslationZ(value - getElevation()); }
    279 
    280     float getZ() const { return getElevation() + getTranslationZ(); }
    281 
    282     bool setRotation(float rotation) {
    283         return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
    284     }
    285 
    286     float getRotation() const { return mPrimitiveFields.mRotation; }
    287 
    288     bool setRotationX(float rotationX) {
    289         return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
    290     }
    291 
    292     float getRotationX() const { return mPrimitiveFields.mRotationX; }
    293 
    294     bool setRotationY(float rotationY) {
    295         return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
    296     }
    297 
    298     float getRotationY() const { return mPrimitiveFields.mRotationY; }
    299 
    300     bool setScaleX(float scaleX) { return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); }
    301 
    302     float getScaleX() const { return mPrimitiveFields.mScaleX; }
    303 
    304     bool setScaleY(float scaleY) { return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); }
    305 
    306     float getScaleY() const { return mPrimitiveFields.mScaleY; }
    307 
    308     bool setPivotX(float pivotX) {
    309         if (RP_SET(mPrimitiveFields.mPivotX, pivotX) || !mPrimitiveFields.mPivotExplicitlySet) {
    310             mPrimitiveFields.mMatrixOrPivotDirty = true;
    311             mPrimitiveFields.mPivotExplicitlySet = true;
    312             return true;
    313         }
    314         return false;
    315     }
    316 
    317     /* Note that getPivotX and getPivotY are adjusted by updateMatrix(),
    318      * so the value returned may be stale if the RenderProperties has been
    319      * modified since the last call to updateMatrix()
    320      */
    321     float getPivotX() const { return mPrimitiveFields.mPivotX; }
    322 
    323     bool setPivotY(float pivotY) {
    324         if (RP_SET(mPrimitiveFields.mPivotY, pivotY) || !mPrimitiveFields.mPivotExplicitlySet) {
    325             mPrimitiveFields.mMatrixOrPivotDirty = true;
    326             mPrimitiveFields.mPivotExplicitlySet = true;
    327             return true;
    328         }
    329         return false;
    330     }
    331 
    332     float getPivotY() const { return mPrimitiveFields.mPivotY; }
    333 
    334     bool isPivotExplicitlySet() const { return mPrimitiveFields.mPivotExplicitlySet; }
    335 
    336     bool resetPivot() { return RP_SET_AND_DIRTY(mPrimitiveFields.mPivotExplicitlySet, false); }
    337 
    338     bool setCameraDistance(float distance) {
    339         if (distance != getCameraDistance()) {
    340             mPrimitiveFields.mMatrixOrPivotDirty = true;
    341             mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance);
    342             return true;
    343         }
    344         return false;
    345     }
    346 
    347     float getCameraDistance() const {
    348         // TODO: update getCameraLocationZ() to be const
    349         return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
    350     }
    351 
    352     bool setLeft(int left) {
    353         if (RP_SET(mPrimitiveFields.mLeft, left)) {
    354             mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
    355             if (!mPrimitiveFields.mPivotExplicitlySet) {
    356                 mPrimitiveFields.mMatrixOrPivotDirty = true;
    357             }
    358             return true;
    359         }
    360         return false;
    361     }
    362 
    363     int getLeft() const { return mPrimitiveFields.mLeft; }
    364 
    365     bool setTop(int top) {
    366         if (RP_SET(mPrimitiveFields.mTop, top)) {
    367             mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
    368             if (!mPrimitiveFields.mPivotExplicitlySet) {
    369                 mPrimitiveFields.mMatrixOrPivotDirty = true;
    370             }
    371             return true;
    372         }
    373         return false;
    374     }
    375 
    376     int getTop() const { return mPrimitiveFields.mTop; }
    377 
    378     bool setRight(int right) {
    379         if (RP_SET(mPrimitiveFields.mRight, right)) {
    380             mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
    381             if (!mPrimitiveFields.mPivotExplicitlySet) {
    382                 mPrimitiveFields.mMatrixOrPivotDirty = true;
    383             }
    384             return true;
    385         }
    386         return false;
    387     }
    388 
    389     int getRight() const { return mPrimitiveFields.mRight; }
    390 
    391     bool setBottom(int bottom) {
    392         if (RP_SET(mPrimitiveFields.mBottom, bottom)) {
    393             mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
    394             if (!mPrimitiveFields.mPivotExplicitlySet) {
    395                 mPrimitiveFields.mMatrixOrPivotDirty = true;
    396             }
    397             return true;
    398         }
    399         return false;
    400     }
    401 
    402     int getBottom() const { return mPrimitiveFields.mBottom; }
    403 
    404     bool setLeftTop(int left, int top) {
    405         bool leftResult = setLeft(left);
    406         bool topResult = setTop(top);
    407         return leftResult || topResult;
    408     }
    409 
    410     bool setLeftTopRightBottom(int left, int top, int right, int bottom) {
    411         if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop ||
    412             right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
    413             mPrimitiveFields.mLeft = left;
    414             mPrimitiveFields.mTop = top;
    415             mPrimitiveFields.mRight = right;
    416             mPrimitiveFields.mBottom = bottom;
    417             mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
    418             mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
    419             if (!mPrimitiveFields.mPivotExplicitlySet) {
    420                 mPrimitiveFields.mMatrixOrPivotDirty = true;
    421             }
    422             return true;
    423         }
    424         return false;
    425     }
    426 
    427     bool offsetLeftRight(int offset) {
    428         if (offset != 0) {
    429             mPrimitiveFields.mLeft += offset;
    430             mPrimitiveFields.mRight += offset;
    431             return true;
    432         }
    433         return false;
    434     }
    435 
    436     bool offsetTopBottom(int offset) {
    437         if (offset != 0) {
    438             mPrimitiveFields.mTop += offset;
    439             mPrimitiveFields.mBottom += offset;
    440             return true;
    441         }
    442         return false;
    443     }
    444 
    445     int getWidth() const { return mPrimitiveFields.mWidth; }
    446 
    447     int getHeight() const { return mPrimitiveFields.mHeight; }
    448 
    449     const SkMatrix* getAnimationMatrix() const { return mAnimationMatrix; }
    450 
    451     bool hasTransformMatrix() const {
    452         return getTransformMatrix() && !getTransformMatrix()->isIdentity();
    453     }
    454 
    455     // May only call this if hasTransformMatrix() is true
    456     bool isTransformTranslateOnly() const {
    457         return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask;
    458     }
    459 
    460     const SkMatrix* getTransformMatrix() const {
    461         LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!");
    462         return mComputedFields.mTransformMatrix;
    463     }
    464 
    465     int getClippingFlags() const { return mPrimitiveFields.mClippingFlags; }
    466 
    467     bool getClipToBounds() const { return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS; }
    468 
    469     const Rect& getClipBounds() const { return mPrimitiveFields.mClipBounds; }
    470 
    471     void getClippingRectForFlags(uint32_t flags, Rect* outRect) const {
    472         if (flags & CLIP_TO_BOUNDS) {
    473             outRect->set(0, 0, getWidth(), getHeight());
    474             if (flags & CLIP_TO_CLIP_BOUNDS) {
    475                 outRect->doIntersect(mPrimitiveFields.mClipBounds);
    476             }
    477         } else {
    478             outRect->set(mPrimitiveFields.mClipBounds);
    479         }
    480     }
    481 
    482     bool getHasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; }
    483 
    484     const Outline& getOutline() const { return mPrimitiveFields.mOutline; }
    485 
    486     const RevealClip& getRevealClip() const { return mPrimitiveFields.mRevealClip; }
    487 
    488     bool getProjectBackwards() const { return mPrimitiveFields.mProjectBackwards; }
    489 
    490     void debugOutputProperties(std::ostream& output, const int level) const;
    491 
    492     void updateMatrix();
    493 
    494     Outline& mutableOutline() { return mPrimitiveFields.mOutline; }
    495 
    496     RevealClip& mutableRevealClip() { return mPrimitiveFields.mRevealClip; }
    497 
    498     const LayerProperties& layerProperties() const { return mLayerProperties; }
    499 
    500     LayerProperties& mutateLayerProperties() { return mLayerProperties; }
    501 
    502     // Returns true if damage calculations should be clipped to bounds
    503     // TODO: Figure out something better for getZ(), as children should still be
    504     // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
    505     // for this RP's getZ() anyway, this can be optimized when we have a
    506     // Z damage estimate instead of INT_MAX
    507     bool getClipDamageToBounds() const {
    508         return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
    509     }
    510 
    511     bool hasShadow() const {
    512         return getZ() > 0.0f && getOutline().getPath() != nullptr &&
    513                getOutline().getAlpha() != 0.0f;
    514     }
    515 
    516     SkColor getSpotShadowColor() const { return mPrimitiveFields.mSpotShadowColor; }
    517 
    518     bool setSpotShadowColor(SkColor shadowColor) {
    519         return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor);
    520     }
    521 
    522     SkColor getAmbientShadowColor() const { return mPrimitiveFields.mAmbientShadowColor; }
    523 
    524     bool setAmbientShadowColor(SkColor shadowColor) {
    525         return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor);
    526     }
    527 
    528     bool fitsOnLayer() const {
    529         const DeviceInfo* deviceInfo = DeviceInfo::get();
    530         return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
    531                mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
    532     }
    533 
    534     bool promotedToLayer() const {
    535         return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
    536                (mComputedFields.mNeedLayerForFunctors ||
    537                 (!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
    538                  mPrimitiveFields.mHasOverlappingRendering));
    539     }
    540 
    541     LayerType effectiveLayerType() const {
    542         return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
    543     }
    544 
    545     bool setAllowForceDark(bool allow) {
    546         return RP_SET(mPrimitiveFields.mAllowForceDark, allow);
    547     }
    548 
    549     bool getAllowForceDark() const {
    550         return mPrimitiveFields.mAllowForceDark;
    551     }
    552 
    553 private:
    554     // Rendering properties
    555     struct PrimitiveFields {
    556         int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0;
    557         int mWidth = 0, mHeight = 0;
    558         int mClippingFlags = CLIP_TO_BOUNDS;
    559         SkColor mSpotShadowColor = SK_ColorBLACK;
    560         SkColor mAmbientShadowColor = SK_ColorBLACK;
    561         float mAlpha = 1;
    562         float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0;
    563         float mElevation = 0;
    564         float mRotation = 0, mRotationX = 0, mRotationY = 0;
    565         float mScaleX = 1, mScaleY = 1;
    566         float mPivotX = 0, mPivotY = 0;
    567         bool mHasOverlappingRendering = false;
    568         bool mPivotExplicitlySet = false;
    569         bool mMatrixOrPivotDirty = false;
    570         bool mProjectBackwards = false;
    571         bool mProjectionReceiver = false;
    572         bool mAllowForceDark = true;
    573         bool mClipMayBeComplex = false;
    574         Rect mClipBounds;
    575         Outline mOutline;
    576         RevealClip mRevealClip;
    577     } mPrimitiveFields;
    578 
    579     SkMatrix* mStaticMatrix;
    580     SkMatrix* mAnimationMatrix;
    581     LayerProperties mLayerProperties;
    582 
    583     /**
    584      * These fields are all generated from other properties and are not set directly.
    585      */
    586     struct ComputedFields {
    587         ComputedFields();
    588         ~ComputedFields();
    589 
    590         /**
    591          * Stores the total transformation of the DisplayList based upon its scalar
    592          * translate/rotate/scale properties.
    593          *
    594          * In the common translation-only case, the matrix isn't necessarily allocated,
    595          * and the mTranslation properties are used directly.
    596          */
    597         SkMatrix* mTransformMatrix;
    598 
    599         Sk3DView mTransformCamera;
    600 
    601         // Force layer on for functors to enable render features they don't yet support (clipping)
    602         bool mNeedLayerForFunctors = false;
    603     } mComputedFields;
    604 };
    605 
    606 } /* namespace uirenderer */
    607 } /* namespace android */
    608