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 
40 namespace android {
41 namespace 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 
50 namespace 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 
82 class PropertyChangedListener {
83 public:
PropertyChangedListener(bool * dirty,bool * stagingDirty)84     PropertyChangedListener(bool* dirty, bool* stagingDirty)
85             : mDirty(dirty), mStagingDirty(stagingDirty) {}
onPropertyChanged()86     void onPropertyChanged() {
87             *mDirty = true;
88     }
onStagingPropertyChanged()89     void onStagingPropertyChanged() {
90             *mStagingDirty = true;
91     }
92 private:
93     bool* mDirty;
94     bool* mStagingDirty;
95 };
96 
97 class ANDROID_API Node {
98 public:
99     class Properties {
100     public:
Properties(Node * node)101         explicit Properties(Node* node) : mNode(node) {}
onPropertyChanged()102         inline void onPropertyChanged() {
103             mNode->onPropertyChanged(this);
104         }
105     private:
106         Node* mNode;
107     };
Node(const Node & node)108     Node(const Node& node) {
109         mName = node.mName;
110     }
Node()111     Node() {}
112     virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0;
113     virtual void dump() = 0;
setName(const char * name)114     void setName(const char* name) {
115         mName = name;
116     }
setPropertyChangedListener(PropertyChangedListener * listener)117     virtual void setPropertyChangedListener(PropertyChangedListener* listener) {
118         mPropertyChangedListener = listener;
119     }
120     virtual void onPropertyChanged(Properties* properties) = 0;
~Node()121     virtual ~Node(){}
122     virtual void syncProperties() = 0;
123 protected:
124     std::string mName;
125     PropertyChangedListener* mPropertyChangedListener = nullptr;
126 };
127 
128 class ANDROID_API Path : public Node {
129 public:
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:
PathProperties(Node * node)142         explicit PathProperties(Node* node) : Properties(node) {}
syncProperties(const PathProperties & prop)143         void syncProperties(const PathProperties& prop) {
144             mData = prop.mData;
145             onPropertyChanged();
146         }
setData(const Data & data)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         }
getData()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);
Path()168     Path() {}
169 
170     void dump() override;
171     virtual void syncProperties() override;
onPropertyChanged(Properties * prop)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     }
mutateStagingProperties()185     PathProperties* mutateStagingProperties() { return &mStagingProperties; }
stagingProperties()186     const PathProperties* stagingProperties() { return &mStagingProperties; }
187 
188     // This should only be called from animations on RT
mutateProperties()189     PathProperties* mutateProperties() { return &mProperties; }
190 
191 protected:
192     virtual const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath);
193 
194     // Internal data, render thread only.
195     bool mSkPathDirty = true;
196     SkPath mSkPath;
197 
198 private:
199     PathProperties mProperties = PathProperties(this);
200     PathProperties mStagingProperties = PathProperties(this);
201     bool mStagingPropertiesDirty = true;
202 };
203 
204 class ANDROID_API FullPath: public Path {
205 public:
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         };
FullPathProperties(Node * mNode)222         explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
~FullPathProperties()223         ~FullPathProperties() {
224             SkSafeUnref(fillGradient);
225             SkSafeUnref(strokeGradient);
226         }
syncProperties(const FullPathProperties & prop)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         }
setFillGradient(SkShader * gradient)234         void setFillGradient(SkShader* gradient) {
235             if(UPDATE_SKPROP(fillGradient, gradient)) {
236                 onPropertyChanged();
237             }
238         }
setStrokeGradient(SkShader * gradient)239         void setStrokeGradient(SkShader* gradient) {
240             if(UPDATE_SKPROP(strokeGradient, gradient)) {
241                 onPropertyChanged();
242             }
243         }
getFillGradient()244         SkShader* getFillGradient() const {
245             return fillGradient;
246         }
getStrokeGradient()247         SkShader* getStrokeGradient() const {
248             return strokeGradient;
249         }
getStrokeWidth()250         float getStrokeWidth() const{
251             return mPrimitiveFields.strokeWidth;
252         }
setStrokeWidth(float strokeWidth)253         void setStrokeWidth(float strokeWidth) {
254             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
255         }
getStrokeColor()256         SkColor getStrokeColor() const{
257             return mPrimitiveFields.strokeColor;
258         }
setStrokeColor(SkColor strokeColor)259         void setStrokeColor(SkColor strokeColor) {
260             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor);
261         }
getStrokeAlpha()262         float getStrokeAlpha() const{
263             return mPrimitiveFields.strokeAlpha;
264         }
setStrokeAlpha(float strokeAlpha)265         void setStrokeAlpha(float strokeAlpha) {
266             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha);
267         }
getFillColor()268         SkColor getFillColor() const {
269             return mPrimitiveFields.fillColor;
270         }
setFillColor(SkColor fillColor)271         void setFillColor(SkColor fillColor) {
272             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor);
273         }
getFillAlpha()274         float getFillAlpha() const{
275             return mPrimitiveFields.fillAlpha;
276         }
setFillAlpha(float fillAlpha)277         void setFillAlpha(float fillAlpha) {
278             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha);
279         }
getTrimPathStart()280         float getTrimPathStart() const{
281             return mPrimitiveFields.trimPathStart;
282         }
setTrimPathStart(float trimPathStart)283         void setTrimPathStart(float trimPathStart) {
284             VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty);
285         }
getTrimPathEnd()286         float getTrimPathEnd() const{
287             return mPrimitiveFields.trimPathEnd;
288         }
setTrimPathEnd(float trimPathEnd)289         void setTrimPathEnd(float trimPathEnd) {
290             VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty);
291         }
getTrimPathOffset()292         float getTrimPathOffset() const{
293             return mPrimitiveFields.trimPathOffset;
294         }
setTrimPathOffset(float trimPathOffset)295         void setTrimPathOffset(float trimPathOffset) {
296             VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty);
297         }
298 
getStrokeMiterLimit()299         float getStrokeMiterLimit() const {
300             return mPrimitiveFields.strokeMiterLimit;
301         }
getStrokeLineCap()302         float getStrokeLineCap() const {
303             return mPrimitiveFields.strokeLineCap;
304         }
getStrokeLineJoin()305         float getStrokeLineJoin() const {
306             return mPrimitiveFields.strokeLineJoin;
307         }
getFillType()308         float getFillType() const {
309             return mPrimitiveFields.fillType;
310         }
311         bool copyProperties(int8_t* outProperties, int length) const;
updateProperties(float strokeWidth,SkColor strokeColor,float strokeAlpha,SkColor fillColor,float fillAlpha,float trimPathStart,float trimPathEnd,float trimPathOffset,float strokeMiterLimit,int strokeLineCap,int strokeLineJoin,int fillType)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
FullPath(const char * path,size_t strLength)358     FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
FullPath()359     FullPath() : Path() {}
360     void draw(SkCanvas* outCanvas, bool useStagingData) override;
361     void dump() override;
mutateStagingProperties()362     FullPathProperties* mutateStagingProperties() { return &mStagingProperties; }
stagingProperties()363     const FullPathProperties* stagingProperties() { return &mStagingProperties; }
364 
365     // This should only be called from animations on RT
mutateProperties()366     FullPathProperties* mutateProperties() { return &mProperties; }
367 
368     virtual void syncProperties() override;
onPropertyChanged(Properties * properties)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 
383 protected:
384     const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
385 private:
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 
396 class ANDROID_API ClipPath: public Path {
397 public:
ClipPath(const ClipPath & path)398     ClipPath(const ClipPath& path) : Path(path) {}
ClipPath(const char * path,size_t strLength)399     ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
ClipPath()400     ClipPath() : Path() {}
401     void draw(SkCanvas* outCanvas, bool useStagingData) override;
402 };
403 
404 class ANDROID_API Group: public Node {
405 public:
406     class GroupProperties : public Properties {
407     public:
GroupProperties(Node * mNode)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;
syncProperties(const GroupProperties & prop)418         void syncProperties(const GroupProperties& prop) {
419             mPrimitiveFields = prop.mPrimitiveFields;
420             onPropertyChanged();
421         }
getRotation()422         float getRotation() const {
423             return mPrimitiveFields.rotate;
424         }
setRotation(float rotation)425         void setRotation(float rotation) {
426             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation);
427         }
getPivotX()428         float getPivotX() const {
429             return mPrimitiveFields.pivotX;
430         }
setPivotX(float pivotX)431         void setPivotX(float pivotX) {
432             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX);
433         }
getPivotY()434         float getPivotY() const {
435             return mPrimitiveFields.pivotY;
436         }
setPivotY(float pivotY)437         void setPivotY(float pivotY) {
438             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY);
439         }
getScaleX()440         float getScaleX() const {
441             return mPrimitiveFields.scaleX;
442         }
setScaleX(float scaleX)443         void setScaleX(float scaleX) {
444             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX);
445         }
getScaleY()446         float getScaleY() const {
447             return mPrimitiveFields.scaleY;
448         }
setScaleY(float scaleY)449         void setScaleY(float scaleY) {
450             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY);
451         }
getTranslateX()452         float getTranslateX() const {
453             return mPrimitiveFields.translateX;
454         }
setTranslateX(float translateX)455         void setTranslateX(float translateX) {
456             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX);
457         }
getTranslateY()458         float getTranslateY() const {
459             return mPrimitiveFields.translateY;
460         }
setTranslateY(float translateY)461         void setTranslateY(float translateY) {
462             VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY);
463         }
updateProperties(float rotate,float pivotX,float pivotY,float scaleX,float scaleY,float translateX,float translateY)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);
Group()494     Group() {}
495     void addChild(Node* child);
setPropertyChangedListener(PropertyChangedListener * listener)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;
mutateStagingProperties()503     GroupProperties* mutateStagingProperties() { return &mStagingProperties; }
stagingProperties()504     const GroupProperties* stagingProperties() { return &mStagingProperties; }
505 
506     // This should only be called from animations on RT
mutateProperties()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 
onPropertyChanged(Properties * properties)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 
528 private:
529     GroupProperties mProperties = GroupProperties(this);
530     GroupProperties mStagingProperties = GroupProperties(this);
531     bool mStagingPropertiesDirty = true;
532     std::vector< std::unique_ptr<Node> > mChildren;
533 };
534 
535 class ANDROID_API Tree : public VirtualLightRefBase {
536 public:
Tree(Group * rootNode)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
Tree(const Tree * copy,Group * rootNode)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();
setAllowCaching(bool allowCaching)553     void setAllowCaching(bool allowCaching) {
554         mAllowCaching = allowCaching;
555     }
556     SkPaint* getPaint();
syncProperties()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:
TreeProperties(Tree * tree)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;
~NonAnimatablePropertiesNonAnimatableProperties584             ~NonAnimatableProperties() {
585                 SkSafeUnref(colorFilter);
586             }
587         } mNonAnimatableProperties;
588         bool mNonAnimatablePropertiesDirty = true;
589 
590         float mRootAlpha = 1.0f;
591         bool mAnimatablePropertiesDirty = true;
592 
syncNonAnimatableProperties(const TreeProperties & prop)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 
setViewportSize(float width,float height)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         }
setBounds(const SkRect & bounds)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 
setScaledSize(int width,int height)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         }
setColorFilter(SkColorFilter * filter)633         void setColorFilter(SkColorFilter* filter) {
634             if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
635                 mNonAnimatablePropertiesDirty = true;
636                 mTree->onPropertyChanged(this);
637             }
638         }
getColorFilter()639         SkColorFilter* getColorFilter() const{
640             return mNonAnimatableProperties.colorFilter;
641         }
642 
getViewportWidth()643         float getViewportWidth() const {
644             return mNonAnimatableProperties.viewportWidth;
645         }
getViewportHeight()646         float getViewportHeight() const {
647             return mNonAnimatableProperties.viewportHeight;
648         }
getScaledWidth()649         float getScaledWidth() const {
650             return mNonAnimatableProperties.scaledWidth;
651         }
getScaledHeight()652         float getScaledHeight() const {
653             return mNonAnimatableProperties.scaledHeight;
654         }
syncAnimatableProperties(const TreeProperties & prop)655         void syncAnimatableProperties(const TreeProperties& prop) {
656             mRootAlpha = prop.mRootAlpha;
657         }
setRootAlpha(float rootAlpha)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         }
getRootAlpha()667         float getRootAlpha() const { return mRootAlpha;}
getBounds()668         const SkRect& getBounds() const {
669             return mNonAnimatableProperties.bounds;
670         }
671         Tree* mTree;
672     };
673     void onPropertyChanged(TreeProperties* prop);
mutateStagingProperties()674     TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
stagingProperties()675     const TreeProperties* stagingProperties() const { return &mStagingProperties; }
676 
677     // This should only be called from animations on RT
mutateProperties()678     TreeProperties* mutateProperties() { return &mProperties; }
679 
680     // This should always be called from RT.
markDirty()681     void markDirty() { mCache.dirty = true; }
isDirty()682     bool isDirty() const { return mCache.dirty; }
getPropertyChangeWillBeConsumed()683     bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
setPropertyChangeWillBeConsumed(bool willBeConsumed)684     void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
685 
686 private:
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 
720 typedef VectorDrawable::Path::Data PathData;
721 } // namespace uirenderer
722 } // namespace android
723 
724 #endif // ANDROID_HWUI_VPATH_H
725