VectorDrawable.h revision f8d131cc8dc4ef675b8f8fc57dcc26062d575d32
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    // Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input
546    // canvas. Returns the number of pixels needed for the bitmap cache.
547    int draw(Canvas* outCanvas, SkColorFilter* colorFilter,
548            const SkRect& bounds, bool needsMirroring, bool canReuseCache);
549    void drawStaging(Canvas* canvas);
550
551    const SkBitmap& getBitmapUpdateIfDirty();
552    void setAllowCaching(bool allowCaching) {
553        mAllowCaching = allowCaching;
554    }
555    SkPaint* getPaint();
556    void syncProperties() {
557        if (mStagingProperties.mNonAnimatablePropertiesDirty) {
558            mProperties.syncNonAnimatableProperties(mStagingProperties);
559            mStagingProperties.mNonAnimatablePropertiesDirty = false;
560        }
561
562        if (mStagingProperties.mAnimatablePropertiesDirty) {
563            mProperties.syncAnimatableProperties(mStagingProperties);
564        } else {
565            mStagingProperties.syncAnimatableProperties(mProperties);
566        }
567        mStagingProperties.mAnimatablePropertiesDirty = false;
568        mRootNode->syncProperties();
569    }
570
571    class TreeProperties {
572    public:
573        TreeProperties(Tree* tree) : mTree(tree) {}
574        // Properties that can only be modified by UI thread, therefore sync should
575        // only go from UI to RT
576        struct NonAnimatableProperties {
577            float viewportWidth = 0;
578            float viewportHeight = 0;
579            SkRect bounds;
580            int scaledWidth = 0;
581            int scaledHeight = 0;
582            SkColorFilter* colorFilter = nullptr;
583            ~NonAnimatableProperties() {
584                SkSafeUnref(colorFilter);
585            }
586        } mNonAnimatableProperties;
587        bool mNonAnimatablePropertiesDirty = true;
588
589        float mRootAlpha = 1.0f;
590        bool mAnimatablePropertiesDirty = true;
591
592        void syncNonAnimatableProperties(const TreeProperties& prop) {
593            // Copy over the data that can only be changed in UI thread
594            if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
595                SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
596                        prop.mNonAnimatableProperties.colorFilter);
597            }
598            mNonAnimatableProperties = prop.mNonAnimatableProperties;
599        }
600
601        void setViewportSize(float width, float height) {
602            if (mNonAnimatableProperties.viewportWidth != width
603                    || mNonAnimatableProperties.viewportHeight != height) {
604                mNonAnimatablePropertiesDirty = true;
605                mNonAnimatableProperties.viewportWidth = width;
606                mNonAnimatableProperties.viewportHeight = height;
607                mTree->onPropertyChanged(this);
608            }
609        }
610        void setBounds(const SkRect& bounds) {
611            if (mNonAnimatableProperties.bounds != bounds) {
612                mNonAnimatableProperties.bounds = bounds;
613                mNonAnimatablePropertiesDirty = true;
614                mTree->onPropertyChanged(this);
615            }
616        }
617
618        void setScaledSize(int width, int height) {
619            if (mNonAnimatableProperties.scaledWidth != width
620                    || mNonAnimatableProperties.scaledHeight != height) {
621                mNonAnimatableProperties.scaledWidth = width;
622                mNonAnimatableProperties.scaledHeight = height;
623                mNonAnimatablePropertiesDirty = true;
624                mTree->onPropertyChanged(this);
625            }
626        }
627        void setColorFilter(SkColorFilter* filter) {
628            if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
629                mNonAnimatablePropertiesDirty = true;
630                mTree->onPropertyChanged(this);
631            }
632        }
633        SkColorFilter* getColorFilter() const{
634            return mNonAnimatableProperties.colorFilter;
635        }
636
637        float getViewportWidth() const {
638            return mNonAnimatableProperties.viewportWidth;
639        }
640        float getViewportHeight() const {
641            return mNonAnimatableProperties.viewportHeight;
642        }
643        float getScaledWidth() const {
644            return mNonAnimatableProperties.scaledWidth;
645        }
646        float getScaledHeight() const {
647            return mNonAnimatableProperties.scaledHeight;
648        }
649        void syncAnimatableProperties(const TreeProperties& prop) {
650            mRootAlpha = prop.mRootAlpha;
651        }
652        bool setRootAlpha(float rootAlpha) {
653            if (rootAlpha != mRootAlpha) {
654                mAnimatablePropertiesDirty = true;
655                mRootAlpha = rootAlpha;
656                mTree->onPropertyChanged(this);
657                return true;
658            }
659            return false;
660        }
661        float getRootAlpha() const { return mRootAlpha;}
662        const SkRect& getBounds() const {
663            return mNonAnimatableProperties.bounds;
664        }
665        Tree* mTree;
666    };
667    void onPropertyChanged(TreeProperties* prop);
668    TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
669    const TreeProperties* stagingProperties() { return &mStagingProperties; }
670    PushStagingFunctor* getFunctor() { return &mFunctor;}
671
672    // This should only be called from animations on RT
673    TreeProperties* mutateProperties() { return &mProperties; }
674
675private:
676    class VectorDrawableFunctor : public PushStagingFunctor {
677    public:
678        VectorDrawableFunctor(Tree* tree) : mTree(tree) {}
679        virtual void operator ()() {
680            mTree->syncProperties();
681        }
682    private:
683        Tree* mTree;
684    };
685
686    SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
687    bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
688    bool canReuseBitmap(const SkBitmap&, int width, int height);
689    void updateBitmapCache(SkBitmap* outCache, bool useStagingData);
690    // Cap the bitmap size, such that it won't hurt the performance too much
691    // and it won't crash due to a very large scale.
692    // The drawable will look blurry above this size.
693    const static int MAX_CACHED_BITMAP_SIZE;
694
695    bool mAllowCaching = true;
696    std::unique_ptr<Group> mRootNode;
697
698    TreeProperties mProperties = TreeProperties(this);
699    TreeProperties mStagingProperties = TreeProperties(this);
700
701    VectorDrawableFunctor mFunctor = VectorDrawableFunctor(this);
702
703    SkPaint mPaint;
704    struct Cache {
705        SkBitmap bitmap;
706        bool dirty = true;
707    };
708
709    Cache mStagingCache;
710    Cache mCache;
711
712    PropertyChangedListener mPropertyChangedListener
713            = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty);
714};
715
716} // namespace VectorDrawable
717
718typedef VectorDrawable::Path::Data PathData;
719} // namespace uirenderer
720} // namespace android
721
722#endif // ANDROID_HWUI_VPATH_H
723