| /* | |
| * Copyright (C) 2015 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #ifndef ANDROID_HWUI_VPATH_H | |
| #define ANDROID_HWUI_VPATH_H | |
| #include "DisplayList.h" | |
| #include "hwui/Bitmap.h" | |
| #include "hwui/Canvas.h" | |
| #include "renderthread/CacheManager.h" | |
| #include <SkBitmap.h> | |
| #include <SkCanvas.h> | |
| #include <SkColor.h> | |
| #include <SkColorFilter.h> | |
| #include <SkMatrix.h> | |
| #include <SkPaint.h> | |
| #include <SkPath.h> | |
| #include <SkPathMeasure.h> | |
| #include <SkRect.h> | |
| #include <SkShader.h> | |
| #include <SkSurface.h> | |
| #include <cutils/compiler.h> | |
| #include <stddef.h> | |
| #include <string> | |
| #include <vector> | |
| namespace android { | |
| namespace uirenderer { | |
| // Debug | |
| #if DEBUG_VECTOR_DRAWABLE | |
| #define VECTOR_DRAWABLE_LOGD(...) ALOGD(__VA_ARGS__) | |
| #else | |
| #define VECTOR_DRAWABLE_LOGD(...) | |
| #endif | |
| namespace VectorDrawable { | |
| #define VD_SET_PRIMITIVE_FIELD_WITH_FLAG(field, value, flag) \ | |
| (VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, (value)) ? ((flag) = true, true) : false) | |
| #define VD_SET_PROP(field, value) ((value) != (field) ? ((field) = (value), true) : false) | |
| #define VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, value) \ | |
| ({ \ | |
| bool retVal = VD_SET_PROP((mPrimitiveFields.field), (value)); \ | |
| onPropertyChanged(); \ | |
| retVal; \ | |
| }) | |
| /* A VectorDrawable is composed of a tree of nodes. | |
| * Each node can be a group node, or a path. | |
| * A group node can have groups or paths as children, but a path node has | |
| * no children. | |
| * One example can be: | |
| * Root Group | |
| * / | \ | |
| * Group Path Group | |
| * / \ | | |
| * Path Path Path | |
| * | |
| * VectorDrawables are drawn into bitmap caches first, then the caches are drawn to the given | |
| * canvas with root alpha applied. Two caches are maintained for VD, one in UI thread, the other in | |
| * Render Thread. A generation id is used to keep track of changes in the vector drawable tree. | |
| * Each cache has their own generation id to track whether they are up to date with the latest | |
| * change in the tree. | |
| * | |
| * Any property change to the vector drawable coming from UI thread (such as bulk setters to update | |
| * all the properties, and viewport change, etc.) are only modifying the staging properties. The | |
| * staging properties will then be marked dirty and will be pushed over to render thread properties | |
| * at sync point. If staging properties are not dirty at sync point, we sync backwards by updating | |
| * staging properties with render thread properties to reflect the latest animation value. | |
| * | |
| */ | |
| class PropertyChangedListener { | |
| public: | |
| PropertyChangedListener(bool* dirty, bool* stagingDirty) | |
| : mDirty(dirty), mStagingDirty(stagingDirty) {} | |
| void onPropertyChanged() { *mDirty = true; } | |
| void onStagingPropertyChanged() { *mStagingDirty = true; } | |
| private: | |
| bool* mDirty; | |
| bool* mStagingDirty; | |
| }; | |
| class Node { | |
| public: | |
| class Properties { | |
| public: | |
| explicit Properties(Node* node) : mNode(node) {} | |
| inline void onPropertyChanged() { mNode->onPropertyChanged(this); } | |
| private: | |
| Node* mNode; | |
| }; | |
| Node(const Node& node) { mName = node.mName; } | |
| Node() {} | |
| virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0; | |
| virtual void dump() = 0; | |
| void setName(const char* name) { mName = name; } | |
| virtual void setPropertyChangedListener(PropertyChangedListener* listener) { | |
| mPropertyChangedListener = listener; | |
| } | |
| virtual void onPropertyChanged(Properties* properties) = 0; | |
| virtual ~Node() {} | |
| virtual void syncProperties() = 0; | |
| virtual void setAntiAlias(bool aa) = 0; | |
| virtual void forEachFillColor(const std::function<void(SkColor)>& func) const { } | |
| protected: | |
| std::string mName; | |
| PropertyChangedListener* mPropertyChangedListener = nullptr; | |
| }; | |
| class Path : public Node { | |
| public: | |
| struct Data { | |
| std::vector<char> verbs; | |
| std::vector<size_t> verbSizes; | |
| std::vector<float> points; | |
| bool operator==(const Data& data) const { | |
| return verbs == data.verbs && verbSizes == data.verbSizes && points == data.points; | |
| } | |
| }; | |
| class PathProperties : public Properties { | |
| public: | |
| explicit PathProperties(Node* node) : Properties(node) {} | |
| void syncProperties(const PathProperties& prop) { | |
| mData = prop.mData; | |
| onPropertyChanged(); | |
| } | |
| void setData(const Data& data) { | |
| // Updates the path data. Note that we don't generate a new Skia path right away | |
| // because there are cases where the animation is changing the path data, but the view | |
| // that hosts the VD has gone off screen, in which case we won't even draw. So we | |
| // postpone the Skia path generation to the draw time. | |
| if (data == mData) { | |
| return; | |
| } | |
| mData = data; | |
| onPropertyChanged(); | |
| } | |
| const Data& getData() const { return mData; } | |
| private: | |
| Data mData; | |
| }; | |
| Path(const Path& path); | |
| Path(const char* path, size_t strLength); | |
| Path() {} | |
| void dump() override; | |
| virtual void syncProperties() override; | |
| virtual void onPropertyChanged(Properties* prop) override { | |
| if (prop == &mStagingProperties) { | |
| mStagingPropertiesDirty = true; | |
| if (mPropertyChangedListener) { | |
| mPropertyChangedListener->onStagingPropertyChanged(); | |
| } | |
| } else if (prop == &mProperties) { | |
| mSkPathDirty = true; | |
| if (mPropertyChangedListener) { | |
| mPropertyChangedListener->onPropertyChanged(); | |
| } | |
| } | |
| } | |
| PathProperties* mutateStagingProperties() { return &mStagingProperties; } | |
| const PathProperties* stagingProperties() { return &mStagingProperties; } | |
| // This should only be called from animations on RT | |
| PathProperties* mutateProperties() { return &mProperties; } | |
| protected: | |
| virtual const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath); | |
| // Internal data, render thread only. | |
| bool mSkPathDirty = true; | |
| SkPath mSkPath; | |
| private: | |
| PathProperties mProperties = PathProperties(this); | |
| PathProperties mStagingProperties = PathProperties(this); | |
| bool mStagingPropertiesDirty = true; | |
| }; | |
| class FullPath : public Path { | |
| public: | |
| class FullPathProperties : public Properties { | |
| public: | |
| struct PrimitiveFields { | |
| float strokeWidth = 0; | |
| SkColor strokeColor = SK_ColorTRANSPARENT; | |
| float strokeAlpha = 1; | |
| SkColor fillColor = SK_ColorTRANSPARENT; | |
| float fillAlpha = 1; | |
| float trimPathStart = 0; | |
| float trimPathEnd = 1; | |
| float trimPathOffset = 0; | |
| int32_t strokeLineCap = SkPaint::Cap::kButt_Cap; | |
| int32_t strokeLineJoin = SkPaint::Join::kMiter_Join; | |
| float strokeMiterLimit = 4; | |
| int fillType = 0; /* non-zero or kWinding_FillType in Skia */ | |
| }; | |
| explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {} | |
| ~FullPathProperties() {} | |
| void syncProperties(const FullPathProperties& prop) { | |
| mPrimitiveFields = prop.mPrimitiveFields; | |
| mTrimDirty = true; | |
| fillGradient = prop.fillGradient; | |
| strokeGradient = prop.strokeGradient; | |
| onPropertyChanged(); | |
| } | |
| void setFillGradient(SkShader* gradient) { | |
| if (fillGradient.get() != gradient) { | |
| fillGradient = sk_ref_sp(gradient); | |
| onPropertyChanged(); | |
| } | |
| } | |
| void setStrokeGradient(SkShader* gradient) { | |
| if (strokeGradient.get() != gradient) { | |
| strokeGradient = sk_ref_sp(gradient); | |
| onPropertyChanged(); | |
| } | |
| } | |
| SkShader* getFillGradient() const { return fillGradient.get(); } | |
| SkShader* getStrokeGradient() const { return strokeGradient.get(); } | |
| float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; } | |
| void setStrokeWidth(float strokeWidth) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth); | |
| } | |
| SkColor getStrokeColor() const { return mPrimitiveFields.strokeColor; } | |
| void setStrokeColor(SkColor strokeColor) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor); | |
| } | |
| float getStrokeAlpha() const { return mPrimitiveFields.strokeAlpha; } | |
| void setStrokeAlpha(float strokeAlpha) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha); | |
| } | |
| SkColor getFillColor() const { return mPrimitiveFields.fillColor; } | |
| void setFillColor(SkColor fillColor) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor); | |
| } | |
| float getFillAlpha() const { return mPrimitiveFields.fillAlpha; } | |
| void setFillAlpha(float fillAlpha) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha); | |
| } | |
| float getTrimPathStart() const { return mPrimitiveFields.trimPathStart; } | |
| void setTrimPathStart(float trimPathStart) { | |
| VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty); | |
| } | |
| float getTrimPathEnd() const { return mPrimitiveFields.trimPathEnd; } | |
| void setTrimPathEnd(float trimPathEnd) { | |
| VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty); | |
| } | |
| float getTrimPathOffset() const { return mPrimitiveFields.trimPathOffset; } | |
| void setTrimPathOffset(float trimPathOffset) { | |
| VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty); | |
| } | |
| float getStrokeMiterLimit() const { return mPrimitiveFields.strokeMiterLimit; } | |
| float getStrokeLineCap() const { return mPrimitiveFields.strokeLineCap; } | |
| float getStrokeLineJoin() const { return mPrimitiveFields.strokeLineJoin; } | |
| float getFillType() const { return mPrimitiveFields.fillType; } | |
| bool copyProperties(int8_t* outProperties, int length) const; | |
| void 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) { | |
| mPrimitiveFields.strokeWidth = strokeWidth; | |
| mPrimitiveFields.strokeColor = strokeColor; | |
| mPrimitiveFields.strokeAlpha = strokeAlpha; | |
| mPrimitiveFields.fillColor = fillColor; | |
| mPrimitiveFields.fillAlpha = fillAlpha; | |
| mPrimitiveFields.trimPathStart = trimPathStart; | |
| mPrimitiveFields.trimPathEnd = trimPathEnd; | |
| mPrimitiveFields.trimPathOffset = trimPathOffset; | |
| mPrimitiveFields.strokeMiterLimit = strokeMiterLimit; | |
| mPrimitiveFields.strokeLineCap = strokeLineCap; | |
| mPrimitiveFields.strokeLineJoin = strokeLineJoin; | |
| mPrimitiveFields.fillType = fillType; | |
| mTrimDirty = true; | |
| onPropertyChanged(); | |
| } | |
| // Set property values during animation | |
| void setColorPropertyValue(int propertyId, int32_t value); | |
| void setPropertyValue(int propertyId, float value); | |
| bool mTrimDirty; | |
| private: | |
| enum class Property { | |
| strokeWidth = 0, | |
| strokeColor, | |
| strokeAlpha, | |
| fillColor, | |
| fillAlpha, | |
| trimPathStart, | |
| trimPathEnd, | |
| trimPathOffset, | |
| strokeLineCap, | |
| strokeLineJoin, | |
| strokeMiterLimit, | |
| fillType, | |
| count, | |
| }; | |
| PrimitiveFields mPrimitiveFields; | |
| sk_sp<SkShader> fillGradient; | |
| sk_sp<SkShader> strokeGradient; | |
| }; | |
| // Called from UI thread | |
| FullPath(const FullPath& path); // for cloning | |
| FullPath(const char* path, size_t strLength) : Path(path, strLength) {} | |
| FullPath() : Path() {} | |
| void draw(SkCanvas* outCanvas, bool useStagingData) override; | |
| void dump() override; | |
| FullPathProperties* mutateStagingProperties() { return &mStagingProperties; } | |
| const FullPathProperties* stagingProperties() { return &mStagingProperties; } | |
| // This should only be called from animations on RT | |
| FullPathProperties* mutateProperties() { return &mProperties; } | |
| virtual void syncProperties() override; | |
| virtual void onPropertyChanged(Properties* properties) override { | |
| Path::onPropertyChanged(properties); | |
| if (properties == &mStagingProperties) { | |
| mStagingPropertiesDirty = true; | |
| if (mPropertyChangedListener) { | |
| mPropertyChangedListener->onStagingPropertyChanged(); | |
| } | |
| } else if (properties == &mProperties) { | |
| if (mPropertyChangedListener) { | |
| mPropertyChangedListener->onPropertyChanged(); | |
| } | |
| } | |
| } | |
| virtual void setAntiAlias(bool aa) { mAntiAlias = aa; } | |
| void forEachFillColor(const std::function<void(SkColor)>& func) const override { | |
| func(mStagingProperties.getFillColor()); | |
| } | |
| protected: | |
| const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override; | |
| private: | |
| FullPathProperties mProperties = FullPathProperties(this); | |
| FullPathProperties mStagingProperties = FullPathProperties(this); | |
| bool mStagingPropertiesDirty = true; | |
| // Intermediate data for drawing, render thread only | |
| SkPath mTrimmedSkPath; | |
| // Default to use AntiAlias | |
| bool mAntiAlias = true; | |
| }; | |
| class ClipPath : public Path { | |
| public: | |
| ClipPath(const ClipPath& path) : Path(path) {} | |
| ClipPath(const char* path, size_t strLength) : Path(path, strLength) {} | |
| ClipPath() : Path() {} | |
| void draw(SkCanvas* outCanvas, bool useStagingData) override; | |
| virtual void setAntiAlias(bool aa) {} | |
| }; | |
| class Group : public Node { | |
| public: | |
| class GroupProperties : public Properties { | |
| public: | |
| explicit GroupProperties(Node* mNode) : Properties(mNode) {} | |
| struct PrimitiveFields { | |
| float rotate = 0; | |
| float pivotX = 0; | |
| float pivotY = 0; | |
| float scaleX = 1; | |
| float scaleY = 1; | |
| float translateX = 0; | |
| float translateY = 0; | |
| } mPrimitiveFields; | |
| void syncProperties(const GroupProperties& prop) { | |
| mPrimitiveFields = prop.mPrimitiveFields; | |
| onPropertyChanged(); | |
| } | |
| float getRotation() const { return mPrimitiveFields.rotate; } | |
| void setRotation(float rotation) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation); } | |
| float getPivotX() const { return mPrimitiveFields.pivotX; } | |
| void setPivotX(float pivotX) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX); } | |
| float getPivotY() const { return mPrimitiveFields.pivotY; } | |
| void setPivotY(float pivotY) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY); } | |
| float getScaleX() const { return mPrimitiveFields.scaleX; } | |
| void setScaleX(float scaleX) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX); } | |
| float getScaleY() const { return mPrimitiveFields.scaleY; } | |
| void setScaleY(float scaleY) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY); } | |
| float getTranslateX() const { return mPrimitiveFields.translateX; } | |
| void setTranslateX(float translateX) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX); | |
| } | |
| float getTranslateY() const { return mPrimitiveFields.translateY; } | |
| void setTranslateY(float translateY) { | |
| VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY); | |
| } | |
| void updateProperties(float rotate, float pivotX, float pivotY, float scaleX, float scaleY, | |
| float translateX, float translateY) { | |
| mPrimitiveFields.rotate = rotate; | |
| mPrimitiveFields.pivotX = pivotX; | |
| mPrimitiveFields.pivotY = pivotY; | |
| mPrimitiveFields.scaleX = scaleX; | |
| mPrimitiveFields.scaleY = scaleY; | |
| mPrimitiveFields.translateX = translateX; | |
| mPrimitiveFields.translateY = translateY; | |
| onPropertyChanged(); | |
| } | |
| void setPropertyValue(int propertyId, float value); | |
| float getPropertyValue(int propertyId) const; | |
| bool copyProperties(float* outProperties, int length) const; | |
| static bool isValidProperty(int propertyId); | |
| private: | |
| enum class Property { | |
| rotate = 0, | |
| pivotX, | |
| pivotY, | |
| scaleX, | |
| scaleY, | |
| translateX, | |
| translateY, | |
| // Count of the properties, must be at the end. | |
| count, | |
| }; | |
| }; | |
| Group(const Group& group); | |
| Group() {} | |
| void addChild(Node* child); | |
| virtual void setPropertyChangedListener(PropertyChangedListener* listener) override { | |
| Node::setPropertyChangedListener(listener); | |
| for (auto& child : mChildren) { | |
| child->setPropertyChangedListener(listener); | |
| } | |
| } | |
| virtual void syncProperties() override; | |
| GroupProperties* mutateStagingProperties() { return &mStagingProperties; } | |
| const GroupProperties* stagingProperties() { return &mStagingProperties; } | |
| // This should only be called from animations on RT | |
| GroupProperties* mutateProperties() { return &mProperties; } | |
| // Methods below could be called from either UI thread or Render Thread. | |
| virtual void draw(SkCanvas* outCanvas, bool useStagingData) override; | |
| void getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties); | |
| void dump() override; | |
| static bool isValidProperty(int propertyId); | |
| virtual void onPropertyChanged(Properties* properties) override { | |
| if (properties == &mStagingProperties) { | |
| mStagingPropertiesDirty = true; | |
| if (mPropertyChangedListener) { | |
| mPropertyChangedListener->onStagingPropertyChanged(); | |
| } | |
| } else { | |
| if (mPropertyChangedListener) { | |
| mPropertyChangedListener->onPropertyChanged(); | |
| } | |
| } | |
| } | |
| virtual void setAntiAlias(bool aa) { | |
| for (auto& child : mChildren) { | |
| child->setAntiAlias(aa); | |
| } | |
| } | |
| void forEachFillColor(const std::function<void(SkColor)>& func) const override { | |
| for (auto& child : mChildren) { | |
| child->forEachFillColor(func); | |
| } | |
| } | |
| private: | |
| GroupProperties mProperties = GroupProperties(this); | |
| GroupProperties mStagingProperties = GroupProperties(this); | |
| bool mStagingPropertiesDirty = true; | |
| std::vector<std::unique_ptr<Node> > mChildren; | |
| }; | |
| class Tree : public VirtualLightRefBase { | |
| public: | |
| explicit Tree(Group* rootNode) : mRootNode(rootNode) { | |
| mRootNode->setPropertyChangedListener(&mPropertyChangedListener); | |
| } | |
| // Copy properties from the tree and use the give node as the root node | |
| Tree(const Tree* copy, Group* rootNode) : Tree(rootNode) { | |
| mStagingProperties.syncAnimatableProperties(copy->stagingProperties()); | |
| mStagingProperties.syncNonAnimatableProperties(copy->stagingProperties()); | |
| } | |
| // Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input | |
| // canvas. Returns the number of pixels needed for the bitmap cache. | |
| int draw(Canvas* outCanvas, SkColorFilter* colorFilter, const SkRect& bounds, | |
| bool needsMirroring, bool canReuseCache); | |
| void drawStaging(Canvas* canvas); | |
| Bitmap& getBitmapUpdateIfDirty(); | |
| void setAllowCaching(bool allowCaching) { mAllowCaching = allowCaching; } | |
| void syncProperties() { | |
| if (mStagingProperties.mNonAnimatablePropertiesDirty) { | |
| mCache.dirty |= (mProperties.mNonAnimatableProperties.viewportWidth != | |
| mStagingProperties.mNonAnimatableProperties.viewportWidth) || | |
| (mProperties.mNonAnimatableProperties.viewportHeight != | |
| mStagingProperties.mNonAnimatableProperties.viewportHeight) || | |
| (mProperties.mNonAnimatableProperties.scaledWidth != | |
| mStagingProperties.mNonAnimatableProperties.scaledWidth) || | |
| (mProperties.mNonAnimatableProperties.scaledHeight != | |
| mStagingProperties.mNonAnimatableProperties.scaledHeight) || | |
| (mProperties.mNonAnimatableProperties.bounds != | |
| mStagingProperties.mNonAnimatableProperties.bounds); | |
| mProperties.syncNonAnimatableProperties(mStagingProperties); | |
| mStagingProperties.mNonAnimatablePropertiesDirty = false; | |
| } | |
| if (mStagingProperties.mAnimatablePropertiesDirty) { | |
| mProperties.syncAnimatableProperties(mStagingProperties); | |
| } else { | |
| mStagingProperties.syncAnimatableProperties(mProperties); | |
| } | |
| mStagingProperties.mAnimatablePropertiesDirty = false; | |
| mRootNode->syncProperties(); | |
| } | |
| class TreeProperties { | |
| public: | |
| explicit TreeProperties(Tree* tree) : mTree(tree) {} | |
| // Properties that can only be modified by UI thread, therefore sync should | |
| // only go from UI to RT | |
| struct NonAnimatableProperties { | |
| float viewportWidth = 0; | |
| float viewportHeight = 0; | |
| SkRect bounds; | |
| int scaledWidth = 0; | |
| int scaledHeight = 0; | |
| sk_sp<SkColorFilter> colorFilter; | |
| } mNonAnimatableProperties; | |
| bool mNonAnimatablePropertiesDirty = true; | |
| float mRootAlpha = 1.0f; | |
| bool mAnimatablePropertiesDirty = true; | |
| void syncNonAnimatableProperties(const TreeProperties& prop) { | |
| // Copy over the data that can only be changed in UI thread | |
| if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) { | |
| mNonAnimatableProperties.colorFilter = prop.mNonAnimatableProperties.colorFilter; | |
| } | |
| mNonAnimatableProperties = prop.mNonAnimatableProperties; | |
| } | |
| void setViewportSize(float width, float height) { | |
| if (mNonAnimatableProperties.viewportWidth != width || | |
| mNonAnimatableProperties.viewportHeight != height) { | |
| mNonAnimatablePropertiesDirty = true; | |
| mNonAnimatableProperties.viewportWidth = width; | |
| mNonAnimatableProperties.viewportHeight = height; | |
| mTree->onPropertyChanged(this); | |
| } | |
| } | |
| void setBounds(const SkRect& bounds) { | |
| if (mNonAnimatableProperties.bounds != bounds) { | |
| mNonAnimatableProperties.bounds = bounds; | |
| mNonAnimatablePropertiesDirty = true; | |
| mTree->onPropertyChanged(this); | |
| } | |
| } | |
| void setScaledSize(int width, int height) { | |
| // If the requested size is bigger than what the bitmap was, then | |
| // we increase the bitmap size to match. The width and height | |
| // are bound by MAX_CACHED_BITMAP_SIZE. | |
| if (mNonAnimatableProperties.scaledWidth < width || | |
| mNonAnimatableProperties.scaledHeight < height) { | |
| mNonAnimatableProperties.scaledWidth = | |
| std::max(width, mNonAnimatableProperties.scaledWidth); | |
| mNonAnimatableProperties.scaledHeight = | |
| std::max(height, mNonAnimatableProperties.scaledHeight); | |
| mNonAnimatablePropertiesDirty = true; | |
| mTree->onPropertyChanged(this); | |
| } | |
| } | |
| void setColorFilter(SkColorFilter* filter) { | |
| if (mNonAnimatableProperties.colorFilter.get() != filter) { | |
| mNonAnimatableProperties.colorFilter = sk_ref_sp(filter); | |
| mNonAnimatablePropertiesDirty = true; | |
| mTree->onPropertyChanged(this); | |
| } | |
| } | |
| SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter.get(); } | |
| float getViewportWidth() const { return mNonAnimatableProperties.viewportWidth; } | |
| float getViewportHeight() const { return mNonAnimatableProperties.viewportHeight; } | |
| float getScaledWidth() const { return mNonAnimatableProperties.scaledWidth; } | |
| float getScaledHeight() const { return mNonAnimatableProperties.scaledHeight; } | |
| void syncAnimatableProperties(const TreeProperties& prop) { mRootAlpha = prop.mRootAlpha; } | |
| bool setRootAlpha(float rootAlpha) { | |
| if (rootAlpha != mRootAlpha) { | |
| mAnimatablePropertiesDirty = true; | |
| mRootAlpha = rootAlpha; | |
| mTree->onPropertyChanged(this); | |
| return true; | |
| } | |
| return false; | |
| } | |
| float getRootAlpha() const { return mRootAlpha; } | |
| const SkRect& getBounds() const { return mNonAnimatableProperties.bounds; } | |
| Tree* mTree; | |
| }; | |
| void onPropertyChanged(TreeProperties* prop); | |
| TreeProperties* mutateStagingProperties() { return &mStagingProperties; } | |
| const TreeProperties& stagingProperties() const { return mStagingProperties; } | |
| // This should only be called from animations on RT | |
| TreeProperties* mutateProperties() { return &mProperties; } | |
| // called from RT only | |
| const TreeProperties& properties() const { return mProperties; } | |
| // This should always be called from RT. | |
| void markDirty() { mCache.dirty = true; } | |
| bool isDirty() const { return mCache.dirty; } | |
| bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; } | |
| void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; } | |
| /** | |
| * Draws VD cache into a canvas. This should always be called from RT and it works with Skia | |
| * pipelines only. | |
| */ | |
| void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint); | |
| void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const; | |
| BitmapPalette computePalette(); | |
| void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); } | |
| private: | |
| class Cache { | |
| public: | |
| sk_sp<Bitmap> bitmap; // used by HWUI pipeline and software | |
| bool dirty = true; | |
| }; | |
| bool allocateBitmapIfNeeded(Cache& cache, int width, int height); | |
| bool canReuseBitmap(Bitmap*, int width, int height); | |
| void updateBitmapCache(Bitmap& outCache, bool useStagingData); | |
| // Cap the bitmap size, such that it won't hurt the performance too much | |
| // and it won't crash due to a very large scale. | |
| // The drawable will look blurry above this size. | |
| const static int MAX_CACHED_BITMAP_SIZE; | |
| bool mAllowCaching = true; | |
| std::unique_ptr<Group> mRootNode; | |
| TreeProperties mProperties = TreeProperties(this); | |
| TreeProperties mStagingProperties = TreeProperties(this); | |
| Cache mStagingCache; | |
| Cache mCache; | |
| PropertyChangedListener mPropertyChangedListener = | |
| PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty); | |
| mutable bool mWillBeConsumed = false; | |
| }; | |
| } // namespace VectorDrawable | |
| typedef VectorDrawable::Path::Data PathData; | |
| typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; | |
| } // namespace uirenderer | |
| } // namespace android | |
| #endif // ANDROID_HWUI_VPATH_H |