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