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