Merge "Create wrapper around Shader native implementation"

This commit is contained in:
Nader Jawad
2020-08-18 06:12:45 +00:00
committed by Android (Google) Code Review
31 changed files with 905 additions and 115 deletions

View File

@@ -3111,7 +3111,7 @@ public class Paint {
@CriticalNative
private static native boolean nGetFillPath(long paintPtr, long src, long dst);
@CriticalNative
private static native long nSetShader(long paintPtr, long shader);
private static native void nSetShader(long paintPtr, long shader);
@CriticalNative
private static native long nSetColorFilter(long paintPtr, long filter);
@CriticalNative

View File

@@ -462,6 +462,13 @@ cc_defaults {
"RenderNode.cpp",
"RenderProperties.cpp",
"RootRenderNode.cpp",
"shader/Shader.cpp",
"shader/BitmapShader.cpp",
"shader/ComposeShader.cpp",
"shader/LinearGradientShader.cpp",
"shader/RadialGradientShader.cpp",
"shader/RuntimeShader.cpp",
"shader/SweepGradientShader.cpp",
"SkiaCanvas.cpp",
"VectorDrawable.cpp",
],

View File

@@ -42,6 +42,8 @@
#include <SkTextBlob.h>
#include <SkVertices.h>
#include <shader/BitmapShader.h>
#include <memory>
#include <optional>
#include <utility>
@@ -49,6 +51,7 @@
namespace android {
using uirenderer::PaintUtils;
using uirenderer::BitmapShader;
Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
return new SkiaCanvas(bitmap);
@@ -681,7 +684,9 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
if (paint) {
pnt = *paint;
}
pnt.setShader(bitmap.makeImage()->makeShader());
pnt.setShader(sk_ref_sp(new BitmapShader(
bitmap.makeImage(), SkTileMode::kClamp, SkTileMode::kClamp, nullptr)));
auto v = builder.detach();
apply_looper(&pnt, [&](const SkPaint& p) {
mCanvas->drawVertices(v, SkBlendMode::kModulate, p);

View File

@@ -23,7 +23,6 @@
#include "PathParser.h"
#include "SkColorFilter.h"
#include "SkImageInfo.h"
#include "SkShader.h"
#include "hwui/Paint.h"
#ifdef __ANDROID__
@@ -159,10 +158,10 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) {
// Draw path's fill, if fill color or gradient is valid
bool needsFill = false;
SkPaint paint;
Paint paint;
if (properties.getFillGradient() != nullptr) {
paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha()));
paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient())));
paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getFillGradient())));
needsFill = true;
} else if (properties.getFillColor() != SK_ColorTRANSPARENT) {
paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha()));
@@ -179,7 +178,7 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) {
bool needsStroke = false;
if (properties.getStrokeGradient() != nullptr) {
paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha()));
paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient())));
paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getStrokeGradient())));
needsStroke = true;
} else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) {
paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha()));

View File

@@ -31,8 +31,8 @@
#include <SkPath.h>
#include <SkPathMeasure.h>
#include <SkRect.h>
#include <SkShader.h>
#include <SkSurface.h>
#include <shader/Shader.h>
#include <cutils/compiler.h>
#include <stddef.h>
@@ -227,20 +227,20 @@ public:
strokeGradient = prop.strokeGradient;
onPropertyChanged();
}
void setFillGradient(SkShader* gradient) {
void setFillGradient(Shader* gradient) {
if (fillGradient.get() != gradient) {
fillGradient = sk_ref_sp(gradient);
onPropertyChanged();
}
}
void setStrokeGradient(SkShader* gradient) {
void setStrokeGradient(Shader* gradient) {
if (strokeGradient.get() != gradient) {
strokeGradient = sk_ref_sp(gradient);
onPropertyChanged();
}
}
SkShader* getFillGradient() const { return fillGradient.get(); }
SkShader* getStrokeGradient() const { return strokeGradient.get(); }
Shader* getFillGradient() const { return fillGradient.get(); }
Shader* getStrokeGradient() const { return strokeGradient.get(); }
float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; }
void setStrokeWidth(float strokeWidth) {
VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
@@ -320,8 +320,8 @@ public:
count,
};
PrimitiveFields mPrimitiveFields;
sk_sp<SkShader> fillGradient;
sk_sp<SkShader> strokeGradient;
sk_sp<Shader> fillGradient;
sk_sp<Shader> strokeGradient;
};
// Called from UI thread

