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 40namespace android { 41namespace 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 50namespace 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 82class PropertyChangedListener { 83public: 84 PropertyChangedListener(bool* dirty, bool* stagingDirty) 85 : mDirty(dirty), mStagingDirty(stagingDirty) {} 86 void onPropertyChanged() { 87 *mDirty = true; 88 } 89 void onStagingPropertyChanged() { 90 *mStagingDirty = true; 91 } 92private: 93 bool* mDirty; 94 bool* mStagingDirty; 95}; 96 97class ANDROID_API Node { 98public: 99 class Properties { 100 public: 101 explicit Properties(Node* node) : mNode(node) {} 102 inline void onPropertyChanged() { 103 mNode->onPropertyChanged(this); 104 } 105 private: 106 Node* mNode; 107 }; 108 Node(const Node& node) { 109 mName = node.mName; 110 } 111 Node() {} 112 virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0; 113 virtual void dump() = 0; 114 void setName(const char* name) { 115 mName = name; 116 } 117 virtual void setPropertyChangedListener(PropertyChangedListener* listener) { 118 mPropertyChangedListener = listener; 119 } 120 virtual void onPropertyChanged(Properties* properties) = 0; 121 virtual ~Node(){} 122 virtual void syncProperties() = 0; 123protected: 124 std::string mName; 125 PropertyChangedListener* mPropertyChangedListener = nullptr; 126}; 127 128class ANDROID_API Path : public Node { 129public: 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: 142 explicit PathProperties(Node* node) : Properties(node) {} 143 void syncProperties(const PathProperties& prop) { 144 mData = prop.mData; 145 onPropertyChanged(); 146 } 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 } 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); 168 Path() {} 169 170 void dump() override; 171 virtual void syncProperties() override; 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 } 185 PathProperties* mutateStagingProperties() { return &mStagingProperties; } 186 const PathProperties* stagingProperties() { return &mStagingProperties; } 187 188 // This should only be called from animations on RT 189 PathProperties* mutateProperties() { return &mProperties; } 190 191protected: 192 virtual const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath); 193 194 // Internal data, render thread only. 195 bool mSkPathDirty = true; 196 SkPath mSkPath; 197 198private: 199 PathProperties mProperties = PathProperties(this); 200 PathProperties mStagingProperties = PathProperties(this); 201 bool mStagingPropertiesDirty = true; 202}; 203 204class ANDROID_API FullPath: public Path { 205public: 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 }; 222 explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {} 223 ~FullPathProperties() { 224 SkSafeUnref(fillGradient); 225 SkSafeUnref(strokeGradient); 226 } 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 } 234 void setFillGradient(SkShader* gradient) { 235 if(UPDATE_SKPROP(fillGradient, gradient)) { 236 onPropertyChanged(); 237 } 238 } 239 void setStrokeGradient(SkShader* gradient) { 240 if(UPDATE_SKPROP(strokeGradient, gradient)) { 241 onPropertyChanged(); 242 } 243 } 244 SkShader* getFillGradient() const { 245 return fillGradient; 246 } 247 SkShader* getStrokeGradient() const { 248 return strokeGradient; 249 } 250 float getStrokeWidth() const{ 251 return mPrimitiveFields.strokeWidth; 252 } 253 void setStrokeWidth(float strokeWidth) { 254 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth); 255 } 256 SkColor getStrokeColor() const{ 257 return mPrimitiveFields.strokeColor; 258 } 259 void setStrokeColor(SkColor strokeColor) { 260 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor); 261 } 262 float getStrokeAlpha() const{ 263 return mPrimitiveFields.strokeAlpha; 264 } 265 void setStrokeAlpha(float strokeAlpha) { 266 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha); 267 } 268 SkColor getFillColor() const { 269 return mPrimitiveFields.fillColor; 270 } 271 void setFillColor(SkColor fillColor) { 272 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor); 273 } 274 float getFillAlpha() const{ 275 return mPrimitiveFields.fillAlpha; 276 } 277 void setFillAlpha(float fillAlpha) { 278 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha); 279 } 280 float getTrimPathStart() const{ 281 return mPrimitiveFields.trimPathStart; 282 } 283 void setTrimPathStart(float trimPathStart) { 284 VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty); 285 } 286 float getTrimPathEnd() const{ 287 return mPrimitiveFields.trimPathEnd; 288 } 289 void setTrimPathEnd(float trimPathEnd) { 290 VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty); 291 } 292 float getTrimPathOffset() const{ 293 return mPrimitiveFields.trimPathOffset; 294 } 295 void setTrimPathOffset(float trimPathOffset) { 296 VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty); 297 } 298 299 float getStrokeMiterLimit() const { 300 return mPrimitiveFields.strokeMiterLimit; 301 } 302 float getStrokeLineCap() const { 303 return mPrimitiveFields.strokeLineCap; 304 } 305 float getStrokeLineJoin() const { 306 return mPrimitiveFields.strokeLineJoin; 307 } 308 float getFillType() const { 309 return mPrimitiveFields.fillType; 310 } 311 bool copyProperties(int8_t* outProperties, int length) const; 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 358 FullPath(const char* path, size_t strLength) : Path(path, strLength) {} 359 FullPath() : Path() {} 360 void draw(SkCanvas* outCanvas, bool useStagingData) override; 361 void dump() override; 362 FullPathProperties* mutateStagingProperties() { return &mStagingProperties; } 363 const FullPathProperties* stagingProperties() { return &mStagingProperties; } 364 365 // This should only be called from animations on RT 366 FullPathProperties* mutateProperties() { return &mProperties; } 367 368 virtual void syncProperties() override; 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 383protected: 384 const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override; 385private: 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 396class ANDROID_API ClipPath: public Path { 397public: 398 ClipPath(const ClipPath& path) : Path(path) {} 399 ClipPath(const char* path, size_t strLength) : Path(path, strLength) {} 400 ClipPath() : Path() {} 401 void draw(SkCanvas* outCanvas, bool useStagingData) override; 402}; 403 404class ANDROID_API Group: public Node { 405public: 406 class GroupProperties : public Properties { 407 public: 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; 418 void syncProperties(const GroupProperties& prop) { 419 mPrimitiveFields = prop.mPrimitiveFields; 420 onPropertyChanged(); 421 } 422 float getRotation() const { 423 return mPrimitiveFields.rotate; 424 } 425 void setRotation(float rotation) { 426 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation); 427 } 428 float getPivotX() const { 429 return mPrimitiveFields.pivotX; 430 } 431 void setPivotX(float pivotX) { 432 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX); 433 } 434 float getPivotY() const { 435 return mPrimitiveFields.pivotY; 436 } 437 void setPivotY(float pivotY) { 438 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY); 439 } 440 float getScaleX() const { 441 return mPrimitiveFields.scaleX; 442 } 443 void setScaleX(float scaleX) { 444 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX); 445 } 446 float getScaleY() const { 447 return mPrimitiveFields.scaleY; 448 } 449 void setScaleY(float scaleY) { 450 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY); 451 } 452 float getTranslateX() const { 453 return mPrimitiveFields.translateX; 454 } 455 void setTranslateX(float translateX) { 456 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX); 457 } 458 float getTranslateY() const { 459 return mPrimitiveFields.translateY; 460 } 461 void setTranslateY(float translateY) { 462 VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY); 463 } 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); 494 Group() {} 495 void addChild(Node* child); 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; 503 GroupProperties* mutateStagingProperties() { return &mStagingProperties; } 504 const GroupProperties* stagingProperties() { return &mStagingProperties; } 505 506 // This should only be called from animations on RT 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 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 528private: 529 GroupProperties mProperties = GroupProperties(this); 530 GroupProperties mStagingProperties = GroupProperties(this); 531 bool mStagingPropertiesDirty = true; 532 std::vector< std::unique_ptr<Node> > mChildren; 533}; 534 535class ANDROID_API Tree : public VirtualLightRefBase { 536public: 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 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(); 553 void setAllowCaching(bool allowCaching) { 554 mAllowCaching = allowCaching; 555 } 556 SkPaint* getPaint(); 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: 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; 584 ~NonAnimatableProperties() { 585 SkSafeUnref(colorFilter); 586 } 587 } mNonAnimatableProperties; 588 bool mNonAnimatablePropertiesDirty = true; 589 590 float mRootAlpha = 1.0f; 591 bool mAnimatablePropertiesDirty = true; 592 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 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 } 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 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 } 633 void setColorFilter(SkColorFilter* filter) { 634 if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) { 635 mNonAnimatablePropertiesDirty = true; 636 mTree->onPropertyChanged(this); 637 } 638 } 639 SkColorFilter* getColorFilter() const{ 640 return mNonAnimatableProperties.colorFilter; 641 } 642 643 float getViewportWidth() const { 644 return mNonAnimatableProperties.viewportWidth; 645 } 646 float getViewportHeight() const { 647 return mNonAnimatableProperties.viewportHeight; 648 } 649 float getScaledWidth() const { 650 return mNonAnimatableProperties.scaledWidth; 651 } 652 float getScaledHeight() const { 653 return mNonAnimatableProperties.scaledHeight; 654 } 655 void syncAnimatableProperties(const TreeProperties& prop) { 656 mRootAlpha = prop.mRootAlpha; 657 } 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 } 667 float getRootAlpha() const { return mRootAlpha;} 668 const SkRect& getBounds() const { 669 return mNonAnimatableProperties.bounds; 670 } 671 Tree* mTree; 672 }; 673 void onPropertyChanged(TreeProperties* prop); 674 TreeProperties* mutateStagingProperties() { return &mStagingProperties; } 675 const TreeProperties* stagingProperties() const { return &mStagingProperties; } 676 677 // This should only be called from animations on RT 678 TreeProperties* mutateProperties() { return &mProperties; } 679 680 // This should always be called from RT. 681 void markDirty() { mCache.dirty = true; } 682 bool isDirty() const { return mCache.dirty; } 683 bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; } 684 void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; } 685 686private: 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 720typedef VectorDrawable::Path::Data PathData; 721} // namespace uirenderer 722} // namespace android 723 724#endif // ANDROID_HWUI_VPATH_H 725