1/*
2 * Copyright (C) 2015 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#ifndef ANDROID_HWUI_VPATH_H
18#define ANDROID_HWUI_VPATH_H
19
20#include "hwui/Canvas.h"
21#include "hwui/Bitmap.h"
22#include "DisplayList.h"
23
24#include <SkBitmap.h>
25#include <SkColor.h>
26#include <SkColorFilter.h>
27#include <SkCanvas.h>
28#include <SkMatrix.h>
29#include <SkPaint.h>
30#include <SkPath.h>
31#include <SkPathMeasure.h>
32#include <SkRect.h>
33#include <SkShader.h>
34
35#include <cutils/compiler.h>
36#include <stddef.h>
37#include <vector>
38#include <string>
39
40namespace android {
41namespace uirenderer {
42
43// Debug
44#if DEBUG_VECTOR_DRAWABLE
45    #define VECTOR_DRAWABLE_LOGD(...) ALOGD(__VA_ARGS__)
46#else
47    #define VECTOR_DRAWABLE_LOGD(...)
48#endif
49
50namespace VectorDrawable {
51#define VD_SET_PRIMITIVE_FIELD_WITH_FLAG(field, value, flag) (VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, (value)) ? ((flag) = true, true) : false)
52#define VD_SET_PROP(field, value) ((value) != (field) ? ((field) = (value), true) : false)
53#define VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, value) ({ bool retVal = VD_SET_PROP((mPrimitiveFields.field), (value));\
54    onPropertyChanged(); retVal;})
55#define UPDATE_SKPROP(field, value) ({bool retVal = ((field) != (value)); if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); retVal;})
56
57/* A VectorDrawable is composed of a tree of nodes.
58 * Each node can be a group node, or a path.
59 * A group node can have groups or paths as children, but a path node has
60 * no children.
61 * One example can be:
62 *                 Root Group
63 *                /    |     \
64 *           Group    Path    Group
65 *          /     \             |
66 *         Path   Path         Path
67 *
68 * VectorDrawables are drawn into bitmap caches first, then the caches are drawn to the given
69 * canvas with root alpha applied. Two caches are maintained for VD, one in UI thread, the other in
70 * Render Thread. A generation id is used to keep track of changes in the vector drawable tree.
71 * Each cache has their own generation id to track whether they are up to date with the latest
72 * change in the tree.
73 *
74 * Any property change to the vector drawable coming from UI thread (such as bulk setters to update
75 * all the properties, and viewport change, etc.) are only modifying the staging properties. The
76 * staging properties will then be marked dirty and will be pushed over to render thread properties
77 * at sync point. If staging properties are not dirty at sync point, we sync backwards by updating
78 * staging properties with render thread properties to reflect the latest animation value.
79 *
80 */
81
82class PropertyChangedListener {
83public:
84    PropertyChangedListener(bool* dirty, bool* stagingDirty)
85            : mDirty(dirty), mStagingDirty(stagingDirty) {}
86    void onPropertyChanged() {
87            *mDirty = true;
88    }
89    void onStagingPropertyChanged() {
90            *mStagingDirty = true;
91    }
92private:
93    bool* mDirty;
94    bool* mStagingDirty;
95};
96
97class ANDROID_API Node {
98public:
99    class Properties {
100    public:
101        explicit Properties(Node* node) : mNode(node) {}
102        inline void onPropertyChanged() {
103            mNode->onPropertyChanged(this);
104        }
105    private:
106        Node* mNode;
107    };
108    Node(const Node& node) {
109        mName = node.mName;
110    }
111    Node() {}
112    virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0;
113    virtual void dump() = 0;
114    void setName(const char* name) {
115        mName = name;
116    }
117    virtual void setPropertyChangedListener(PropertyChangedListener* listener) {
118        mPropertyChangedListener = listener;
119    }
120    virtual void onPropertyChanged(Properties* properties) = 0;
121    virtual ~Node(){}
122    virtual void syncProperties() = 0;
123protected:
124    std::string mName;
125    PropertyChangedListener* mPropertyChangedListener = nullptr;
126};
127
128class ANDROID_API Path : public Node {
129public:
130    struct ANDROID_API Data {
131        std::vector<char> verbs;
132        std::vector<size_t> verbSizes;
133        std::vector<float> points;
134        bool operator==(const Data& data) const {
135            return verbs == data.verbs && verbSizes == data.verbSizes
136                    && points == data.points;
137        }
138    };
139
140    class PathProperties : public Properties {
141    public:
142        explicit PathProperties(Node* node) : Properties(node) {}
143        void syncProperties(const PathProperties& prop) {
144            mData = prop.mData;
145            onPropertyChanged();
146        }
147        void setData(const Data& data) {
148            // Updates the path data. Note that we don't generate a new Skia path right away
149            // because there are cases where the animation is changing the path data, but the view
150            // that hosts the VD has gone off screen, in which case we won't even draw. So we
151            // postpone the Skia path generation to the draw time.
152            if (data == mData) {
153                return;
154            }
155            mData = data;
156            onPropertyChanged();
157
158        }
159        const Data& getData() const {
160            return mData;
161        }
162    private:
163        Data mData;
164    };
165
166    Path(const Path& path);
167    Path(const char* path, size_t strLength);
168    Path() {}
169
170    void dump() override;
171    virtual void syncProperties() override;
172    virtual void onPropertyChanged(Properties* prop) override {
173        if (prop == &mStagingProperties) {
174            mStagingPropertiesDirty = true;
175            if (mPropertyChangedListener) {
176                mPropertyChangedListener->onStagingPropertyChanged();
177            }
178        } else if (prop == &mProperties){
179            mSkPathDirty = true;
180            if (mPropertyChangedListener) {
181                mPropertyChangedListener->onPropertyChanged();
182            }
183        }
184    }
185    PathProperties* mutateStagingProperties() { return &mStagingProperties; }
186    const PathProperties* stagingProperties() { return &mStagingProperties; }
187
188    // This should only be called from animations on RT
189    PathProperties* mutateProperties() { return &mProperties; }
190
191protected:
192    virtual const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath);
193
194    // Internal data, render thread only.
195    bool mSkPathDirty = true;
196    SkPath mSkPath;
197
198private:
199    PathProperties mProperties = PathProperties(this);
200    PathProperties mStagingProperties = PathProperties(this);
201    bool mStagingPropertiesDirty = true;
202};
203
204class ANDROID_API FullPath: public Path {
205public:
206    class FullPathProperties : public Properties {
207    public:
208        struct PrimitiveFields {
209            float strokeWidth = 0;
210            SkColor strokeColor = SK_ColorTRANSPARENT;
211            float strokeAlpha = 1;
212            SkColor fillColor = SK_ColorTRANSPARENT;
213            float fillAlpha = 1;
214            float trimPathStart = 0;
215            float trimPathEnd = 1;
216            float trimPathOffset = 0;
217            int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
218            int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
219            float strokeMiterLimit = 4;
220            int fillType = 0; /* non-zero or kWinding_FillType in Skia */
221        };
222        explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
223        ~FullPathProperties() {
224            SkSafeUnref(fillGradient);
225            SkSafeUnref(strokeGradient);
226        }
227        void syncProperties(const FullPathProperties& prop) {
228            mPrimitiveFields = prop.mPrimitiveFields;
229            mTrimDirty = true;
230            UPDATE_SKPROP(fillGradient, prop.fillGradient);
231            UPDATE_SKPROP(strokeGradient, prop.strokeGradient);
232            onPropertyChanged();
233        }
234        void setFillGradient(SkShader* gradient) {
235            if(UPDATE_SKPROP(fillGradient, gradient)) {
236                onPropertyChanged();
237            }
238        }
239        void setStrokeGradient(SkShader* gradient) {
240            if(UPDATE_SKPROP(strokeGradient, gradient)) {
241                onPropertyChanged();
242            }
243        }
244        SkShader* getFillGradient() const {
245            return fillGradient;
246        }
247        SkShader* getStrokeGradient() const {
248            return strokeGradient;
249        }
250        float getStrokeWidth() const{
251            return mPrimitiveFields.strokeWidth;
252        }
253        void setStrokeWidth(float strokeWidth) {
254            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
255        }
256        SkColor getStrokeColor() const{
257            return mPrimitiveFields.strokeColor;
258        }
259        void setStrokeColor(SkColor strokeColor) {
260            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor);
261        }
262        float getStrokeAlpha() const{
263            return mPrimitiveFields.strokeAlpha;
264        }
265        void setStrokeAlpha(float strokeAlpha) {
266            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha);
267        }
268        SkColor getFillColor() const {
269            return mPrimitiveFields.fillColor;
270        }
271        void setFillColor(SkColor fillColor) {
272            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor);
273        }
274        float getFillAlpha() const{
275            return mPrimitiveFields.fillAlpha;
276        }
277        void setFillAlpha(float fillAlpha) {
278            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha);
279        }
280        float getTrimPathStart() const{
281            return mPrimitiveFields.trimPathStart;
282        }
283        void setTrimPathStart(float trimPathStart) {
284            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty);
285        }
286        float getTrimPathEnd() const{
287            return mPrimitiveFields.trimPathEnd;
288        }
289        void setTrimPathEnd(float trimPathEnd) {
290            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty);
291        }
292        float getTrimPathOffset() const{
293            return mPrimitiveFields.trimPathOffset;
294        }
295        void setTrimPathOffset(float trimPathOffset) {
296            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty);
297        }
298
299        float getStrokeMiterLimit() const {
300            return mPrimitiveFields.strokeMiterLimit;
301        }
302        float getStrokeLineCap() const {
303            return mPrimitiveFields.strokeLineCap;
304        }
305        float getStrokeLineJoin() const {
306            return mPrimitiveFields.strokeLineJoin;
307        }
308        float getFillType() const {
309            return mPrimitiveFields.fillType;
310        }
311        bool copyProperties(int8_t* outProperties, int length) const;
312        void updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
313                SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
314                float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin,
315                int fillType) {
316            mPrimitiveFields.strokeWidth = strokeWidth;
317            mPrimitiveFields.strokeColor = strokeColor;
318            mPrimitiveFields.strokeAlpha = strokeAlpha;
319            mPrimitiveFields.fillColor = fillColor;
320            mPrimitiveFields.fillAlpha = fillAlpha;
321            mPrimitiveFields.trimPathStart = trimPathStart;
322            mPrimitiveFields.trimPathEnd = trimPathEnd;
323            mPrimitiveFields.trimPathOffset = trimPathOffset;
324            mPrimitiveFields.strokeMiterLimit = strokeMiterLimit;
325            mPrimitiveFields.strokeLineCap = strokeLineCap;
326            mPrimitiveFields.strokeLineJoin = strokeLineJoin;
327            mPrimitiveFields.fillType = fillType;
328            mTrimDirty = true;
329            onPropertyChanged();
330        }
331        // Set property values during animation
332        void setColorPropertyValue(int propertyId, int32_t value);
333        void setPropertyValue(int propertyId, float value);
334        bool mTrimDirty;
335    private:
336        enum class Property {
337            strokeWidth = 0,
338            strokeColor,
339            strokeAlpha,
340            fillColor,
341            fillAlpha,
342            trimPathStart,
343            trimPathEnd,
344            trimPathOffset,
345            strokeLineCap,
346            strokeLineJoin,
347            strokeMiterLimit,
348            fillType,
349            count,
350        };
351        PrimitiveFields mPrimitiveFields;
352        SkShader* fillGradient = nullptr;
353        SkShader* strokeGradient = nullptr;
354    };
355
356    // Called from UI thread
357    FullPath(const FullPath& path); // for cloning
358    FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
359    FullPath() : Path() {}
360    void draw(SkCanvas* outCanvas, bool useStagingData) override;
361    void dump() override;
362    FullPathProperties* mutateStagingProperties() { return &mStagingProperties; }
363    const FullPathProperties* stagingProperties() { return &mStagingProperties; }
364
365    // This should only be called from animations on RT
366    FullPathProperties* mutateProperties() { return &mProperties; }
367
368    virtual void syncProperties() override;
369    virtual void onPropertyChanged(Properties* properties) override {
370        Path::onPropertyChanged(properties);
371        if (properties == &mStagingProperties) {
372            mStagingPropertiesDirty = true;
373            if (mPropertyChangedListener) {
374                mPropertyChangedListener->onStagingPropertyChanged();
375            }
376        } else if (properties == &mProperties) {
377            if (mPropertyChangedListener) {
378                mPropertyChangedListener->onPropertyChanged();
379            }
380        }
381    }
382
383protected:
384    const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
385private:
386
387    FullPathProperties mProperties = FullPathProperties(this);
388    FullPathProperties mStagingProperties = FullPathProperties(this);
389    bool mStagingPropertiesDirty = true;
390
391    // Intermediate data for drawing, render thread only
392    SkPath mTrimmedSkPath;
393
394};
395
396class ANDROID_API ClipPath: public Path {
397public:
398    ClipPath(const ClipPath& path) : Path(path) {}
399    ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
400    ClipPath() : Path() {}
401    void draw(SkCanvas* outCanvas, bool useStagingData) override;
402};
403
404class ANDROID_API Group: public Node {
405public:
406    class GroupProperties : public Properties {
407    public:
408        explicit GroupProperties(Node* mNode) : Properties(mNode) {}
409        struct PrimitiveFields {
410            float rotate = 0;
411            float pivotX = 0;
412            float pivotY = 0;
413            float scaleX = 1;
414            float scaleY = 1;
415            float translateX = 0;
416            float translateY = 0;
417        } mPrimitiveFields;
418        void syncProperties(const GroupProperties& prop) {
419            mPrimitiveFields = prop.mPrimitiveFields;
420            onPropertyChanged();
421        }
422        float getRotation() const {
423            return mPrimitiveFields.rotate;
424        }
425        void setRotation(float rotation) {
426            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation);
427        }
428        float getPivotX() const {
429            return mPrimitiveFields.pivotX;
430        }
431        void setPivotX(float pivotX) {
432            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX);
433        }
434        float getPivotY() const {
435            return mPrimitiveFields.pivotY;
436        }
437        void setPivotY(float pivotY) {
438            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY);
439        }
440        float getScaleX() const {
441            return mPrimitiveFields.scaleX;
442        }
443        void setScaleX(float scaleX) {
444            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX);
445        }
446        float getScaleY() const {
447            return mPrimitiveFields.scaleY;
448        }
449        void setScaleY(float scaleY) {
450            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY);
451        }
452        float getTranslateX() const {
453            return mPrimitiveFields.translateX;
454        }
455        void setTranslateX(float translateX) {
456            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX);
457        }
458        float getTranslateY() const {
459            return mPrimitiveFields.translateY;
460        }
461        void setTranslateY(float translateY) {
462            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY);
463        }
464        void updateProperties(float rotate, float pivotX, float pivotY,
465                float scaleX, float scaleY, float translateX, float translateY) {
466            mPrimitiveFields.rotate = rotate;
467            mPrimitiveFields.pivotX = pivotX;
468            mPrimitiveFields.pivotY = pivotY;
469            mPrimitiveFields.scaleX = scaleX;
470            mPrimitiveFields.scaleY = scaleY;
471            mPrimitiveFields.translateX = translateX;
472            mPrimitiveFields.translateY = translateY;
473            onPropertyChanged();
474        }
475        void setPropertyValue(int propertyId, float value);
476        float getPropertyValue(int propertyId) const;
477        bool copyProperties(float* outProperties, int length) const;
478        static bool isValidProperty(int propertyId);
479    private:
480        enum class Property {
481            rotate = 0,
482            pivotX,
483            pivotY,
484            scaleX,
485            scaleY,
486            translateX,
487            translateY,
488            // Count of the properties, must be at the end.
489            count,
490        };
491    };
492
493    Group(const Group& group);
494    Group() {}
495    void addChild(Node* child);
496    virtual void setPropertyChangedListener(PropertyChangedListener* listener) override {
497        Node::setPropertyChangedListener(listener);
498        for (auto& child : mChildren) {
499             child->setPropertyChangedListener(listener);
500        }
501    }
502    virtual void syncProperties() override;
503    GroupProperties* mutateStagingProperties() { return &mStagingProperties; }
504    const GroupProperties* stagingProperties() { return &mStagingProperties; }
505
506    // This should only be called from animations on RT
507    GroupProperties* mutateProperties() { return &mProperties; }
508
509    // Methods below could be called from either UI thread or Render Thread.
510    virtual void draw(SkCanvas* outCanvas, bool useStagingData) override;
511    void getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties);
512    void dump() override;
513    static bool isValidProperty(int propertyId);
514
515    virtual void onPropertyChanged(Properties* properties) override {
516        if (properties == &mStagingProperties) {
517            mStagingPropertiesDirty = true;
518            if (mPropertyChangedListener) {
519                mPropertyChangedListener->onStagingPropertyChanged();
520            }
521        } else {
522            if (mPropertyChangedListener) {
523                mPropertyChangedListener->onPropertyChanged();
524            }
525        }
526    }
527
528private:
529    GroupProperties mProperties = GroupProperties(this);
530    GroupProperties mStagingProperties = GroupProperties(this);
531    bool mStagingPropertiesDirty = true;
532    std::vector< std::unique_ptr<Node> > mChildren;
533};
534
535class ANDROID_API Tree : public VirtualLightRefBase {
536public:
537    explicit Tree(Group* rootNode) : mRootNode(rootNode) {
538        mRootNode->setPropertyChangedListener(&mPropertyChangedListener);
539    }
540
541    // Copy properties from the tree and use the give node as the root node
542    Tree(const Tree* copy, Group* rootNode) : Tree(rootNode) {
543        mStagingProperties.syncAnimatableProperties(*copy->stagingProperties());
544        mStagingProperties.syncNonAnimatableProperties(*copy->stagingProperties());
545    }
546    // Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input
547    // canvas. Returns the number of pixels needed for the bitmap cache.
548    int draw(Canvas* outCanvas, SkColorFilter* colorFilter,
549            const SkRect& bounds, bool needsMirroring, bool canReuseCache);
550    void drawStaging(Canvas* canvas);
551
552    Bitmap& getBitmapUpdateIfDirty();
553    void setAllowCaching(bool allowCaching) {
554        mAllowCaching = allowCaching;
555    }
556    SkPaint* getPaint();
557    void syncProperties() {
558        if (mStagingProperties.mNonAnimatablePropertiesDirty) {
559            mProperties.syncNonAnimatableProperties(mStagingProperties);
560            mStagingProperties.mNonAnimatablePropertiesDirty = false;
561        }
562
563        if (mStagingProperties.mAnimatablePropertiesDirty) {
564            mProperties.syncAnimatableProperties(mStagingProperties);
565        } else {
566            mStagingProperties.syncAnimatableProperties(mProperties);
567        }
568        mStagingProperties.mAnimatablePropertiesDirty = false;
569        mRootNode->syncProperties();
570    }
571
572    class TreeProperties {
573    public:
574        explicit TreeProperties(Tree* tree) : mTree(tree) {}
575        // Properties that can only be modified by UI thread, therefore sync should
576        // only go from UI to RT
577        struct NonAnimatableProperties {
578            float viewportWidth = 0;
579            float viewportHeight = 0;
580            SkRect bounds;
581            int scaledWidth = 0;
582            int scaledHeight = 0;
583            SkColorFilter* colorFilter = nullptr;
584            ~NonAnimatableProperties() {
585                SkSafeUnref(colorFilter);
586            }
587        } mNonAnimatableProperties;
588        bool mNonAnimatablePropertiesDirty = true;
589
590        float mRootAlpha = 1.0f;
591        bool mAnimatablePropertiesDirty = true;
592
593        void syncNonAnimatableProperties(const TreeProperties& prop) {
594            // Copy over the data that can only be changed in UI thread
595            if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
596                SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
597                        prop.mNonAnimatableProperties.colorFilter);
598            }
599            mNonAnimatableProperties = prop.mNonAnimatableProperties;
600        }
601
602        void setViewportSize(float width, float height) {
603            if (mNonAnimatableProperties.viewportWidth != width
604                    || mNonAnimatableProperties.viewportHeight != height) {
605                mNonAnimatablePropertiesDirty = true;
606                mNonAnimatableProperties.viewportWidth = width;
607                mNonAnimatableProperties.viewportHeight = height;
608                mTree->onPropertyChanged(this);
609            }
610        }
611        void setBounds(const SkRect& bounds) {
612            if (mNonAnimatableProperties.bounds != bounds) {
613                mNonAnimatableProperties.bounds = bounds;
614                mNonAnimatablePropertiesDirty = true;
615                mTree->onPropertyChanged(this);
616            }
617        }
618
619        void setScaledSize(int width, int height) {
620            // If the requested size is bigger than what the bitmap was, then
621            // we increase the bitmap size to match. The width and height
622            // are bound by MAX_CACHED_BITMAP_SIZE.
623            if (mNonAnimatableProperties.scaledWidth < width
624                    || mNonAnimatableProperties.scaledHeight < height) {
625                mNonAnimatableProperties.scaledWidth = std::max(width,
626                        mNonAnimatableProperties.scaledWidth);
627                mNonAnimatableProperties.scaledHeight = std::max(height,
628                        mNonAnimatableProperties.scaledHeight);
629                mNonAnimatablePropertiesDirty = true;
630                mTree->onPropertyChanged(this);
631            }
632        }
633        void setColorFilter(SkColorFilter* filter) {
634            if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
635                mNonAnimatablePropertiesDirty = true;
636                mTree->onPropertyChanged(this);
637            }
638        }
639        SkColorFilter* getColorFilter() const{
640            return mNonAnimatableProperties.colorFilter;
641        }
642
643        float getViewportWidth() const {
644            return mNonAnimatableProperties.viewportWidth;
645        }
646        float getViewportHeight() const {
647            return mNonAnimatableProperties.viewportHeight;
648        }
649        float getScaledWidth() const {
650            return mNonAnimatableProperties.scaledWidth;
651        }
652        float getScaledHeight() const {
653            return mNonAnimatableProperties.scaledHeight;
654        }
655        void syncAnimatableProperties(const TreeProperties& prop) {
656            mRootAlpha = prop.mRootAlpha;
657        }
658        bool setRootAlpha(float rootAlpha) {
659            if (rootAlpha != mRootAlpha) {
660                mAnimatablePropertiesDirty = true;
661                mRootAlpha = rootAlpha;
662                mTree->onPropertyChanged(this);
663                return true;
664            }
665            return false;
666        }
667        float getRootAlpha() const { return mRootAlpha;}
668        const SkRect& getBounds() const {
669            return mNonAnimatableProperties.bounds;
670        }
671        Tree* mTree;
672    };
673    void onPropertyChanged(TreeProperties* prop);
674    TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
675    const TreeProperties* stagingProperties() const { return &mStagingProperties; }
676
677    // This should only be called from animations on RT
678    TreeProperties* mutateProperties() { return &mProperties; }
679
680    // This should always be called from RT.
681    void markDirty() { mCache.dirty = true; }
682    bool isDirty() const { return mCache.dirty; }
683    bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
684    void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
685
686private:
687    struct Cache {
688        sk_sp<Bitmap> bitmap;
689        bool dirty = true;
690    };
691
692    SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
693    bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
694    bool canReuseBitmap(Bitmap*, int width, int height);
695    void updateBitmapCache(Bitmap& outCache, bool useStagingData);
696    // Cap the bitmap size, such that it won't hurt the performance too much
697    // and it won't crash due to a very large scale.
698    // The drawable will look blurry above this size.
699    const static int MAX_CACHED_BITMAP_SIZE;
700
701    bool mAllowCaching = true;
702    std::unique_ptr<Group> mRootNode;
703
704    TreeProperties mProperties = TreeProperties(this);
705    TreeProperties mStagingProperties = TreeProperties(this);
706
707    SkPaint mPaint;
708
709    Cache mStagingCache;
710    Cache mCache;
711
712    PropertyChangedListener mPropertyChangedListener
713            = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty);
714
715    mutable bool mWillBeConsumed = false;
716};
717
718} // namespace VectorDrawable
719
720typedef VectorDrawable::Path::Data PathData;
721} // namespace uirenderer
722} // namespace android
723
724#endif // ANDROID_HWUI_VPATH_H
725