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