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