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