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