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