diff options
| author | 2016-02-05 21:31:05 +0000 | |
|---|---|---|
| committer | 2016-02-05 21:31:06 +0000 | |
| commit | 43a5591c8da267e85db9df6c93db73ca29a1bcba (patch) | |
| tree | e0bda443b122692ab29ef8a9b76bc30108b391b2 /libs | |
| parent | bb66a0edfe99a545a016376e5bdd1e76a9a141f1 (diff) | |
| parent | 766431aa57c16ece8842287a92b2e7208e3b8ac3 (diff) | |
Merge "Revert "Revert "VectorDrawable native rendering - Step 4 of MANY"""
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/Android.mk | 2 | ||||
| -rw-r--r-- | libs/hwui/Animator.cpp | 11 | ||||
| -rw-r--r-- | libs/hwui/Animator.h | 26 | ||||
| -rw-r--r-- | libs/hwui/Canvas.h | 12 | ||||
| -rw-r--r-- | libs/hwui/DisplayListCanvas.cpp | 13 | ||||
| -rw-r--r-- | libs/hwui/DisplayListCanvas.h | 3 | ||||
| -rw-r--r-- | libs/hwui/FrameBuilder.cpp | 13 | ||||
| -rw-r--r-- | libs/hwui/PropertyValuesAnimatorSet.cpp | 129 | ||||
| -rw-r--r-- | libs/hwui/PropertyValuesAnimatorSet.h | 85 | ||||
| -rw-r--r-- | libs/hwui/PropertyValuesHolder.cpp | 99 | ||||
| -rw-r--r-- | libs/hwui/PropertyValuesHolder.h | 121 | ||||
| -rw-r--r-- | libs/hwui/RecordedOp.h | 13 | ||||
| -rw-r--r-- | libs/hwui/RecordingCanvas.cpp | 11 | ||||
| -rw-r--r-- | libs/hwui/RecordingCanvas.h | 2 | ||||
| -rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 11 | ||||
| -rw-r--r-- | libs/hwui/VectorDrawable.cpp | 279 | ||||
| -rw-r--r-- | libs/hwui/VectorDrawable.h | 153 |
17 files changed, 780 insertions, 203 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 1fb9ac515f03..c232bd1101a3 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -78,6 +78,8 @@ hwui_src_files := \ Program.cpp \ ProgramCache.cpp \ Properties.cpp \ + PropertyValuesHolder.cpp \ + PropertyValuesAnimatorSet.cpp \ RenderBufferCache.cpp \ RenderNode.cpp \ RenderProperties.cpp \ diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 5ca2a2fa37ab..7bd2b24bf56b 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -90,6 +90,9 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { doSetStartValue(getValue(mTarget)); } if (mStagingPlayState > mPlayState) { + if (mStagingPlayState == PlayState::Restarted) { + mStagingPlayState = PlayState::Running; + } mPlayState = mStagingPlayState; // Oh boy, we're starting! Man the battle stations! if (mPlayState == PlayState::Running) { @@ -131,6 +134,11 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) { return true; } + // This should be set before setValue() so animators can query this time when setValue + // is called. + nsecs_t currentFrameTime = context.frameTimeMs(); + onPlayTimeChanged(currentFrameTime - mStartTime); + // If BaseRenderNodeAnimator is handling the delay (not typical), then // because the staging properties reflect the final value, we always need // to call setValue even if the animation isn't yet running or is still @@ -141,8 +149,9 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) { } float fraction = 1.0f; + if (mPlayState == PlayState::Running && mDuration > 0) { - fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration; + fraction = (float)(currentFrameTime - mStartTime) / mDuration; } if (fraction >= 1.0f) { fraction = 1.0f; diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index aea95bfc1c0e..2c9c9c3fe0f9 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -59,7 +59,13 @@ public: mMayRunAsync = mayRunAsync; } bool mayRunAsync() { return mMayRunAsync; } - ANDROID_API void start() { mStagingPlayState = PlayState::Running; onStagingPlayStateChanged(); } + ANDROID_API void start() { + if (mStagingPlayState == PlayState::NotStarted) { + mStagingPlayState = PlayState::Running; + } else { + mStagingPlayState = PlayState::Restarted; + } + onStagingPlayStateChanged(); } ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); } void attach(RenderNode* target); @@ -77,10 +83,27 @@ public: void forceEndNow(AnimationContext& context); protected: + // PlayState is used by mStagingPlayState and mPlayState to track the state initiated from UI + // thread and Render Thread animation state, respectively. + // From the UI thread, mStagingPlayState transition looks like + // NotStarted -> Running -> Finished + // ^ | + // | | + // Restarted <------ + // Note: For mStagingState, the Finished state (optional) is only set when the animation is + // terminated by user. + // + // On Render Thread, mPlayState transition: + // NotStart -> Running -> Finished + // ^ | + // | | + // ------------- + enum class PlayState { NotStarted, Running, Finished, + Restarted, }; BaseRenderNodeAnimator(float finalValue); @@ -93,6 +116,7 @@ protected: void callOnFinishedListener(AnimationContext& context); virtual void onStagingPlayStateChanged() {} + virtual void onPlayTimeChanged(nsecs_t playTime) {} RenderNode* mTarget; diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h index dbd502d19fd2..27facdf652cd 100644 --- a/libs/hwui/Canvas.h +++ b/libs/hwui/Canvas.h @@ -52,6 +52,13 @@ typedef uint32_t Flags; } // namespace SaveFlags +namespace uirenderer { +namespace VectorDrawable { +class Tree; +}; +}; +typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; + class ANDROID_API Canvas { public: virtual ~Canvas() {}; @@ -217,6 +224,11 @@ public: */ virtual bool drawTextAbsolutePos() const = 0; + /** + * Draws a VectorDrawable onto the canvas. + */ + virtual void drawVectorDrawable(VectorDrawableRoot* tree); + protected: void drawTextDecorations(float x, float y, float length, const SkPaint& paint); }; diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index 7eaa78581391..3db14b55cff6 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -16,11 +16,12 @@ #include "DisplayListCanvas.h" -#include "ResourceCache.h" #include "DeferredDisplayList.h" #include "DeferredLayerUpdater.h" #include "DisplayListOp.h" +#include "ResourceCache.h" #include "RenderNode.h" +#include "VectorDrawable.h" #include "utils/PaintUtils.h" #include <SkCamera.h> @@ -412,6 +413,16 @@ void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint))); } +void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { + mDisplayList->ref(tree); + const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty(); + SkPaint* paint = tree->getPaint(); + const SkRect bounds = tree->getBounds(); + addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap), + 0, 0, bitmap.width(), bitmap.height(), + bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint))); +} + void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) { if (!glyphs || count <= 0) return; diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index ad939602ec7d..06e72a06eaca 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -206,6 +206,8 @@ public: float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawVectorDrawable(VectorDrawableRoot* tree) override; + // Text virtual void drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, @@ -214,7 +216,6 @@ public: float hOffset, float vOffset, const SkPaint& paint) override; virtual bool drawTextAbsolutePos() const override { return false; } - private: CanvasState mState; diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index c8910cbdd337..185accec1ef6 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -19,6 +19,7 @@ #include "Canvas.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" +#include "VectorDrawable.h" #include "renderstate/OffscreenBufferPool.h" #include "utils/FatVector.h" #include "utils/PaintUtils.h" @@ -545,6 +546,18 @@ void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) { currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); } +void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) { + const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty(); + SkPaint* paint = op.vectorDrawable->getPaint(); + const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds, + op.localMatrix, + op.localClip, + paint, + &bitmap, + Rect(bitmap.width(), bitmap.height())); + deferBitmapRectOp(*resolvedOp); +} + void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) { // allocate a temporary oval op (with mAllocator, so it persists until render), so the // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple. diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp new file mode 100644 index 000000000000..eca1afcc54dc --- /dev/null +++ b/libs/hwui/PropertyValuesAnimatorSet.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "PropertyValuesAnimatorSet.h" +#include "RenderNode.h" + +namespace android { +namespace uirenderer { + +void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder, + Interpolator* interpolator, nsecs_t startDelay, + nsecs_t duration, int repeatCount) { + + PropertyAnimator* animator = new PropertyAnimator(propertyValuesHolder, + interpolator, startDelay, duration, repeatCount); + mAnimators.emplace_back(animator); + setListener(new PropertyAnimatorSetListener(this)); +} + +PropertyValuesAnimatorSet::PropertyValuesAnimatorSet() + : BaseRenderNodeAnimator(1.0f) { + setStartValue(0); + mLastFraction = 0.0f; + setInterpolator(new LinearInterpolator()); +} + +void PropertyValuesAnimatorSet::onFinished(BaseRenderNodeAnimator* animator) { + if (mOneShotListener.get()) { + mOneShotListener->onAnimationFinished(animator); + mOneShotListener = nullptr; + } +} + +float PropertyValuesAnimatorSet::getValue(RenderNode* target) const { + return mLastFraction; +} + +void PropertyValuesAnimatorSet::setValue(RenderNode* target, float value) { + mLastFraction = value; +} + +void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) { + for (size_t i = 0; i < mAnimators.size(); i++) { + mAnimators[i]->setCurrentPlayTime(playTime); + } +} + +void PropertyValuesAnimatorSet::reset() { + // TODO: implement reset through adding a play state because we need to support reset() even + // during an animation run. +} + +void PropertyValuesAnimatorSet::start(AnimationListener* listener) { + init(); + mOneShotListener = listener; + BaseRenderNodeAnimator::start(); +} + +void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) { +// TODO: implement reverse +} + +void PropertyValuesAnimatorSet::init() { + if (mInitialized) { + return; + } + nsecs_t maxDuration = 0; + for (size_t i = 0; i < mAnimators.size(); i++) { + if (maxDuration < mAnimators[i]->getTotalDuration()) { + maxDuration = mAnimators[i]->getTotalDuration(); + } + } + mDuration = maxDuration; + mInitialized = true; +} + +uint32_t PropertyValuesAnimatorSet::dirtyMask() { + return RenderNode::DISPLAY_LIST; +} + +PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, + nsecs_t startDelay, nsecs_t duration, int repeatCount) + : mPropertyValuesHolder(holder), mInterpolator(interpolator), mStartDelay(startDelay), + mDuration(duration) { + if (repeatCount < 0) { + mRepeatCount = UINT32_MAX; + } else { + mRepeatCount = repeatCount; + } + mTotalDuration = ((nsecs_t) mRepeatCount + 1) * mDuration + mStartDelay; +} + +void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) { + if (playTime >= mStartDelay && playTime < mTotalDuration) { + nsecs_t currentIterationPlayTime = (playTime - mStartDelay) % mDuration; + mLatestFraction = currentIterationPlayTime / (float) mDuration; + } else if (mLatestFraction < 1.0f && playTime >= mTotalDuration) { + mLatestFraction = 1.0f; + } else { + return; + } + + setFraction(mLatestFraction); +} + +void PropertyAnimator::setFraction(float fraction) { + float interpolatedFraction = mInterpolator->interpolate(mLatestFraction); + mPropertyValuesHolder->setFraction(interpolatedFraction); +} + +void PropertyAnimatorSetListener::onAnimationFinished(BaseRenderNodeAnimator* animator) { + mSet->onFinished(animator); +} + +} +} diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h new file mode 100644 index 000000000000..4c7ce528bb20 --- /dev/null +++ b/libs/hwui/PropertyValuesAnimatorSet.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#include "Animator.h" +#include "PropertyValuesHolder.h" +#include "Interpolator.h" + +namespace android { +namespace uirenderer { + +class PropertyAnimator { +public: + PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, nsecs_t startDelay, + nsecs_t duration, int repeatCount); + void setCurrentPlayTime(nsecs_t playTime); + nsecs_t getTotalDuration() { + return mTotalDuration; + } + void setFraction(float fraction); + +private: + std::unique_ptr<PropertyValuesHolder> mPropertyValuesHolder; + std::unique_ptr<Interpolator> mInterpolator; + nsecs_t mStartDelay; + nsecs_t mDuration; + uint32_t mRepeatCount; + nsecs_t mTotalDuration; + float mLatestFraction = 0.0f; +}; + +class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator { +public: + friend class PropertyAnimatorSetListener; + PropertyValuesAnimatorSet(); + + void start(AnimationListener* listener); + void reverse(AnimationListener* listener); + void reset(); + + void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder, + Interpolator* interpolators, int64_t startDelays, + nsecs_t durations, int repeatCount); + virtual uint32_t dirtyMask(); + +protected: + virtual float getValue(RenderNode* target) const override; + virtual void setValue(RenderNode* target, float value) override; + virtual void onPlayTimeChanged(nsecs_t playTime) override; + +private: + void init(); + void onFinished(BaseRenderNodeAnimator* animator); + // Listener set from outside + sp<AnimationListener> mOneShotListener; + std::vector< std::unique_ptr<PropertyAnimator> > mAnimators; + float mLastFraction = 0.0f; + bool mInitialized = false; +}; + +class PropertyAnimatorSetListener : public AnimationListener { +public: + PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {} + virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) override; + +private: + PropertyValuesAnimatorSet* mSet; +}; + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp new file mode 100644 index 000000000000..8f837f6048d6 --- /dev/null +++ b/libs/hwui/PropertyValuesHolder.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "PropertyValuesHolder.h" + +#include "utils/VectorDrawableUtils.h" + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { + +using namespace VectorDrawable; + +float PropertyValuesHolder::getValueFromData(float fraction) { + if (mDataSource.size() == 0) { + LOG_ALWAYS_FATAL("No data source is defined"); + return 0; + } + if (fraction <= 0.0f) { + return mDataSource.front(); + } + if (fraction >= 1.0f) { + return mDataSource.back(); + } + + fraction *= mDataSource.size() - 1; + int lowIndex = floor(fraction); + fraction -= lowIndex; + + float value = mDataSource[lowIndex] * (1.0f - fraction) + + mDataSource[lowIndex + 1] * fraction; + return value; +} + +void GroupPropertyValuesHolder::setFraction(float fraction) { + float animatedValue; + if (mDataSource.size() > 0) { + animatedValue = getValueFromData(fraction); + } else { + animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction; + } + mGroup->setPropertyValue(mPropertyId, animatedValue); +} + +inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) { + return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction); +} + +// TODO: Add a test for this +SkColor FullPathColorPropertyValuesHolder::interpolateColors(SkColor fromColor, SkColor toColor, + float fraction) { + U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction); + U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction); + U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction); + U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction); + return SkColorSetARGB(alpha, red, green, blue); +} + +void FullPathColorPropertyValuesHolder::setFraction(float fraction) { + SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction); + mFullPath->setColorPropertyValue(mPropertyId, animatedValue); +} + +void FullPathPropertyValuesHolder::setFraction(float fraction) { + float animatedValue; + if (mDataSource.size() > 0) { + animatedValue = getValueFromData(fraction); + } else { + animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction; + } + mFullPath->setPropertyValue(mPropertyId, animatedValue); +} + +void PathDataPropertyValuesHolder::setFraction(float fraction) { + VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction); + mPath->setPathData(mPathData); +} + +void RootAlphaPropertyValuesHolder::setFraction(float fraction) { + float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction; + mTree->setRootAlpha(animatedValue); +} + +} // namepace uirenderer +} // namespace android diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h new file mode 100644 index 000000000000..b905faef104c --- /dev/null +++ b/libs/hwui/PropertyValuesHolder.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#include "VectorDrawable.h" + +#include <SkColor.h> + +namespace android { +namespace uirenderer { + +/** + * PropertyValues holder contains data needed to change a property of a Vector Drawable object. + * When a fraction in [0f, 1f] is provided, the holder will calculate an interpolated value based + * on its start and end value, and set the new value on the VectorDrawble's corresponding property. + */ +class ANDROID_API PropertyValuesHolder { +public: + virtual void setFraction(float fraction) = 0; + void setPropertyDataSource(float* dataSource, int length) { + mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length); + } + float getValueFromData(float fraction); + virtual ~PropertyValuesHolder() {} +protected: + std::vector<float> mDataSource; +}; + +class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolder { +public: + GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue, + float endValue) + : mGroup(ptr) + , mPropertyId(propertyId) + , mStartValue(startValue) + , mEndValue(endValue){ + } + void setFraction(float fraction) override; +private: + VectorDrawable::Group* mGroup; + int mPropertyId; + float mStartValue; + float mEndValue; +}; + +class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolder { +public: + FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, int32_t startValue, + int32_t endValue) + : mFullPath(ptr) + , mPropertyId(propertyId) + , mStartValue(startValue) + , mEndValue(endValue) {}; + void setFraction(float fraction) override; + static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction); +private: + VectorDrawable::FullPath* mFullPath; + int mPropertyId; + int32_t mStartValue; + int32_t mEndValue; +}; + +class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolder { +public: + FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue, + float endValue) + : mFullPath(ptr) + , mPropertyId(propertyId) + , mStartValue(startValue) + , mEndValue(endValue) {}; + void setFraction(float fraction) override; +private: + VectorDrawable::FullPath* mFullPath; + int mPropertyId; + float mStartValue; + float mEndValue; +}; + +class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolder { +public: + PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue, + PathData* endValue) + : mPath(ptr) + , mStartValue(*startValue) + , mEndValue(*endValue) {}; + void setFraction(float fraction) override; +private: + VectorDrawable::Path* mPath; + PathData mPathData; + PathData mStartValue; + PathData mEndValue; +}; + +class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolder { +public: + RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue) + : mTree(tree) + , mStartValue(startValue) + , mEndValue(endValue) {} + void setFraction(float fraction) override; +private: + VectorDrawable::Tree* mTree; + float mStartValue; + float mEndValue; +}; +} +} diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index 593d690f2b43..bb26e2ec67a8 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HWUI_RECORDED_OP_H #define ANDROID_HWUI_RECORDED_OP_H +#include "RecordedOp.h" #include "font/FontUtil.h" #include "Matrix.h" #include "Rect.h" @@ -39,6 +40,10 @@ class OffscreenBuffer; class RenderNode; struct Vertex; +namespace VectorDrawable { +class Tree; +} + /** * Authoritative op list, used for generating the op ID enum, ID based LUTS, and * the functions to which they dispatch. Parameter macros are executed for each op, @@ -75,6 +80,7 @@ struct Vertex; PRE_RENDER_OP_FN(EndLayerOp) \ PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \ PRE_RENDER_OP_FN(EndUnclippedLayerOp) \ + PRE_RENDER_OP_FN(VectorDrawableOp) \ \ RENDER_ONLY_OP_FN(ShadowOp) \ RENDER_ONLY_OP_FN(LayerOp) \ @@ -325,6 +331,13 @@ struct RoundRectPropsOp : RecordedOp { const float* ry; }; +struct VectorDrawableOp : RecordedOp { + VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS) + : SUPER_PAINTLESS(VectorDrawableOp) + , vectorDrawable(tree) {} + VectorDrawable::Tree* vectorDrawable; +}; + /** * Real-time, dynamic-lit shadow. * diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 2387962cc795..16929b8ac8ee 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -19,6 +19,7 @@ #include "DeferredLayerUpdater.h" #include "RecordedOp.h" #include "RenderNode.h" +#include "VectorDrawable.h" namespace android { namespace uirenderer { @@ -395,7 +396,6 @@ void RecordingCanvas::drawCircle( &x->value, &y->value, &radius->value)); } - void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { addOp(new (alloc()) OvalOp( Rect(left, top, right, bottom), @@ -422,6 +422,15 @@ void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { refPaint(&paint), refPath(&path))); } +void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { + mDisplayList->ref(tree); + addOp(new (alloc()) VectorDrawableOp( + tree, + Rect(tree->getBounds()), + *(mState.currentSnapshot()->transform), + getRecordedClip())); +} + // Bitmap-based void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { save(SaveFlags::Matrix); diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 375760f2dd81..cc14e6111cec 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -175,6 +175,8 @@ public: const uint16_t* indices, int indexCount, const SkPaint& paint) override { /* RecordingCanvas does not support drawVertices(); ignore */ } + virtual void drawVectorDrawable(VectorDrawableRoot* tree) override; + // Bitmap-based virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override; virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index d320a410bc84..bd4442dc378d 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -32,6 +32,8 @@ #include <SkTLazy.h> #include <SkTemplates.h> +#include "VectorDrawable.h" + #include <memory> namespace android { @@ -153,6 +155,7 @@ public: float hOffset, float vOffset, const SkPaint& paint) override; virtual bool drawTextAbsolutePos() const override { return true; } + virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right, @@ -742,6 +745,14 @@ void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chu NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr); } +void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { + const SkBitmap& bitmap = vectorDrawable->getBitmapUpdateIfDirty(); + SkRect bounds = vectorDrawable->getBounds(); + drawBitmap(bitmap, 0, 0, bitmap.width(), bitmap.height(), + bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, + vectorDrawable->getPaint()); +} + // ---------------------------------------------------------------------------- // Canvas draw operations: Text // ---------------------------------------------------------------------------- diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 1cf15ac01154..541c799f0149 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -138,18 +138,7 @@ void Path::setPath(const char* pathStr, size_t strLength) { } FullPath::FullPath(const FullPath& path) : Path(path) { - mStrokeWidth = path.mStrokeWidth; - mStrokeColor = path.mStrokeColor; - mStrokeAlpha = path.mStrokeAlpha; - mFillColor = path.mFillColor; - mFillAlpha = path.mFillAlpha; - mTrimPathStart = path.mTrimPathStart; - mTrimPathEnd = path.mTrimPathEnd; - mTrimPathOffset = path.mTrimPathOffset; - mStrokeMiterLimit = path.mStrokeMiterLimit; - mStrokeLineCap = path.mStrokeLineCap; - mStrokeLineJoin = path.mStrokeLineJoin; - + mProperties = path.mProperties; SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient); SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient); } @@ -159,7 +148,7 @@ const SkPath& FullPath::getUpdatedPath() { return mTrimmedSkPath; } Path::getUpdatedPath(); - if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) { + if (mProperties.trimPathStart != 0.0f || mProperties.trimPathEnd != 1.0f) { applyTrim(); return mTrimmedSkPath; } else { @@ -170,14 +159,14 @@ const SkPath& FullPath::getUpdatedPath() { void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha, SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin) { - mStrokeWidth = strokeWidth; - mStrokeColor = strokeColor; - mStrokeAlpha = strokeAlpha; - mFillColor = fillColor; - mFillAlpha = fillAlpha; - mStrokeMiterLimit = strokeMiterLimit; - mStrokeLineCap = SkPaint::Cap(strokeLineCap); - mStrokeLineJoin = SkPaint::Join(strokeLineJoin); + mProperties.strokeWidth = strokeWidth; + mProperties.strokeColor = strokeColor; + mProperties.strokeAlpha = strokeAlpha; + mProperties.fillColor = fillColor; + mProperties.fillAlpha = fillAlpha; + mProperties.strokeMiterLimit = strokeMiterLimit; + mProperties.strokeLineCap = strokeLineCap; + mProperties.strokeLineJoin = strokeLineJoin; // If any trim property changes, mark trim dirty and update the trim path setTrimPathStart(trimPathStart); @@ -195,12 +184,12 @@ void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float str // Draw path's fill, if fill color or gradient is valid bool needsFill = false; if (mFillGradient != nullptr) { - mPaint.setColor(applyAlpha(SK_ColorBLACK, mFillAlpha)); + mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.fillAlpha)); SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix); mPaint.setShader(newShader); needsFill = true; - } else if (mFillColor != SK_ColorTRANSPARENT) { - mPaint.setColor(applyAlpha(mFillColor, mFillAlpha)); + } else if (mProperties.fillColor != SK_ColorTRANSPARENT) { + mPaint.setColor(applyAlpha(mProperties.fillColor, mProperties.fillAlpha)); needsFill = true; } @@ -213,21 +202,21 @@ void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float str // Draw path's stroke, if stroke color or gradient is valid bool needsStroke = false; if (mStrokeGradient != nullptr) { - mPaint.setColor(applyAlpha(SK_ColorBLACK, mStrokeAlpha)); + mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.strokeAlpha)); SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix); mPaint.setShader(newShader); needsStroke = true; - } else if (mStrokeColor != SK_ColorTRANSPARENT) { - mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha)); + } else if (mProperties.strokeColor != SK_ColorTRANSPARENT) { + mPaint.setColor(applyAlpha(mProperties.strokeColor, mProperties.strokeAlpha)); needsStroke = true; } if (needsStroke) { mPaint.setStyle(SkPaint::Style::kStroke_Style); mPaint.setAntiAlias(true); - mPaint.setStrokeJoin(mStrokeLineJoin); - mPaint.setStrokeCap(mStrokeLineCap); - mPaint.setStrokeMiter(mStrokeMiterLimit); - mPaint.setStrokeWidth(mStrokeWidth * strokeScale); + mPaint.setStrokeJoin(SkPaint::Join(mProperties.strokeLineJoin)); + mPaint.setStrokeCap(SkPaint::Cap(mProperties.strokeLineCap)); + mPaint.setStrokeMiter(mProperties.strokeMiterLimit); + mPaint.setStrokeWidth(mProperties.strokeWidth * strokeScale); outCanvas->drawPath(renderPath, mPaint); } } @@ -236,14 +225,14 @@ void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float str * Applies trimming to the specified path. */ void FullPath::applyTrim() { - if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) { + if (mProperties.trimPathStart == 0.0f && mProperties.trimPathEnd == 1.0f) { // No trimming necessary. return; } SkPathMeasure measure(mSkPath, false); float len = SkScalarToFloat(measure.getLength()); - float start = len * fmod((mTrimPathStart + mTrimPathOffset), 1.0f); - float end = len * fmod((mTrimPathEnd + mTrimPathOffset), 1.0f); + float start = len * fmod((mProperties.trimPathStart + mProperties.trimPathOffset), 1.0f); + float end = len * fmod((mProperties.trimPathEnd + mProperties.trimPathOffset), 1.0f); mTrimmedSkPath.reset(); if (start > end) { @@ -255,76 +244,69 @@ void FullPath::applyTrim() { mTrimDirty = false; } -inline int putData(int8_t* outBytes, int startIndex, float value) { - int size = sizeof(float); - memcpy(&outBytes[startIndex], &value, size); - return size; -} - -inline int putData(int8_t* outBytes, int startIndex, int value) { - int size = sizeof(int); - memcpy(&outBytes[startIndex], &value, size); - return size; -} - -struct FullPathProperties { - // TODO: Consider storing full path properties in this struct instead of the fields. - float strokeWidth; - SkColor strokeColor; - float strokeAlpha; - SkColor fillColor; - float fillAlpha; - float trimPathStart; - float trimPathEnd; - float trimPathOffset; - int32_t strokeLineCap; - int32_t strokeLineJoin; - float strokeMiterLimit; -}; - -REQUIRE_COMPATIBLE_LAYOUT(FullPathProperties); +REQUIRE_COMPATIBLE_LAYOUT(FullPath::Properties); static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t"); static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t"); bool FullPath::getProperties(int8_t* outProperties, int length) { - int propertyDataSize = sizeof(FullPathProperties); + int propertyDataSize = sizeof(Properties); if (length != propertyDataSize) { LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided", propertyDataSize, length); return false; } - // TODO: consider replacing the property fields with a FullPathProperties struct. - FullPathProperties properties; - properties.strokeWidth = mStrokeWidth; - properties.strokeColor = mStrokeColor; - properties.strokeAlpha = mStrokeAlpha; - properties.fillColor = mFillColor; - properties.fillAlpha = mFillAlpha; - properties.trimPathStart = mTrimPathStart; - properties.trimPathEnd = mTrimPathEnd; - properties.trimPathOffset = mTrimPathOffset; - properties.strokeLineCap = mStrokeLineCap; - properties.strokeLineJoin = mStrokeLineJoin; - properties.strokeMiterLimit = mStrokeMiterLimit; - - memcpy(outProperties, &properties, length); + Properties* out = reinterpret_cast<Properties*>(outProperties); + *out = mProperties; return true; } +void FullPath::setColorPropertyValue(int propertyId, int32_t value) { + Property currentProperty = static_cast<Property>(propertyId); + if (currentProperty == Property::StrokeColor) { + mProperties.strokeColor = value; + } else if (currentProperty == Property::FillColor) { + mProperties.fillColor = value; + } else { + LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property with id: %d", + propertyId); + } +} + +void FullPath::setPropertyValue(int propertyId, float value) { + Property property = static_cast<Property>(propertyId); + switch (property) { + case Property::StrokeWidth: + setStrokeWidth(value); + break; + case Property::StrokeAlpha: + setStrokeAlpha(value); + break; + case Property::FillAlpha: + setFillAlpha(value); + break; + case Property::TrimPathStart: + setTrimPathStart(value); + break; + case Property::TrimPathEnd: + setTrimPathEnd(value); + break; + case Property::TrimPathOffset: + setTrimPathOffset(value); + break; + default: + LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId); + break; + } +} + void ClipPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float strokeScale, const SkMatrix& matrix){ outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op); } Group::Group(const Group& group) : Node(group) { - mRotate = group.mRotate; - mPivotX = group.mPivotX; - mPivotY = group.mPivotY; - mScaleX = group.mScaleX; - mScaleY = group.mScaleY; - mTranslateX = group.mTranslateX; - mTranslateY = group.mTranslateY; + mProperties = group.mProperties; } void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX, @@ -371,10 +353,11 @@ void Group::getLocalMatrix(SkMatrix* outMatrix) { outMatrix->reset(); // TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of // translating to pivot for rotating and scaling, then translating back. - outMatrix->postTranslate(-mPivotX, -mPivotY); - outMatrix->postScale(mScaleX, mScaleY); - outMatrix->postRotate(mRotate, 0, 0); - outMatrix->postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); + outMatrix->postTranslate(-mProperties.pivotX, -mProperties.pivotY); + outMatrix->postScale(mProperties.scaleX, mProperties.scaleY); + outMatrix->postRotate(mProperties.rotate, 0, 0); + outMatrix->postTranslate(mProperties.translateX + mProperties.pivotX, + mProperties.translateY + mProperties.pivotY); } void Group::addChild(Node* child) { @@ -388,38 +371,68 @@ bool Group::getProperties(float* outProperties, int length) { propertyCount, length); return false; } - for (int i = 0; i < propertyCount; i++) { - Property currentProperty = static_cast<Property>(i); - switch (currentProperty) { - case Property::Rotate_Property: - outProperties[i] = mRotate; - break; - case Property::PivotX_Property: - outProperties[i] = mPivotX; - break; - case Property::PivotY_Property: - outProperties[i] = mPivotY; - break; - case Property::ScaleX_Property: - outProperties[i] = mScaleX; - break; - case Property::ScaleY_Property: - outProperties[i] = mScaleY; - break; - case Property::TranslateX_Property: - outProperties[i] = mTranslateX; - break; - case Property::TranslateY_Property: - outProperties[i] = mTranslateY; - break; - default: - LOG_ALWAYS_FATAL("Invalid input index: %d", i); - return false; - } - } + Properties* out = reinterpret_cast<Properties*>(outProperties); + *out = mProperties; return true; } +// TODO: Consider animating the properties as float pointers +float Group::getPropertyValue(int propertyId) const { + Property currentProperty = static_cast<Property>(propertyId); + switch (currentProperty) { + case Property::Rotate: + return mProperties.rotate; + case Property::PivotX: + return mProperties.pivotX; + case Property::PivotY: + return mProperties.pivotY; + case Property::ScaleX: + return mProperties.scaleX; + case Property::ScaleY: + return mProperties.scaleY; + case Property::TranslateX: + return mProperties.translateX; + case Property::TranslateY: + return mProperties.translateY; + default: + LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId); + return 0; + } +} + +void Group::setPropertyValue(int propertyId, float value) { + Property currentProperty = static_cast<Property>(propertyId); + switch (currentProperty) { + case Property::Rotate: + mProperties.rotate = value; + break; + case Property::PivotX: + mProperties.pivotX = value; + break; + case Property::PivotY: + mProperties.pivotY = value; + break; + case Property::ScaleX: + mProperties.scaleX = value; + break; + case Property::ScaleY: + mProperties.scaleY = value; + break; + case Property::TranslateX: + mProperties.translateX = value; + break; + case Property::TranslateY: + mProperties.translateY = value; + break; + default: + LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId); + } +} + +bool Group::isValidProperty(int propertyId) { + return propertyId >= 0 && propertyId < static_cast<int>(Property::Count); +} + void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, const SkRect& bounds, bool needsMirroring, bool canReuseCache) { // The imageView can scale the canvas in different ways, in order to @@ -445,6 +458,8 @@ void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, return; } + mPaint.setColorFilter(colorFilter); + int saveCount = outCanvas->save(SaveFlags::MatrixClip); outCanvas->translate(mBounds.fLeft, mBounds.fTop); @@ -458,43 +473,33 @@ void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, // And we use this bound for the destination rect for the drawBitmap, so // we offset to (0, 0); mBounds.offsetTo(0, 0); - createCachedBitmapIfNeeded(scaledWidth, scaledHeight); - if (!mAllowCaching) { - updateCachedBitmap(scaledWidth, scaledHeight); - } else { - if (!canReuseCache || mCacheDirty) { - updateCachedBitmap(scaledWidth, scaledHeight); - } - } - drawCachedBitmapWithRootAlpha(outCanvas, colorFilter, mBounds); + + outCanvas->drawVectorDrawable(this); outCanvas->restoreToCount(saveCount); } -void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter, - const SkRect& originalBounds) { +SkPaint* Tree::getPaint() { SkPaint* paint; - if (mRootAlpha == 1.0f && filter == NULL) { + if (mRootAlpha == 1.0f && mPaint.getColorFilter() == NULL) { paint = NULL; } else { mPaint.setFilterQuality(kLow_SkFilterQuality); mPaint.setAlpha(mRootAlpha * 255); - mPaint.setColorFilter(filter); paint = &mPaint; } - outCanvas->drawBitmap(mCachedBitmap, 0, 0, mCachedBitmap.width(), mCachedBitmap.height(), - originalBounds.fLeft, originalBounds.fTop, originalBounds.fRight, - originalBounds.fBottom, paint); + return paint; } -void Tree::updateCachedBitmap(int width, int height) { +const SkBitmap& Tree::getBitmapUpdateIfDirty() { mCachedBitmap.eraseColor(SK_ColorTRANSPARENT); SkCanvas outCanvas(mCachedBitmap); - float scaleX = width / mViewportWidth; - float scaleY = height / mViewportHeight; + float scaleX = (float) mCachedBitmap.width() / mViewportWidth; + float scaleY = (float) mCachedBitmap.height() / mViewportHeight; mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY); mCacheDirty = false; + return mCachedBitmap; } void Tree::createCachedBitmapIfNeeded(int width, int height) { diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index 09bdce596a21..f8f1ea62a624 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -18,6 +18,7 @@ #define ANDROID_HWUI_VPATH_H #include "Canvas.h" + #include <SkBitmap.h> #include <SkColor.h> #include <SkCanvas.h> @@ -104,6 +105,21 @@ protected: class ANDROID_API FullPath: public Path { public: + +struct Properties { + 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; +}; + FullPath(const FullPath& path); // for cloning FullPath(const char* path, size_t strLength) : Path(path, strLength) {} FullPath() : Path() {} @@ -118,55 +134,58 @@ public: float strokeAlpha, SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin); + // TODO: Cleanup: Remove the setter and getters below, and their counterparts in java and JNI float getStrokeWidth() { - return mStrokeWidth; + return mProperties.strokeWidth; } void setStrokeWidth(float strokeWidth) { - mStrokeWidth = strokeWidth; + mProperties.strokeWidth = strokeWidth; } SkColor getStrokeColor() { - return mStrokeColor; + return mProperties.strokeColor; } void setStrokeColor(SkColor strokeColor) { - mStrokeColor = strokeColor; + mProperties.strokeColor = strokeColor; } float getStrokeAlpha() { - return mStrokeAlpha; + return mProperties.strokeAlpha; } void setStrokeAlpha(float strokeAlpha) { - mStrokeAlpha = strokeAlpha; + mProperties.strokeAlpha = strokeAlpha; } SkColor getFillColor() { - return mFillColor; + return mProperties.fillColor; } void setFillColor(SkColor fillColor) { - mFillColor = fillColor; + mProperties.fillColor = fillColor; } float getFillAlpha() { - return mFillAlpha; + return mProperties.fillAlpha; } void setFillAlpha(float fillAlpha) { - mFillAlpha = fillAlpha; + mProperties.fillAlpha = fillAlpha; } float getTrimPathStart() { - return mTrimPathStart; + return mProperties.trimPathStart; } void setTrimPathStart(float trimPathStart) { - VD_SET_PROP_WITH_FLAG(mTrimPathStart, trimPathStart, mTrimDirty); + VD_SET_PROP_WITH_FLAG(mProperties.trimPathStart, trimPathStart, mTrimDirty); } float getTrimPathEnd() { - return mTrimPathEnd; + return mProperties.trimPathEnd; } void setTrimPathEnd(float trimPathEnd) { - VD_SET_PROP_WITH_FLAG(mTrimPathEnd, trimPathEnd, mTrimDirty); + VD_SET_PROP_WITH_FLAG(mProperties.trimPathEnd, trimPathEnd, mTrimDirty); } float getTrimPathOffset() { - return mTrimPathOffset; + return mProperties.trimPathOffset; } void setTrimPathOffset(float trimPathOffset) { - VD_SET_PROP_WITH_FLAG(mTrimPathOffset, trimPathOffset, mTrimDirty); + VD_SET_PROP_WITH_FLAG(mProperties.trimPathOffset, trimPathOffset, mTrimDirty); } bool getProperties(int8_t* outProperties, int length); + void setColorPropertyValue(int propertyId, int32_t value); + void setPropertyValue(int propertyId, float value); void setFillGradient(SkShader* fillGradient) { SkRefCnt_SafeAssign(mFillGradient, fillGradient); @@ -182,24 +201,28 @@ protected: float strokeScale, const SkMatrix& matrix) override; private: + enum class Property { + StrokeWidth = 0, + StrokeColor, + StrokeAlpha, + FillColor, + FillAlpha, + TrimPathStart, + TrimPathEnd, + TrimPathOffset, + StrokeLineCap, + StrokeLineJoin, + StrokeMiterLimit, + Count, + }; // Applies trimming to the specified path. void applyTrim(); - float mStrokeWidth = 0; - SkColor mStrokeColor = SK_ColorTRANSPARENT; - float mStrokeAlpha = 1; - SkColor mFillColor = SK_ColorTRANSPARENT; - SkShader* mStrokeGradient = nullptr; - SkShader* mFillGradient = nullptr; - float mFillAlpha = 1; - float mTrimPathStart = 0; - float mTrimPathEnd = 1; - float mTrimPathOffset = 0; + Properties mProperties; bool mTrimDirty = true; - SkPaint::Cap mStrokeLineCap = SkPaint::Cap::kButt_Cap; - SkPaint::Join mStrokeLineJoin = SkPaint::Join::kMiter_Join; - float mStrokeMiterLimit = 4; SkPath mTrimmedSkPath; SkPaint mPaint; + SkShader* mStrokeGradient = nullptr; + SkShader* mFillGradient = nullptr; }; class ANDROID_API ClipPath: public Path { @@ -216,49 +239,58 @@ protected: class ANDROID_API Group: public Node { public: + struct Properties { + float rotate = 0; + float pivotX = 0; + float pivotY = 0; + float scaleX = 1; + float scaleY = 1; + float translateX = 0; + float translateY = 0; + }; Group(const Group& group); Group() {} float getRotation() { - return mRotate; + return mProperties.rotate; } void setRotation(float rotation) { - mRotate = rotation; + mProperties.rotate = rotation; } float getPivotX() { - return mPivotX; + return mProperties.pivotX; } void setPivotX(float pivotX) { - mPivotX = pivotX; + mProperties.pivotX = pivotX; } float getPivotY() { - return mPivotY; + return mProperties.pivotY; } void setPivotY(float pivotY) { - mPivotY = pivotY; + mProperties.pivotY = pivotY; } float getScaleX() { - return mScaleX; + return mProperties.scaleX; } void setScaleX(float scaleX) { - mScaleX = scaleX; + mProperties.scaleX = scaleX; } float getScaleY() { - return mScaleY; + return mProperties.scaleY; } void setScaleY(float scaleY) { - mScaleY = scaleY; + mProperties.scaleY = scaleY; } float getTranslateX() { - return mTranslateX; + return mProperties.translateX; } void setTranslateX(float translateX) { - mTranslateX = translateX; + mProperties.translateX = translateX; } float getTranslateY() { - return mTranslateY; + return mProperties.translateY; } void setTranslateY(float translateY) { - mTranslateY = translateY; + mProperties.translateY = translateY; } virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX, float scaleY) override; @@ -268,38 +300,33 @@ public: void addChild(Node* child); void dump() override; bool getProperties(float* outProperties, int length); + float getPropertyValue(int propertyId) const; + void setPropertyValue(int propertyId, float value); + static bool isValidProperty(int propertyId); private: enum class Property { - Rotate_Property = 0, - PivotX_Property, - PivotY_Property, - ScaleX_Property, - ScaleY_Property, - TranslateX_Property, - TranslateY_Property, + Rotate = 0, + PivotX, + PivotY, + ScaleX, + ScaleY, + TranslateX, + TranslateY, // Count of the properties, must be at the end. Count, }; - float mRotate = 0; - float mPivotX = 0; - float mPivotY = 0; - float mScaleX = 1; - float mScaleY = 1; - float mTranslateX = 0; - float mTranslateY = 0; std::vector<Node*> mChildren; + Properties mProperties; }; -class ANDROID_API Tree { +class ANDROID_API Tree : public VirtualLightRefBase { public: Tree(Group* rootNode) : mRootNode(rootNode) {} void draw(Canvas* outCanvas, SkColorFilter* colorFilter, const SkRect& bounds, bool needsMirroring, bool canReuseCache); - void drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter, - const SkRect& originalBounds); - void updateCachedBitmap(int width, int height); + const SkBitmap& getBitmapUpdateIfDirty(); void createCachedBitmapIfNeeded(int width, int height); bool canReuseBitmap(int width, int height); void setAllowCaching(bool allowCaching) { @@ -316,6 +343,10 @@ public: mViewportWidth = viewportWidth; mViewportHeight = viewportHeight; } + SkPaint* getPaint(); + const SkRect& getBounds() const { + return mBounds; + } private: // Cap the bitmap size, such that it won't hurt the performance too much |