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