Merge "Support auto-dark for VectorDrawable"
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -49,4 +49,5 @@ X(DrawPatch)
|
||||
X(DrawPoints)
|
||||
X(DrawVertices)
|
||||
X(DrawAtlas)
|
||||
X(DrawShadowRec)
|
||||
X(DrawShadowRec)
|
||||
X(DrawVectorDrawable)
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
mStagingProperties.getBounds().bottom(), &paint);
|
||||
}
|
||||
|
||||
SkPaint* Tree::getPaint() {
|
||||
return updatePaint(&mPaint, &mProperties);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user