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