View File

@@ -73,7 +73,7 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa
static void simplifyPaint(int color, Paint* paint) {
paint->setColor(color);
paint->setShader(nullptr);
paint->setShader((sk_sp<uirenderer::Shader>)nullptr);
paint->setColorFilter(nullptr);
paint->setLooper(nullptr);
paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());

View File

@@ -30,6 +30,8 @@
#include <minikin/FamilyVariant.h>
#include <minikin/Hyphenator.h>
#include <shader/Shader.h>
namespace android {
class Paint : public SkPaint {
@@ -149,8 +151,14 @@ public:
// The only respected flags are : [ antialias, dither, filterBitmap ]
static uint32_t GetSkPaintJavaFlags(const SkPaint&);
static void SetSkPaintJavaFlags(SkPaint*, uint32_t flags);
void setShader(sk_sp<uirenderer::Shader> shader);
private:
using SkPaint::setShader;
using SkPaint::setImageFilter;
SkFont mFont;
sk_sp<SkDrawLooper> mLooper;
@@ -169,6 +177,7 @@ private:
bool mStrikeThru = false;
bool mUnderline = false;
bool mDevKern = false;
sk_sp<uirenderer::Shader> mShader;
};
} // namespace android

View File

@@ -24,7 +24,8 @@ Paint::Paint()
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
, mFamilyVariant(minikin::FamilyVariant::DEFAULT) {
, mFamilyVariant(minikin::FamilyVariant::DEFAULT)
, mShader(nullptr) {
// SkPaint::antialiasing defaults to false, but
// SkFont::edging defaults to kAntiAlias. To keep them
// insync, we manually set the font to kAilas.
@@ -45,7 +46,8 @@ Paint::Paint(const Paint& paint)
, mAlign(paint.mAlign)
, mStrikeThru(paint.mStrikeThru)
, mUnderline(paint.mUnderline)
, mDevKern(paint.mDevKern) {}
, mDevKern(paint.mDevKern)
, mShader(paint.mShader){}
Paint::~Paint() {}
@@ -65,9 +67,30 @@ Paint& Paint::operator=(const Paint& other) {
mStrikeThru = other.mStrikeThru;
mUnderline = other.mUnderline;
mDevKern = other.mDevKern;
mShader = other.mShader;
return *this;
}
void Paint::setShader(sk_sp<uirenderer::Shader> shader) {
if (shader) {
// If there is an SkShader compatible shader, apply it
sk_sp<SkShader> skShader = shader->asSkShader();
if (skShader.get()) {
SkPaint::setShader(skShader);
SkPaint::setImageFilter(nullptr);
} else {
// ... otherwise the specified shader can only be represented as an ImageFilter
SkPaint::setShader(nullptr);
SkPaint::setImageFilter(shader->asSkImageFilter());
}
} else {
// No shader is provided at all, clear out both the SkShader and SkImageFilter slots
SkPaint::setShader(nullptr);
SkPaint::setImageFilter(nullptr);
}
mShader = shader;
}
bool operator==(const Paint& a, const Paint& b) {
return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) &&
a.mFont == b.mFont &&

View File

@@ -47,6 +47,7 @@
#include <minikin/LocaleList.h>
#include <minikin/Measurement.h>
#include <minikin/MinikinPaint.h>
#include <shader/Shader.h>
#include <unicode/utf16.h>
#include <cassert>
@@ -54,6 +55,8 @@
#include <memory>
#include <vector>
using namespace android::uirenderer;
namespace android {
struct JMetricsID {
@@ -782,11 +785,10 @@ namespace PaintGlue {
return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
}
static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
obj->setShader(sk_ref_sp(shader));
return reinterpret_cast<jlong>(obj->getShader());
static void setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) {
auto* paint = reinterpret_cast<Paint*>(objHandle);
auto* shader = reinterpret_cast<Shader*>(shaderHandle);
paint->setShader(sk_ref_sp(shader));
}
static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) {
@@ -1097,7 +1099,7 @@ static const JNINativeMethod methods[] = {
{"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
{"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
{"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
{"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
{"nSetShader","(JJ)V", (void*) PaintGlue::setShader},
{"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
{"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
{"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},

View File

@@ -5,6 +5,13 @@
#include "SkShader.h"
#include "SkBlendMode.h"
#include "include/effects/SkRuntimeEffect.h"
#include "shader/Shader.h"
#include "shader/BitmapShader.h"
#include "shader/ComposeShader.h"
#include "shader/LinearGradientShader.h"
#include "shader/RadialGradientShader.h"
#include "shader/RuntimeShader.h"
#include "shader/SweepGradientShader.h"
#include <vector>
@@ -50,7 +57,7 @@ static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvAr
///////////////////////////////////////////////////////////////////////////////////////////////
static void Shader_safeUnref(SkShader* shader) {
static void Shader_safeUnref(Shader* shader) {
SkSafeUnref(shader);
}
@@ -74,15 +81,15 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j
SkBitmap bitmap;
image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
}
sk_sp<SkShader> shader = image->makeShader(
(SkTileMode)tileModeX, (SkTileMode)tileModeY);
ThrowIAE_IfNull(env, shader.get());
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
auto* shader = new BitmapShader(
image,
static_cast<SkTileMode>(tileModeX),
static_cast<SkTileMode>(tileModeY),
matrix
);
return reinterpret_cast<jlong>(shader.release());
return reinterpret_cast<jlong>(shader);
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -118,17 +125,18 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
#error Need to convert float array to SkScalar array before calling the following function.
#endif
sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr));
ThrowIAE_IfNull(env, shader);
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
auto* shader = new LinearGradientShader(
pts,
colors,
GraphicsJNI::getNativeColorSpace(colorSpaceHandle),
pos,
static_cast<SkTileMode>(tileMode),
sGradientShaderFlags,
matrix
);
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
return reinterpret_cast<jlong>(shader.release());
return reinterpret_cast<jlong>(shader);
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -148,17 +156,20 @@ static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat
#error Need to convert float array to SkScalar array before calling the following function.
#endif
sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr);
ThrowIAE_IfNull(env, shader);
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
auto* shader = new RadialGradientShader(
center,
radius,
colors,
GraphicsJNI::getNativeColorSpace(colorSpaceHandle),
pos,
static_cast<SkTileMode>(tileMode),
sGradientShaderFlags,
matrix
);
return reinterpret_cast<jlong>(shader.release());
return reinterpret_cast<jlong>(shader);
}
///////////////////////////////////////////////////////////////////////////////
@@ -174,54 +185,58 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat
#error Need to convert float array to SkScalar array before calling the following function.
#endif
sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
sGradientShaderFlags, nullptr);
ThrowIAE_IfNull(env, shader);
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
auto* shader = new SweepGradientShader(
x,
y,
colors,
GraphicsJNI::getNativeColorSpace(colorSpaceHandle),
pos,
sGradientShaderFlags,
matrix
);
return reinterpret_cast<jlong>(shader.release());
return reinterpret_cast<jlong>(shader);
}
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
sk_sp<SkShader> baseShader(SkShaders::Blend(mode,
sk_ref_sp(shaderA), sk_ref_sp(shaderB)));
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
auto* shaderA = reinterpret_cast<Shader*>(shaderAHandle);
auto* shaderB = reinterpret_cast<Shader*>(shaderBHandle);
SkShader* shader;
auto mode = static_cast<SkBlendMode>(xfermodeHandle);
if (matrix) {
shader = baseShader->makeWithLocalMatrix(*matrix).release();
} else {
shader = baseShader.release();
}
return reinterpret_cast<jlong>(shader);
auto* composeShader = new ComposeShader(
*shaderA,
*shaderB,
mode,
matrix
);
return reinterpret_cast<jlong>(composeShader);
}
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
AutoJavaByteArray arInputs(env, inputs);
sk_sp<SkData> fData;
fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
ThrowIAE_IfNull(env, shader);
auto data = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
return reinterpret_cast<jlong>(shader.release());
auto* shader = new RuntimeShader(
*effect,
std::move(data),
isOpaque == JNI_TRUE,
matrix
);
return reinterpret_cast<jlong>(shader);
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -239,12 +254,8 @@ static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sks
///////////////////////////////////////////////////////////////////////////////////////////////
static void Effect_safeUnref(SkRuntimeEffect* effect) {
SkSafeUnref(effect);
}
static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref));
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
}
///////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -143,13 +143,13 @@ static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong full
static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) {
VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr);
auto* fillShader = reinterpret_cast<Shader*>(fillGradientPtr);
path->mutateStagingProperties()->setFillGradient(fillShader);
}
static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) {
VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr);
auto* strokeShader = reinterpret_cast<Shader*>(strokeGradientPtr);
path->mutateStagingProperties()->setStrokeGradient(strokeShader);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2020 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 "BitmapShader.h"
#include "SkImagePriv.h"
namespace android::uirenderer {
BitmapShader::BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX,
const SkTileMode tileModeY, const SkMatrix* matrix)
: Shader(matrix), skShader(image->makeShader(tileModeX, tileModeY)) {}
sk_sp<SkShader> BitmapShader::makeSkShader() {
return skShader;
}
BitmapShader::~BitmapShader() {}
} // namespace android::uirenderer

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkShader.h"
namespace android::uirenderer {
/**
* Shader implementation that renders a Bitmap as either a SkShader or SkImageFilter
*/
class BitmapShader : public Shader {
public:
BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX,
const SkTileMode tileModeY, const SkMatrix* matrix);
~BitmapShader() override;
protected:
sk_sp<SkShader> makeSkShader() override;
private:
sk_sp<SkShader> skShader;
};
} // namespace android::uirenderer

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2020 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 "ComposeShader.h"
#include "SkImageFilters.h"
#include "SkShader.h"
namespace android::uirenderer {
ComposeShader::ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode,
const SkMatrix* matrix)
: Shader(matrix) {
// If both Shaders can be represented as SkShaders then use those, if not
// create an SkImageFilter from both Shaders and create the equivalent SkImageFilter
sk_sp<SkShader> skShaderA = shaderA.asSkShader();
sk_sp<SkShader> skShaderB = shaderB.asSkShader();
if (skShaderA.get() && skShaderB.get()) {
skShader = SkShaders::Blend(blendMode, skShaderA, skShaderB);
skImageFilter = nullptr;
} else {
sk_sp<SkImageFilter> skImageFilterA = shaderA.asSkImageFilter();
sk_sp<SkImageFilter> skImageFilterB = shaderB.asSkImageFilter();
skShader = nullptr;
skImageFilter = SkImageFilters::Xfermode(blendMode, skImageFilterA, skImageFilterB);
}
}
sk_sp<SkShader> ComposeShader::makeSkShader() {
return skShader;
}
sk_sp<SkImageFilter> ComposeShader::makeSkImageFilter() {
return skImageFilter;
}
ComposeShader::~ComposeShader() {}
} // namespace android::uirenderer

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkShader.h"
namespace android::uirenderer {
/**
* Shader implementation that can composite 2 Shaders together with the specified blend mode.
* This implementation can appropriately convert the composed result to either an SkShader or
* SkImageFilter depending on the inputs
*/
class ComposeShader : public Shader {
public:
ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode,
const SkMatrix* matrix);
~ComposeShader() override;
protected:
sk_sp<SkShader> makeSkShader() override;
sk_sp<SkImageFilter> makeSkImageFilter() override;
private:
sk_sp<SkShader> skShader;
sk_sp<SkImageFilter> skImageFilter;
};
} // namespace android::uirenderer

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2020 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 "LinearGradientShader.h"
#include <vector>
#include "SkGradientShader.h"
namespace android::uirenderer {
LinearGradientShader::LinearGradientShader(const SkPoint pts[2],
const std::vector<SkColor4f>& colors,
sk_sp<SkColorSpace> colorspace, const SkScalar pos[],
const SkTileMode tileMode, const uint32_t shaderFlags,
const SkMatrix* matrix)
: Shader(matrix)
, skShader(SkGradientShader::MakeLinear(pts, colors.data(), colorspace, pos, colors.size(),
tileMode, shaderFlags, nullptr)) {}
sk_sp<SkShader> LinearGradientShader::makeSkShader() {
return skShader;
}
LinearGradientShader::~LinearGradientShader() {}
} // namespace android::uirenderer

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkShader.h"
namespace android::uirenderer {
/**
* Shader implementation that renders a color ramp of colors to either as either SkShader or
* SkImageFilter
*/
class LinearGradientShader : public Shader {
public:
LinearGradientShader(const SkPoint pts[2], const std::vector<SkColor4f>& colors,
sk_sp<SkColorSpace> colorspace, const SkScalar pos[],
const SkTileMode tileMode, const uint32_t shaderFlags,
const SkMatrix* matrix);
~LinearGradientShader() override;
protected:
sk_sp<SkShader> makeSkShader() override;
private:
sk_sp<SkShader> skShader;
};
} // namespace android::uirenderer

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 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 "RadialGradientShader.h"
#include <vector>
#include "SkGradientShader.h"
namespace android::uirenderer {
RadialGradientShader::RadialGradientShader(const SkPoint& center, const float radius,
const std::vector<SkColor4f>& colors,
sk_sp<SkColorSpace> colorspace, const SkScalar pos[],
const SkTileMode tileMode, const uint32_t shaderFlags,
const SkMatrix* matrix)
: Shader(matrix)
, skShader(SkGradientShader::MakeRadial(center, radius, colors.data(), colorspace, pos,
colors.size(), tileMode, shaderFlags, nullptr)) {}
sk_sp<SkShader> RadialGradientShader::makeSkShader() {
return skShader;
}
RadialGradientShader::~RadialGradientShader() {}
} // namespace android::uirenderer

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkShader.h"
namespace android::uirenderer {
/**
* Shader implementation that renders a color ramp from the center outward to either as either
* a SkShader or SkImageFilter
*/
class RadialGradientShader : public Shader {
public:
RadialGradientShader(const SkPoint& center, const float radius,
const std::vector<SkColor4f>& colors, sk_sp<SkColorSpace> colorSpace,
const SkScalar pos[], const SkTileMode tileMode, const uint32_t shaderFlags,
const SkMatrix* matrix);
~RadialGradientShader() override;
protected:
sk_sp<SkShader> makeSkShader() override;
private:
sk_sp<SkShader> skShader;
};
} // namespace android::uirenderer

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2020 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 "RuntimeShader.h"
#include "SkShader.h"
#include "include/effects/SkRuntimeEffect.h"
namespace android::uirenderer {
RuntimeShader::RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque,
const SkMatrix* matrix)
: Shader(nullptr)
, // Explicitly passing null as RuntimeShader is created with the
// matrix directly
skShader(effect.makeShader(std::move(data), nullptr, 0, matrix, isOpaque)) {}
sk_sp<SkShader> RuntimeShader::makeSkShader() {
return skShader;
}
RuntimeShader::~RuntimeShader() {}
} // namespace android::uirenderer

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkShader.h"
#include "include/effects/SkRuntimeEffect.h"
namespace android::uirenderer {
/**
* RuntimeShader implementation that can map to either a SkShader or SkImageFilter
*/
class RuntimeShader : public Shader {
public:
RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque,
const SkMatrix* matrix);
~RuntimeShader() override;
protected:
sk_sp<SkShader> makeSkShader() override;
private:
sk_sp<SkShader> skShader;
};
} // namespace android::uirenderer

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkImageFilters.h"
#include "SkPaint.h"
#include "SkRefCnt.h"
namespace android::uirenderer {
Shader::Shader(const SkMatrix* matrix)
: localMatrix(matrix ? *matrix : SkMatrix::I())
, skShader(nullptr)
, skImageFilter(nullptr) {}
Shader::~Shader() {}
sk_sp<SkShader> Shader::asSkShader() {
// If we already have created a shader with these parameters just return the existing
// shader we have already created
if (!this->skShader.get()) {
this->skShader = makeSkShader();
if (this->skShader.get()) {
if (!localMatrix.isIdentity()) {
this->skShader = this->skShader->makeWithLocalMatrix(localMatrix);
}
}
}
return this->skShader;
}
/**
* By default return null as we cannot convert all visual effects to SkShader instances
*/
sk_sp<SkShader> Shader::makeSkShader() {
return nullptr;
}
sk_sp<SkImageFilter> Shader::asSkImageFilter() {
// If we already have created an ImageFilter with these parameters just return the existing
// ImageFilter we have already created
if (!this->skImageFilter.get()) {
// Attempt to create an SkImageFilter from the current Shader implementation
this->skImageFilter = makeSkImageFilter();
if (this->skImageFilter) {
if (!localMatrix.isIdentity()) {
// If we have created an SkImageFilter and we have a transformation, wrap
// the created SkImageFilter to apply the given matrix
this->skImageFilter = SkImageFilters::MatrixTransform(
localMatrix, kMedium_SkFilterQuality, this->skImageFilter);
}
} else {
// Otherwise if no SkImageFilter implementation is provided, create one from
// the result of asSkShader. Note the matrix is already applied to the shader in
// this case so just convert it to an SkImageFilter using SkImageFilters::Paint
SkPaint paint;
paint.setShader(asSkShader());
sk_sp<SkImageFilter> paintFilter = SkImageFilters::Paint(paint);
this->skImageFilter = SkImageFilters::Xfermode(SkBlendMode::kDstIn,
std::move(paintFilter));
}
}
return this->skImageFilter;
}
/**
* By default return null for subclasses to implement. If there is not a direct SkImageFilter
* conversion
*/
sk_sp<SkImageFilter> Shader::makeSkImageFilter() {
return nullptr;
}
} // namespace android::uirenderer

