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