VectorDrawable.h revision 32d7cda0b89a114171f14de0753674090b3d75fc
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        void syncProperties(const FullPathProperties& prop) {
223            mPrimitiveFields = prop.mPrimitiveFields;
224            mTrimDirty = true;
225            fillGradient.reset(prop.fillGradient);
226            strokeGradient.reset(prop.strokeGradient);
227            onPropertyChanged();
228        }
229        void setFillGradient(SkShader* gradient) {
230            if(fillGradient != gradient){
231                fillGradient.reset(gradient);
232                onPropertyChanged();
233            }
234        }
235        void setStrokeGradient(SkShader* gradient) {
236            if(strokeGradient != gradient){
237                strokeGradient.reset(gradient);
238                onPropertyChanged();
239            }
240        }
241        SkShader* getFillGradient() const {
242            return fillGradient;
243        }
244        SkShader* getStrokeGradient() const {
245            return strokeGradient;
246        }
247        float getStrokeWidth() const{
248            return mPrimitiveFields.strokeWidth;
249        }
250        void setStrokeWidth(float strokeWidth) {
251            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
252        }
253        SkColor getStrokeColor() const{
254            return mPrimitiveFields.strokeColor;
255        }
256        void setStrokeColor(SkColor strokeColor) {
257            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor);
258        }
259        float getStrokeAlpha() const{
260            return mPrimitiveFields.strokeAlpha;
261        }
262        void setStrokeAlpha(float strokeAlpha) {
263            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha);
264        }
265        SkColor getFillColor() const {
266            return mPrimitiveFields.fillColor;
267        }
268        void setFillColor(SkColor fillColor) {
269            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor);
270        }
271        float getFillAlpha() const{
272            return mPrimitiveFields.fillAlpha;
273        }
274        void setFillAlpha(float fillAlpha) {
275            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha);
276        }
277        float getTrimPathStart() const{
278            return mPrimitiveFields.trimPathStart;
279        }
280        void setTrimPathStart(float trimPathStart) {
281            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty);
282        }
283        float getTrimPathEnd() const{
284            return mPrimitiveFields.trimPathEnd;
285        }
286        void setTrimPathEnd(float trimPathEnd) {
287            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty);
288        }
289        float getTrimPathOffset() const{
290            return mPrimitiveFields.trimPathOffset;
291        }
292        void setTrimPathOffset(float trimPathOffset) {
293            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty);
294        }
295
296        float getStrokeMiterLimit() const {
297            return mPrimitiveFields.strokeMiterLimit;
298        }
299        float getStrokeLineCap() const {
300            return mPrimitiveFields.strokeLineCap;
301        }
302        float getStrokeLineJoin() const {
303            return mPrimitiveFields.strokeLineJoin;
304        }
305        float getFillType() const {
306            return mPrimitiveFields.fillType;
307        }
308        bool copyProperties(int8_t* outProperties, int length) const;
309        void updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
310                SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
311                float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin,
312                int fillType) {
313            mPrimitiveFields.strokeWidth = strokeWidth;
314            mPrimitiveFields.strokeColor = strokeColor;
315            mPrimitiveFields.strokeAlpha = strokeAlpha;
316            mPrimitiveFields.fillColor = fillColor;
317            mPrimitiveFields.fillAlpha = fillAlpha;
318            mPrimitiveFields.trimPathStart = trimPathStart;
319            mPrimitiveFields.trimPathEnd = trimPathEnd;
320            mPrimitiveFields.trimPathOffset = trimPathOffset;
321            mPrimitiveFields.strokeMiterLimit = strokeMiterLimit;
322            mPrimitiveFields.strokeLineCap = strokeLineCap;
323            mPrimitiveFields.strokeLineJoin = strokeLineJoin;
324            mPrimitiveFields.fillType = fillType;
325            mTrimDirty = true;
326            onPropertyChanged();
327        }
328        // Set property values during animation
329        void setColorPropertyValue(int propertyId, int32_t value);
330        void setPropertyValue(int propertyId, float value);
331        bool mTrimDirty;
332    private:
333        enum class Property {
334            strokeWidth = 0,
335            strokeColor,
336            strokeAlpha,
337            fillColor,
338            fillAlpha,
339            trimPathStart,
340            trimPathEnd,
341            trimPathOffset,
342            strokeLineCap,
343            strokeLineJoin,
344            strokeMiterLimit,
345            fillType,
346            count,
347        };
348        PrimitiveFields mPrimitiveFields;
349        SkAutoTUnref<SkShader> fillGradient;
350        SkAutoTUnref<SkShader> strokeGradient;
351    };
352
353    // Called from UI thread
354    FullPath(const FullPath& path); // for cloning
355    FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
356    FullPath() : Path() {}
357    void dump() override;
358    FullPathProperties* mutateStagingProperties() { return &mStagingProperties; }
359    const FullPathProperties* stagingProperties() { return &mStagingProperties; }
360
361    // This should only be called from animations on RT
362    FullPathProperties* mutateProperties() { return &mProperties; }
363
364    virtual void syncProperties() override;
365    virtual void onPropertyChanged(Properties* properties) override {
366        Path::onPropertyChanged(properties);
367        if (properties == &mStagingProperties) {
368            mStagingPropertiesDirty = true;
369            if (mPropertyChangedListener) {
370                mPropertyChangedListener->onStagingPropertyChanged();
371            }
372        } else if (properties == &mProperties) {
373            if (mPropertyChangedListener) {
374                mPropertyChangedListener->onPropertyChanged();
375            }
376        }
377    }
378
379protected:
380    const SkPath& getUpdatedPath() override;
381    void getStagingPath(SkPath* outPath) override;
382    void drawPath(SkCanvas* outCanvas, SkPath& renderPath,
383            float strokeScale, const SkMatrix& matrix, bool useStagingData) override;
384private:
385
386    FullPathProperties mProperties = FullPathProperties(this);
387    FullPathProperties mStagingProperties = FullPathProperties(this);
388    bool mStagingPropertiesDirty = true;
389
390    // Intermediate data for drawing, render thread only
391    SkPath mTrimmedSkPath;
392
393};
394
395class ANDROID_API ClipPath: public Path {
396public:
397    ClipPath(const ClipPath& path) : Path(path) {}
398    ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
399    ClipPath() : Path() {}
400
401protected:
402    void drawPath(SkCanvas* outCanvas, SkPath& renderPath,
403            float strokeScale, const SkMatrix& matrix, bool useStagingData) override;
404};
405
406class ANDROID_API Group: public Node {
407public:
408    class GroupProperties : public Properties {
409    public:
410        GroupProperties(Node* mNode) : Properties(mNode) {}
411        struct PrimitiveFields {
412            float rotate = 0;
413            float pivotX = 0;
414            float pivotY = 0;
415            float scaleX = 1;
416            float scaleY = 1;
417            float translateX = 0;
418            float translateY = 0;
419        } mPrimitiveFields;
420        void syncProperties(const GroupProperties& prop) {
421            mPrimitiveFields = prop.mPrimitiveFields;
422            onPropertyChanged();
423        }
424        float getRotation() const {
425            return mPrimitiveFields.rotate;
426        }
427        void setRotation(float rotation) {
428            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation);
429        }
430        float getPivotX() const {
431            return mPrimitiveFields.pivotX;
432        }
433        void setPivotX(float pivotX) {
434            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX);
435        }
436        float getPivotY() const {
437            return mPrimitiveFields.pivotY;
438        }
439        void setPivotY(float pivotY) {
440            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY);
441        }
442        float getScaleX() const {
443            return mPrimitiveFields.scaleX;
444        }
445        void setScaleX(float scaleX) {
446            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX);
447        }
448        float getScaleY() const {
449            return mPrimitiveFields.scaleY;
450        }
451        void setScaleY(float scaleY) {
452            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY);
453        }
454        float getTranslateX() const {
455            return mPrimitiveFields.translateX;
456        }
457        void setTranslateX(float translateX) {
458            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX);
459        }
460        float getTranslateY() const {
461            return mPrimitiveFields.translateY;
462        }
463        void setTranslateY(float translateY) {
464            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY);
465        }
466        void updateProperties(float rotate, float pivotX, float pivotY,
467                float scaleX, float scaleY, float translateX, float translateY) {
468            mPrimitiveFields.rotate = rotate;
469            mPrimitiveFields.pivotX = pivotX;
470            mPrimitiveFields.pivotY = pivotY;
471            mPrimitiveFields.scaleX = scaleX;
472            mPrimitiveFields.scaleY = scaleY;
473            mPrimitiveFields.translateX = translateX;
474            mPrimitiveFields.translateY = translateY;
475            onPropertyChanged();
476        }
477        void setPropertyValue(int propertyId, float value);
478        float getPropertyValue(int propertyId) const;
479        bool copyProperties(float* outProperties, int length) const;
480        static bool isValidProperty(int propertyId);
481    private:
482        enum class Property {
483            rotate = 0,
484            pivotX,
485            pivotY,
486            scaleX,
487            scaleY,
488            translateX,
489            translateY,
490            // Count of the properties, must be at the end.
491            count,
492        };
493    };
494
495    Group(const Group& group);
496    Group() {}
497    void addChild(Node* child);
498    virtual void setPropertyChangedListener(PropertyChangedListener* listener) override {
499        Node::setPropertyChangedListener(listener);
500        for (auto& child : mChildren) {
501             child->setPropertyChangedListener(listener);
502        }
503    }
504    virtual void syncProperties() override;
505    GroupProperties* mutateStagingProperties() { return &mStagingProperties; }
506    const GroupProperties* stagingProperties() { return &mStagingProperties; }
507
508    // This should only be called from animations on RT
509    GroupProperties* mutateProperties() { return &mProperties; }
510
511    // Methods below could be called from either UI thread or Render Thread.
512    virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
513            float scaleX, float scaleY, bool useStagingData) override;
514    void getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties);
515    void dump() override;
516    static bool isValidProperty(int propertyId);
517
518    virtual void onPropertyChanged(Properties* properties) override {
519        if (properties == &mStagingProperties) {
520            mStagingPropertiesDirty = true;
521            if (mPropertyChangedListener) {
522                mPropertyChangedListener->onStagingPropertyChanged();
523            }
524        } else {
525            if (mPropertyChangedListener) {
526                mPropertyChangedListener->onPropertyChanged();
527            }
528        }
529    }
530
531private:
532    GroupProperties mProperties = GroupProperties(this);
533    GroupProperties mStagingProperties = GroupProperties(this);
534    bool mStagingPropertiesDirty = true;
535    std::vector< std::unique_ptr<Node> > mChildren;
536};
537
538class ANDROID_API Tree : public VirtualLightRefBase {
539public:
540    Tree(Group* rootNode) : mRootNode(rootNode) {
541        mRootNode->setPropertyChangedListener(&mPropertyChangedListener);
542    }
543    void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
544            const SkRect& bounds, bool needsMirroring, bool canReuseCache);
545    void drawStaging(Canvas* canvas);
546
547    const SkBitmap& getBitmapUpdateIfDirty();
548    void setAllowCaching(bool allowCaching) {
549        mAllowCaching = allowCaching;
550    }
551    SkPaint* getPaint();
552    void syncProperties() {
553        if (mStagingProperties.mNonAnimatablePropertiesDirty) {
554            mProperties.syncNonAnimatableProperties(mStagingProperties);
555            mStagingProperties.mNonAnimatablePropertiesDirty = false;
556        }
557
558        if (mStagingProperties.mAnimatablePropertiesDirty) {
559            mProperties.syncAnimatableProperties(mStagingProperties);
560        } else {
561            mStagingProperties.syncAnimatableProperties(mProperties);
562        }
563        mStagingProperties.mAnimatablePropertiesDirty = false;
564        mRootNode->syncProperties();
565    }
566
567    class TreeProperties {
568    public:
569        TreeProperties(Tree* tree) : mTree(tree) {}
570        // Properties that can only be modified by UI thread, therefore sync should
571        // only go from UI to RT
572        struct NonAnimatableProperties {
573            float viewportWidth = 0;
574            float viewportHeight = 0;
575            SkRect bounds;
576            int scaledWidth = 0;
577            int scaledHeight = 0;
578            SkColorFilter* colorFilter = nullptr;
579            ~NonAnimatableProperties() {
580                SkSafeUnref(colorFilter);
581            }
582        } mNonAnimatableProperties;
583        bool mNonAnimatablePropertiesDirty = true;
584
585        float mRootAlpha = 1.0f;
586        bool mAnimatablePropertiesDirty = true;
587
588        void syncNonAnimatableProperties(const TreeProperties& prop) {
589            // Copy over the data that can only be changed in UI thread
590            if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
591                SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
592                        prop.mNonAnimatableProperties.colorFilter);
593            }
594            mNonAnimatableProperties = prop.mNonAnimatableProperties;
595        }
596
597        void setViewportSize(float width, float height) {
598            if (mNonAnimatableProperties.viewportWidth != width
599                    || mNonAnimatableProperties.viewportHeight != height) {
600                mNonAnimatablePropertiesDirty = true;
601                mNonAnimatableProperties.viewportWidth = width;
602                mNonAnimatableProperties.viewportHeight = height;
603                mTree->onPropertyChanged(this);
604            }
605        }
606        void setBounds(const SkRect& bounds) {
607            if (mNonAnimatableProperties.bounds != bounds) {
608                mNonAnimatableProperties.bounds = bounds;
609                mNonAnimatablePropertiesDirty = true;
610                mTree->onPropertyChanged(this);
611            }
612        }
613
614        void setScaledSize(int width, int height) {
615            if (mNonAnimatableProperties.scaledWidth != width
616                    || mNonAnimatableProperties.scaledHeight != height) {
617                mNonAnimatableProperties.scaledWidth = width;
618                mNonAnimatableProperties.scaledHeight = height;
619                mNonAnimatablePropertiesDirty = true;
620                mTree->onPropertyChanged(this);
621            }
622        }
623        void setColorFilter(SkColorFilter* filter) {
624            if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
625                mNonAnimatablePropertiesDirty = true;
626                mTree->onPropertyChanged(this);
627            }
628        }
629        SkColorFilter* getColorFilter() const{
630            return mNonAnimatableProperties.colorFilter;
631        }
632
633        float getViewportWidth() const {
634            return mNonAnimatableProperties.viewportWidth;
635        }
636        float getViewportHeight() const {
637            return mNonAnimatableProperties.viewportHeight;
638        }
639        float getScaledWidth() const {
640            return mNonAnimatableProperties.scaledWidth;
641        }
642        float getScaledHeight() const {
643            return mNonAnimatableProperties.scaledHeight;
644        }
645        void syncAnimatableProperties(const TreeProperties& prop) {
646            mRootAlpha = prop.mRootAlpha;
647        }
648        bool setRootAlpha(float rootAlpha) {
649            if (rootAlpha != mRootAlpha) {
650                mAnimatablePropertiesDirty = true;
651                mRootAlpha = rootAlpha;
652                mTree->onPropertyChanged(this);
653                return true;
654            }
655            return false;
656        }
657        float getRootAlpha() const { return mRootAlpha;}
658        const SkRect& getBounds() const {
659            return mNonAnimatableProperties.bounds;
660        }
661        Tree* mTree;
662    };
663    void onPropertyChanged(TreeProperties* prop);
664    TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
665    const TreeProperties* stagingProperties() { return &mStagingProperties; }
666    PushStagingFunctor* getFunctor() { return &mFunctor;}
667
668    // This should only be called from animations on RT
669    TreeProperties* mutateProperties() { return &mProperties; }
670
671private:
672    class VectorDrawableFunctor : public PushStagingFunctor {
673    public:
674        VectorDrawableFunctor(Tree* tree) : mTree(tree) {}
675        virtual void operator ()() {
676            mTree->syncProperties();
677        }
678    private:
679        Tree* mTree;
680    };
681
682    SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
683    bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
684    bool canReuseBitmap(const SkBitmap&, int width, int height);
685    void updateBitmapCache(SkBitmap* outCache, bool useStagingData);
686    // Cap the bitmap size, such that it won't hurt the performance too much
687    // and it won't crash due to a very large scale.
688    // The drawable will look blurry above this size.
689    const static int MAX_CACHED_BITMAP_SIZE;
690
691    bool mAllowCaching = true;
692    std::unique_ptr<Group> mRootNode;
693
694    TreeProperties mProperties = TreeProperties(this);
695    TreeProperties mStagingProperties = TreeProperties(this);
696
697    VectorDrawableFunctor mFunctor = VectorDrawableFunctor(this);
698
699    SkPaint mPaint;
700    struct Cache {
701        SkBitmap bitmap;
702        bool dirty = true;
703    };
704
705    Cache mStagingCache;
706    Cache mCache;
707
708    PropertyChangedListener mPropertyChangedListener
709            = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty);
710};
711
712} // namespace VectorDrawable
713
714typedef VectorDrawable::Path::Data PathData;
715} // namespace uirenderer
716} // namespace android
717
718#endif // ANDROID_HWUI_VPATH_H
719