82
libs/hwui/shader/Shader.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 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 "SkImageFilter.h"
#include "SkShader.h"
#include "SkPaint.h"
#include "SkRefCnt.h"
class SkMatrix;
namespace android::uirenderer {
/**
* Shader class that can optionally wrap an SkShader or SkImageFilter depending
* on the implementation
*/
class Shader: public SkRefCnt {
public:
/**
* Creates a Shader instance with an optional transformation matrix
* @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter
*/
Shader(const SkMatrix* matrix);
virtual ~Shader();
/**
* Create an SkShader from the current Shader instance or return a previously
* created instance. This can be null if no SkShader could be created from this
* Shader instance.
*/
sk_sp<SkShader> asSkShader();
/**
* Create an SkImageFilter from the current Shader instance or return a previously
* created instance. Unlike asSkShader, this method cannot return null.
*/
sk_sp<SkImageFilter> asSkImageFilter();
protected:
/**
* Create a new SkShader instance based on this Shader instance
*/
virtual sk_sp<SkShader> makeSkShader();
/**
* Create a new SkImageFilter instance based on this Shader instance. If no SkImageFilter
* can be created then return nullptr
*/
virtual sk_sp<SkImageFilter> makeSkImageFilter();
private:
/**
* Optional matrix transform
*/
const SkMatrix localMatrix;
/**
* Cached SkShader instance to be returned on subsequent queries
*/
sk_sp<SkShader> skShader;
/**
* Cached SkImageFilter instance to be returned on subsequent queries
*/
sk_sp<SkImageFilter> skImageFilter;
};
} // namespace android::uirenderer

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 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 "SweepGradientShader.h"
#include <vector>
#include "SkGradientShader.h"
#include "SkImageFilters.h"
namespace android::uirenderer {
SweepGradientShader::SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors,
const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[],
const uint32_t shaderFlags, const SkMatrix* matrix)
: Shader(matrix)
, skShader(SkGradientShader::MakeSweep(x, y, colors.data(), colorspace, pos, colors.size(),
shaderFlags, nullptr)) {}
sk_sp<SkShader> SweepGradientShader::makeSkShader() {
return skShader;
}
SweepGradientShader::~SweepGradientShader() {}
} // namespace android::uirenderer

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2020 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 "Shader.h"
#include "SkShader.h"
namespace android::uirenderer {
/**
* Shader implementation that renders a color ramp clockwise such that the start and end colors
* are visible at 3 o'clock. This handles converting to either an SkShader or SkImageFilter
*/
class SweepGradientShader : public Shader {
public:
SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors,
const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[],
const uint32_t shaderFlags, const SkMatrix* matrix);
virtual ~SweepGradientShader() override;
protected:
virtual sk_sp<SkShader> makeSkShader() override;
private:
sk_sp<SkShader> skShader;
};
} // namespace android::uirenderer

