diff options
author | 2018-09-20 16:27:46 -0700 | |
---|---|---|
committer | 2018-09-21 09:56:42 -0700 | |
commit | 08ee815625770e69146b7899e1701e2b2e85464e (patch) | |
tree | 9ae931c890b45e7c377951ba1c3ebb4a4b02f3db | |
parent | 4603406afe2667b1eac5b07bc21d3b44443a9e9a (diff) |
Support auto-dark for VectorDrawable
Also fixes a bug where non-animatable properties (colorfilter)
weren't captured at record-time
Test: poked around, quick settings doesn't look awful
Change-Id: I57312dd5eb70f477814a4d898963ee010153c243
-rw-r--r-- | core/jni/android_graphics_drawable_VectorDrawable.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/CanvasTransform.cpp | 18 | ||||
-rw-r--r-- | libs/hwui/DisplayListOps.in | 3 | ||||
-rw-r--r-- | libs/hwui/RecordingCanvas.cpp | 30 | ||||
-rw-r--r-- | libs/hwui/RecordingCanvas.h | 3 | ||||
-rw-r--r-- | libs/hwui/VectorDrawable.cpp | 105 | ||||
-rw-r--r-- | libs/hwui/VectorDrawable.h | 26 | ||||
-rw-r--r-- | libs/hwui/hwui/Bitmap.h | 2 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 19 |
9 files changed, 162 insertions, 46 deletions
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp index 6f4a58aadca3..58a2379a6999 100644 --- a/core/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp @@ -130,7 +130,7 @@ static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) { static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) { VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr); - return tree->stagingProperties()->getRootAlpha(); + return tree->stagingProperties().getRootAlpha(); } static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr, diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp index a03b31723808..06e937ab66f4 100644 --- a/libs/hwui/CanvasTransform.cpp +++ b/libs/hwui/CanvasTransform.cpp @@ -79,7 +79,6 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { info.fColors = _colorStorage.data(); info.fColorOffsets = _offsetStorage.data(); SkShader::GradientType type = paint.getShader()->asAGradient(&info); - ALOGW_IF(type, "Found gradient of type = %d", type); if (info.fColorCount <= 10) { switch (type) { @@ -108,6 +107,22 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { } } +static BitmapPalette paletteForColorHSV(SkColor color) { + float hsv[3]; + SkColorToHSV(color, hsv); + return hsv[2] >= .5f ? BitmapPalette::Light : BitmapPalette::Dark; +} + +static BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette) { + if (palette == BitmapPalette::Unknown || !paint || !paint->getColorFilter()) { + return palette; + } + + SkColor color = palette == BitmapPalette::Light ? SK_ColorWHITE : SK_ColorBLACK; + color = paint->getColorFilter()->filterColor(color); + return paletteForColorHSV(color); +} + bool transformPaint(ColorTransform transform, SkPaint* paint) { // TODO applyColorTransform(transform, *paint); @@ -115,6 +130,7 @@ bool transformPaint(ColorTransform transform, SkPaint* paint) { } bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) { + palette = filterPalette(paint, palette); bool shouldInvert = false; if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) { shouldInvert = true; diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index f61c1562c6a7..04cf611c8322 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -49,4 +49,5 @@ X(DrawPatch) X(DrawPoints) X(DrawVertices) X(DrawAtlas) -X(DrawShadowRec)
\ No newline at end of file +X(DrawShadowRec) +X(DrawVectorDrawable)
\ No newline at end of file diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 5f54c025c819..c30af842ebbb 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -16,6 +16,8 @@ #include "RecordingCanvas.h" +#include "VectorDrawable.h" + #include "SkCanvas.h" #include "SkData.h" #include "SkDrawShadowInfo.h" @@ -498,6 +500,27 @@ struct DrawShadowRec final : Op { SkDrawShadowRec fRec; void draw(SkCanvas* c, const SkMatrix&) const { c->private_draw_shadow_rec(fPath, fRec); } }; + +struct DrawVectorDrawable final : Op { + static const auto kType = Type::DrawVectorDrawable; + DrawVectorDrawable(VectorDrawableRoot* tree) + : mRoot(tree) + , mBounds(tree->stagingProperties().getBounds()) + , palette(tree->computePalette()) { + // Recording, so use staging properties + tree->getPaintFor(&paint, tree->stagingProperties()); + } + + void draw(SkCanvas* canvas, const SkMatrix&) const { + mRoot->draw(canvas, mBounds, paint); + } + + sp<VectorDrawableRoot> mRoot; + SkRect mBounds; + SkPaint paint; + BitmapPalette palette; +}; + } template <typename T, typename... Args> @@ -698,6 +721,9 @@ void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { this->push<DrawShadowRec>(0, path, rec); } +void DisplayListData::drawVectorDrawable(VectorDrawableRoot* tree) { + this->push<DrawVectorDrawable>(0, tree); +} typedef void (*draw_fn)(const void*, SkCanvas*, const SkMatrix&); typedef void (*void_fn)(const void*); @@ -962,5 +988,9 @@ void RecordingCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& fDL->drawShadowRec(path, rec); } +void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { + fDL->drawVectorDrawable(tree); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index eecf51ce0996..80c80ca46515 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -120,6 +120,7 @@ private: void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, SkBlendMode, const SkRect*, const SkPaint*); void drawShadowRec(const SkPath&, const SkDrawShadowRec&); + void drawVectorDrawable(VectorDrawableRoot* tree); template <typename T, typename... Args> void* push(size_t, Args&&...); @@ -205,6 +206,8 @@ public: SkBlendMode, const SkRect*, const SkPaint*) override; void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + void drawVectorDrawable(VectorDrawableRoot* tree); + private: typedef SkCanvasVirtualEnforcer<SkNoDrawCanvas> INHERITED; diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 9f82d0fa3018..dbbe9f3acf53 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -458,29 +458,22 @@ void Tree::drawStaging(Canvas* outCanvas) { mStagingCache.dirty = false; } - SkPaint tmpPaint; - SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties); + SkPaint paint; + getPaintFor(&paint, mStagingProperties); outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(), mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(), mStagingProperties.getBounds().right(), - mStagingProperties.getBounds().bottom(), paint); -} - -SkPaint* Tree::getPaint() { - return updatePaint(&mPaint, &mProperties); + mStagingProperties.getBounds().bottom(), &paint); } -// Update the given paint with alpha and color filter. Return nullptr if no color filter is -// specified and root alpha is 1. Otherwise, return updated paint. -SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) { +void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const { // HWUI always draws VD with bilinear filtering. outPaint->setFilterQuality(kLow_SkFilterQuality); - if (prop->getRootAlpha() < 1.0f || prop->getColorFilter() != nullptr) { - outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter())); - outPaint->setAlpha(prop->getRootAlpha() * 255); + if (prop.getRootAlpha() < 1.0f || prop.getColorFilter() != nullptr) { + outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter())); + outPaint->setAlpha(prop.getRootAlpha() * 255); } - return outPaint; } Bitmap& Tree::getBitmapUpdateIfDirty() { @@ -553,11 +546,15 @@ void Tree::Cache::clear() { mAtlasKey = INVALID_ATLAS_KEY; } -void Tree::draw(SkCanvas* canvas, const SkRect& bounds) { +void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) { + // Update the paint for any animatable properties + SkPaint paint = inPaint; + paint.setAlpha(mProperties.getRootAlpha() * 255); + SkRect src; sk_sp<SkSurface> vdSurface = mCache.getSurface(&src); if (vdSurface) { - canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, getPaint(), + canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint, SkCanvas::kFast_SrcRectConstraint); } else { // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure. @@ -570,7 +567,7 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds) { int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, - getPaint(), SkCanvas::kFast_SrcRectConstraint); + &paint, SkCanvas::kFast_SrcRectConstraint); mCache.clear(); markDirty(); } @@ -620,6 +617,80 @@ void Tree::onPropertyChanged(TreeProperties* prop) { } } +class MinMaxAverage { +public: + void add(float sample) { + if (mCount == 0) { + mMin = sample; + mMax = sample; + } else { + mMin = std::min(mMin, sample); + mMax = std::max(mMax, sample); + } + mTotal += sample; + mCount++; + } + + float average() { return mTotal / mCount; } + + float min() { return mMin; } + + float max() { return mMax; } + + float delta() { return mMax - mMin; } + +private: + float mMin = 0.0f; + float mMax = 0.0f; + float mTotal = 0.0f; + int mCount = 0; +}; + +BitmapPalette Tree::computePalette() { + // TODO Cache this and share the code with Bitmap.cpp + + ATRACE_CALL(); + + // TODO: This calculation of converting to HSV & tracking min/max is probably overkill + // Experiment with something simpler since we just want to figure out if it's "color-ful" + // and then the average perceptual lightness. + + MinMaxAverage hue, saturation, value; + int sampledCount = 0; + + // Sample a grid of 100 pixels to get an overall estimation of the colors in play + mRootNode->forEachFillColor([&](SkColor color) { + if (SkColorGetA(color) < 75) { + return; + } + sampledCount++; + float hsv[3]; + SkColorToHSV(color, hsv); + hue.add(hsv[0]); + saturation.add(hsv[1]); + value.add(hsv[2]); + }); + + if (sampledCount == 0) { + ALOGV("VectorDrawable is mostly translucent"); + return BitmapPalette::Unknown; + } + + ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = " + "%f]; value [min = %f, max = %f, avg = %f]", + sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(), + saturation.average(), value.min(), value.max(), value.average()); + + if (hue.delta() <= 20 && saturation.delta() <= .1f) { + if (value.average() >= .5f) { + return BitmapPalette::Light; + } else { + return BitmapPalette::Dark; + } + } + return BitmapPalette::Unknown; +} + }; // namespace VectorDrawable }; // namespace uirenderer diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index af0ae05a74b3..9c0bb161380c 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -120,6 +120,8 @@ public: 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; @@ -349,6 +351,9 @@ public: } } 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; @@ -480,6 +485,12 @@ public: } } + 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); @@ -495,8 +506,8 @@ public: // 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()); + 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. @@ -506,7 +517,6 @@ public: Bitmap& getBitmapUpdateIfDirty(); void setAllowCaching(bool allowCaching) { mAllowCaching = allowCaching; } - SkPaint* getPaint(); void syncProperties() { if (mStagingProperties.mNonAnimatablePropertiesDirty) { mCache.dirty |= (mProperties.mNonAnimatableProperties.viewportWidth != @@ -618,7 +628,7 @@ public: }; void onPropertyChanged(TreeProperties* prop); TreeProperties* mutateStagingProperties() { return &mStagingProperties; } - const TreeProperties* stagingProperties() const { return &mStagingProperties; } + const TreeProperties& stagingProperties() const { return mStagingProperties; } // This should only be called from animations on RT TreeProperties* mutateProperties() { return &mProperties; } @@ -636,7 +646,10 @@ public: * 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); + void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint); + + void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const; + BitmapPalette computePalette(); /** * Draws VD into a GPU backed surface. @@ -680,7 +693,6 @@ private: skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY; }; - SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop); bool allocateBitmapIfNeeded(Cache& cache, int width, int height); bool canReuseBitmap(Bitmap*, int width, int height); void updateBitmapCache(Bitmap& outCache, bool useStagingData); @@ -696,8 +708,6 @@ private: TreeProperties mProperties = TreeProperties(this); TreeProperties mStagingProperties = TreeProperties(this); - SkPaint mPaint; - Cache mStagingCache; Cache mCache; diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 170335da7d7c..43d457a77b15 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -34,6 +34,8 @@ enum class PixelStorageType { Hardware, }; +// TODO: Find a better home for this. It's here because hwui/Bitmap is exported and CanvasTransform +// isn't, but cleanup should be done enum class BitmapPalette { Unknown, Light, diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 3042006e5684..fac07d74dad4 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -130,25 +130,8 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, drawDrawable(functorDrawable); } -class VectorDrawable : public SkDrawable { -public: - VectorDrawable(VectorDrawableRoot* tree) - : mRoot(tree) - , mBounds(tree->stagingProperties()->getBounds()) {} - -protected: - virtual SkRect onGetBounds() override { return mBounds; } - virtual void onDraw(SkCanvas* canvas) override { - mRoot->draw(canvas, mBounds); - } - -private: - sp<VectorDrawableRoot> mRoot; - SkRect mBounds; -}; - void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { - drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree)); + mRecorder.drawVectorDrawable(tree); mDisplayList->mVectorDrawables.push_back(tree); } |