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