View File

@@ -18,6 +18,7 @@
#include "hwui/Paint.h"
#include "TestSceneBase.h"
#include "tests/common/BitmapAllocationTestUtils.h"
#include <shader/BitmapShader.h>
#include "utils/Color.h"
class BitmapShaders;
@@ -45,15 +46,24 @@ public:
});
Paint paint;
sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>(
hwuiBitmap->makeImage(),
SkTileMode::kRepeat,
SkTileMode::kRepeat,
nullptr
);
sk_sp<SkImage> image = hwuiBitmap->makeImage();
sk_sp<SkShader> repeatShader =
image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat);
paint.setShader(std::move(repeatShader));
paint.setShader(std::move(bitmapShader));
canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint);
sk_sp<SkShader> mirrorShader =
image->makeShader(SkTileMode::kMirror, SkTileMode::kMirror);
paint.setShader(std::move(mirrorShader));
sk_sp<BitmapShader> mirrorBitmapShader = sk_make_sp<BitmapShader>(
image,
SkTileMode::kMirror,
SkTileMode::kMirror,
nullptr
);
paint.setShader(std::move(mirrorBitmapShader));
canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint);
}

View File

@@ -20,6 +20,10 @@
#include <SkGradientShader.h>
#include <SkImagePriv.h>
#include <ui/PixelFormat.h>
#include <shader/BitmapShader.h>
#include <shader/LinearGradientShader.h>
#include <shader/RadialGradientShader.h>
#include <shader/ComposeShader.h>
class HwBitmapInCompositeShader;
@@ -50,20 +54,41 @@ public:
pixels[4000 + 4 * i + 3] = 255;
}
buffer->unlock();
sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer->toAHardwareBuffer(),
SkColorSpace::MakeSRGB()));
sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>(
Bitmap::createFrom(
buffer->toAHardwareBuffer(),
SkColorSpace::MakeSRGB()
)->makeImage(),
SkTileMode::kClamp,
SkTileMode::kClamp,
nullptr
);
SkPoint center;
center.set(50, 50);
SkColor colors[2];
colors[0] = Color::Black;
colors[1] = Color::White;
sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial(
center, 50, colors, nullptr, 2, SkTileMode::kRepeat);
sk_sp<SkShader> compositeShader(
SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader));
std::vector<SkColor4f> vColors(2);
vColors[0] = SkColors::kBlack;
vColors[1] = SkColors::kWhite;
sk_sp<RadialGradientShader> radialShader = sk_make_sp<RadialGradientShader>(
center,
50,
vColors,
SkColorSpace::MakeSRGB(),
nullptr,
SkTileMode::kRepeat,
0,
nullptr
);
sk_sp<ComposeShader> compositeShader = sk_make_sp<ComposeShader>(
*bitmapShader.get(),
*radialShader.get(),
SkBlendMode::kDstATop,
nullptr
);
Paint paint;
paint.setShader(std::move(compositeShader));

