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