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:
setType(LayerType type)71     bool setType(LayerType type) {
72         if (RP_SET(mType, type)) {
73             reset();
74             return true;
75         }
76         return false;
77     }
78 
setOpaque(bool opaque)79     bool setOpaque(bool opaque) { return RP_SET(mOpaque, opaque); }
80 
opaque()81     bool opaque() const { return mOpaque; }
82 
setAlpha(uint8_t alpha)83     bool setAlpha(uint8_t alpha) { return RP_SET(mAlpha, alpha); }
84 
alpha()85     uint8_t alpha() const { return mAlpha; }
86 
setXferMode(SkBlendMode mode)87     bool setXferMode(SkBlendMode mode) { return RP_SET(mMode, mode); }
88 
xferMode()89     SkBlendMode xferMode() const { return mMode; }
90 
getColorFilter()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 
needsBlending()97     bool needsBlending() const { return !opaque() || alpha() < 255; }
98 
99     LayerProperties& operator=(const LayerProperties& other);
100 
101     // Strongly recommend using effectiveLayerType instead
type()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 
setFlag(int flag,bool newValue,int * outFlags)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      */
prepareForFunctorPresence(bool willHaveFunctor,bool ancestorDictatesFunctorsNeedLayer)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 
setClipToBounds(bool clipToBounds)174     bool setClipToBounds(bool clipToBounds) {
175         return setFlag(CLIP_TO_BOUNDS, clipToBounds, &mPrimitiveFields.mClippingFlags);
176     }
177 
setClipBounds(const Rect & clipBounds)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 
setClipBoundsEmpty()183     bool setClipBoundsEmpty() {
184         return setFlag(CLIP_TO_CLIP_BOUNDS, false, &mPrimitiveFields.mClippingFlags);
185     }
186 
setProjectBackwards(bool shouldProject)187     bool setProjectBackwards(bool shouldProject) {
188         return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
189     }
190 
setProjectionReceiver(bool shouldReceive)191     bool setProjectionReceiver(bool shouldReceive) {
192         return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive);
193     }
194 
isProjectionReceiver()195     bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; }
196 
setClipMayBeComplex(bool isClipMayBeComplex)197     bool setClipMayBeComplex(bool isClipMayBeComplex) {
198         return RP_SET(mPrimitiveFields.mClipMayBeComplex, isClipMayBeComplex);
199     }
200 
isClipMayBeComplex()201     bool isClipMayBeComplex() const { return mPrimitiveFields.mClipMayBeComplex; }
202 
setStaticMatrix(const SkMatrix * matrix)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
getStaticMatrix()214     const SkMatrix* getStaticMatrix() const { return mStaticMatrix; }
215 
setAnimationMatrix(const SkMatrix * matrix)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 
setAlpha(float alpha)226     bool setAlpha(float alpha) {
227         alpha = MathUtils::clampAlpha(alpha);
228         return RP_SET(mPrimitiveFields.mAlpha, alpha);
229     }
230 
getAlpha()231     float getAlpha() const { return mPrimitiveFields.mAlpha; }
232 
setHasOverlappingRendering(bool hasOverlappingRendering)233     bool setHasOverlappingRendering(bool hasOverlappingRendering) {
234         return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
235     }
236 
hasOverlappingRendering()237     bool hasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; }
238 
setElevation(float elevation)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 
getElevation()244     float getElevation() const { return mPrimitiveFields.mElevation; }
245 
setTranslationX(float translationX)246     bool setTranslationX(float translationX) {
247         return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
248     }
249 
getTranslationX()250     float getTranslationX() const { return mPrimitiveFields.mTranslationX; }
251 
setTranslationY(float translationY)252     bool setTranslationY(float translationY) {
253         return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
254     }
255 
getTranslationY()256     float getTranslationY() const { return mPrimitiveFields.mTranslationY; }
257 
setTranslationZ(float translationZ)258     bool setTranslationZ(float translationZ) {
259         return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
260         // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
261     }
262 
getTranslationZ()263     float getTranslationZ() const { return mPrimitiveFields.mTranslationZ; }
264 
265     // Animation helper
setX(float value)266     bool setX(float value) { return setTranslationX(value - getLeft()); }
267 
268     // Animation helper
getX()269     float getX() const { return getLeft() + getTranslationX(); }
270 
271     // Animation helper
setY(float value)272     bool setY(float value) { return setTranslationY(value - getTop()); }
273 
274     // Animation helper
getY()275     float getY() const { return getTop() + getTranslationY(); }
276 
277     // Animation helper
setZ(float value)278     bool setZ(float value) { return setTranslationZ(value - getElevation()); }
279 
getZ()280     float getZ() const { return getElevation() + getTranslationZ(); }
281 
setRotation(float rotation)282     bool setRotation(float rotation) {
283         return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
284     }
285 
getRotation()286     float getRotation() const { return mPrimitiveFields.mRotation; }
287 
setRotationX(float rotationX)288     bool setRotationX(float rotationX) {
289         return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
290     }
291 
getRotationX()292     float getRotationX() const { return mPrimitiveFields.mRotationX; }
293 
setRotationY(float rotationY)294     bool setRotationY(float rotationY) {
295         return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
296     }
297 
getRotationY()298     float getRotationY() const { return mPrimitiveFields.mRotationY; }
299 
setScaleX(float scaleX)300     bool setScaleX(float scaleX) { return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); }
301 
getScaleX()302     float getScaleX() const { return mPrimitiveFields.mScaleX; }
303 
setScaleY(float scaleY)304     bool setScaleY(float scaleY) { return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); }
305 
getScaleY()306     float getScaleY() const { return mPrimitiveFields.mScaleY; }
307 
setPivotX(float pivotX)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      */
getPivotX()321     float getPivotX() const { return mPrimitiveFields.mPivotX; }
322 
setPivotY(float pivotY)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 
getPivotY()332     float getPivotY() const { return mPrimitiveFields.mPivotY; }
333 
isPivotExplicitlySet()334     bool isPivotExplicitlySet() const { return mPrimitiveFields.mPivotExplicitlySet; }
335 
resetPivot()336     bool resetPivot() { return RP_SET_AND_DIRTY(mPrimitiveFields.mPivotExplicitlySet, false); }
337 
setCameraDistance(float distance)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 
getCameraDistance()347     float getCameraDistance() const {
348         // TODO: update getCameraLocationZ() to be const
349         return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
350     }
351 
setLeft(int left)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 
getLeft()363     int getLeft() const { return mPrimitiveFields.mLeft; }
364 
setTop(int top)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 
getTop()376     int getTop() const { return mPrimitiveFields.mTop; }
377 
setRight(int right)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 
getRight()389     int getRight() const { return mPrimitiveFields.mRight; }
390 
setBottom(int bottom)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 
getBottom()402     int getBottom() const { return mPrimitiveFields.mBottom; }
403 
setLeftTop(int left,int top)404     bool setLeftTop(int left, int top) {
405         bool leftResult = setLeft(left);
406         bool topResult = setTop(top);
407         return leftResult || topResult;
408     }
409 
setLeftTopRightBottom(int left,int top,int right,int bottom)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 
offsetLeftRight(int offset)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 
offsetTopBottom(int offset)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 
getWidth()445     int getWidth() const { return mPrimitiveFields.mWidth; }
446 
getHeight()447     int getHeight() const { return mPrimitiveFields.mHeight; }
448 
getAnimationMatrix()449     const SkMatrix* getAnimationMatrix() const { return mAnimationMatrix; }
450 
hasTransformMatrix()451     bool hasTransformMatrix() const {
452         return getTransformMatrix() && !getTransformMatrix()->isIdentity();
453     }
454 
455     // May only call this if hasTransformMatrix() is true
isTransformTranslateOnly()456     bool isTransformTranslateOnly() const {
457         return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask;
458     }
459 
getTransformMatrix()460     const SkMatrix* getTransformMatrix() const {
461         LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!");
462         return mComputedFields.mTransformMatrix;
463     }
464 
getClippingFlags()465     int getClippingFlags() const { return mPrimitiveFields.mClippingFlags; }
466 
getClipToBounds()467     bool getClipToBounds() const { return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS; }
468 
getClipBounds()469     const Rect& getClipBounds() const { return mPrimitiveFields.mClipBounds; }
470 
getClippingRectForFlags(uint32_t flags,Rect * outRect)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 
getHasOverlappingRendering()482     bool getHasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; }
483 
getOutline()484     const Outline& getOutline() const { return mPrimitiveFields.mOutline; }
485 
getRevealClip()486     const RevealClip& getRevealClip() const { return mPrimitiveFields.mRevealClip; }
487 
getProjectBackwards()488     bool getProjectBackwards() const { return mPrimitiveFields.mProjectBackwards; }
489 
490     void debugOutputProperties(std::ostream& output, const int level) const;
491 
492     void updateMatrix();
493 
mutableOutline()494     Outline& mutableOutline() { return mPrimitiveFields.mOutline; }
495 
mutableRevealClip()496     RevealClip& mutableRevealClip() { return mPrimitiveFields.mRevealClip; }
497 
layerProperties()498     const LayerProperties& layerProperties() const { return mLayerProperties; }
499 
mutateLayerProperties()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
getClipDamageToBounds()507     bool getClipDamageToBounds() const {
508         return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
509     }
510 
hasShadow()511     bool hasShadow() const {
512         return getZ() > 0.0f && getOutline().getPath() != nullptr &&
513                getOutline().getAlpha() != 0.0f;
514     }
515 
getSpotShadowColor()516     SkColor getSpotShadowColor() const { return mPrimitiveFields.mSpotShadowColor; }
517 
setSpotShadowColor(SkColor shadowColor)518     bool setSpotShadowColor(SkColor shadowColor) {
519         return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor);
520     }
521 
getAmbientShadowColor()522     SkColor getAmbientShadowColor() const { return mPrimitiveFields.mAmbientShadowColor; }
523 
setAmbientShadowColor(SkColor shadowColor)524     bool setAmbientShadowColor(SkColor shadowColor) {
525         return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor);
526     }
527 
fitsOnLayer()528     bool fitsOnLayer() const {
529         const DeviceInfo* deviceInfo = DeviceInfo::get();
530         return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
531                mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
532     }
533 
promotedToLayer()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 
effectiveLayerType()541     LayerType effectiveLayerType() const {
542         return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
543     }
544 
setAllowForceDark(bool allow)545     bool setAllowForceDark(bool allow) {
546         return RP_SET(mPrimitiveFields.mAllowForceDark, allow);
547     }
548 
getAllowForceDark()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