View File

@@ -17,7 +17,8 @@
#include "TestSceneBase.h"
#include "tests/common/TestListViewSceneBase.h"
#include "hwui/Paint.h"
#include <SkGradientShader.h>
#include "SkColor.h"
#include <shader/LinearGradientShader.h>
class ListOfFadedTextAnimation;
@@ -42,15 +43,26 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase {
pts[0].set(0, 0);
pts[1].set(0, 1);
SkColor colors[2] = {Color::Black, Color::Transparent};
sk_sp<SkShader> s(
SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkTileMode::kClamp));
SkMatrix matrix;
matrix.setScale(1, length);
matrix.postRotate(-90);
std::vector<SkColor4f> vColors(2);
vColors[0] = SkColors::kBlack;
vColors[1] = SkColors::kTransparent;
sk_sp<LinearGradientShader> linearGradientShader = sk_make_sp<LinearGradientShader>(
pts,
vColors,
SkColorSpace::MakeSRGB(),
nullptr,
SkTileMode::kClamp,
0,
&matrix
);
Paint fadingPaint;
fadingPaint.setShader(s->makeWithLocalMatrix(matrix));
fadingPaint.setShader(linearGradientShader);
fadingPaint.setBlendMode(SkBlendMode::kDstOut);
canvas.drawRect(0, 0, length, itemHeight, fadingPaint);
canvas.restore();

