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