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