View File

@@ -17,7 +17,7 @@
#include "TestSceneBase.h"
#include <SkColorMatrixFilter.h>
#include <SkGradientShader.h>
#include <shader/LinearGradientShader.h>
class SimpleColorMatrixAnimation;
@@ -65,9 +65,12 @@ private:
// enough renderer might apply it directly to the paint color)
float pos[] = {0, 1};
SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)};
SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500};
paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2,
SkTileMode::kClamp));
std::vector<SkColor4f> colors(2);
colors[0] = SkColor4f::FromColor(Color::DeepPurple_500);
colors[1] = SkColor4f::FromColor(Color::DeepOrange_500);
paint.setShader(sk_make_sp<LinearGradientShader>(
pts, colors, SkColorSpace::MakeSRGB(), pos, SkTileMode::kClamp,
0, nullptr));
// overdraw several times to emphasize shader cost
for (int i = 0; i < 10; i++) {

View File

@@ -17,6 +17,7 @@
#include "TestSceneBase.h"
#include <SkGradientShader.h>
#include <shader/LinearGradientShader.h>
class SimpleGradientAnimation;
@@ -55,9 +56,24 @@ private:
// overdraw several times to emphasize shader cost
for (int i = 0; i < 10; i++) {
// use i%2 start position to pick 2 color combo with black in it
SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500};
paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2,
SkTileMode::kClamp));
std::vector<SkColor4f> vColors(2);
vColors[0] = ((i % 2) == 0) ?
SkColor4f::FromColor(Color::Transparent) :
SkColor4f::FromColor(Color::Black);
vColors[1] = (((i + 1) % 2) == 0) ?
SkColor4f::FromColor(Color::Black) :
SkColor4f::FromColor(Color::Cyan_500);
sk_sp<LinearGradientShader> gradient = sk_make_sp<LinearGradientShader>(
pts,
vColors,
SkColorSpace::MakeSRGB(),
pos,
SkTileMode::kClamp,
0,
nullptr
);
paint.setShader(gradient);
canvas.drawRect(i, i, width, height, paint);
}
});

View File

@@ -17,9 +17,14 @@
#include <gtest/gtest.h>
#include "PathParser.h"
#include "GraphicsJNI.h"
#include "SkGradientShader.h"
#include "SkShader.h"
#include "VectorDrawable.h"
#include "utils/MathUtils.h"
#include "utils/VectorDrawableUtils.h"
#include <shader/Shader.h>
#include <shader/LinearGradientShader.h>
#include <functional>
@@ -395,7 +400,21 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) {
bitmap.allocN32Pixels(5, 5, false);
SkCanvas canvas(bitmap);
sk_sp<SkShader> shader = SkShaders::Color(SK_ColorBLACK);
SkPoint pts[2];
pts[0].set(0, 0);
pts[1].set(0, 0);
std::vector<SkColor4f> colors(2);
colors[0] = SkColors::kBlack;
colors[1] = SkColors::kBlack;
sk_sp<LinearGradientShader> shader = sk_sp(new LinearGradientShader(pts,
colors,
SkColorSpace::MakeSRGB(),
nullptr,
SkTileMode::kClamp,
SkGradientShader::kInterpolateColorsInPremul_Flag,
nullptr));
// Initial ref count is 1
EXPECT_TRUE(shader->unique());