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 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 Path : public Node { 131 public: 132 struct 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 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 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 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 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(Paint* 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