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