Add SkImageFilters::Shader in place of Paint factory

SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.

Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.

SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.

Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2020-10-07 15:27:20 -04:00 committed by Skia Commit-Bot
parent 30378382aa
commit 7d0f853158
11 changed files with 68 additions and 38 deletions

View File

@ -9,6 +9,10 @@ Milestone 88
* <insert new release notes here>
* Add new SkImageFilters::Shader factory and deprecate SkImageFilters::Paint factory. All
supported/valid Paint() filters can be represented more cleanly as a Shader image filter.
https://review.skia.org/323680
* GrContext has been replaced by two separate classes: GrDirectContext which is
the traditional notion of GrContext, and GrRecordingContext which is a context
that is recording an SkDeferredDisplayList and therefore has reduced functionality.

View File

@ -526,7 +526,7 @@ static sk_sp<SkImageFilter> make_fuzz_imageFilter(Fuzz* fuzz, int depth) {
return nullptr;
}
uint8_t imageFilterType;
fuzz->nextRange(&imageFilterType, 0, 23);
fuzz->nextRange(&imageFilterType, 0, 24);
switch (imageFilterType) {
case 0:
return nullptr;
@ -788,6 +788,16 @@ static sk_sp<SkImageFilter> make_fuzz_imageFilter(Fuzz* fuzz, int depth) {
return SkImageFilters::Xfermode(blendMode, std::move(bg), std::move(fg),
useCropRect ? &cropRect : nullptr);
}
case 24: {
sk_sp<SkShader> shader = make_fuzz_shader(fuzz, depth - 1);
bool useCropRect;
fuzz->next(&useCropRect);
SkIRect cropRect;
if (useCropRect) {
fuzz->next(&cropRect);
}
return SkImageFilters::Shader(std::move(shader), useCropRect ? &cropRect : nullptr);
}
default:
SkASSERT(false);
return nullptr;

View File

@ -264,12 +264,10 @@ DEF_SIMPLE_GM(imagefilters_effect_order, canvas, 512, 512) {
testMaskPaint.setMaskFilter(maskFilter);
testMaskPaint.setImageFilter(edgeBlend);
SkPaint alphaPaint;
alphaPaint.setShader(alphaMaskShader);
SkPaint expectedMaskPaint;
expectedMaskPaint.setImageFilter(SkImageFilters::Compose(edgeBlend,
SkImageFilters::Xfermode(SkBlendMode::kSrcIn,
SkImageFilters::Paint(alphaPaint))));
SkImageFilters::Shader(alphaMaskShader))));
canvas->save();
canvas->translate(0, image->height());

View File

@ -135,10 +135,8 @@ protected:
}
canvas->restore();
SkPaint noisePaint;
noisePaint.setShader(SkPerlinNoiseShader::MakeFractalNoise(0.1f, 0.05f, 1, 0));
sk_sp<SkImageFilter> rectFilter(SkImageFilters::Paint(noisePaint));
sk_sp<SkImageFilter> rectFilter(SkImageFilters::Shader(
SkPerlinNoiseShader::MakeFractalNoise(0.1f, 0.05f, 1, 0)));
canvas->translate(SK_ARRAY_COUNT(filters)*(r.width() + margin), 0);
for (int xOffset = 0; xOffset < 80; xOffset += 16) {
bounds.fLeft = SkIntToScalar(xOffset);

View File

@ -83,10 +83,6 @@ protected:
sk_sp<SkImageFilter> gradient(SkImageFilters::Image(fGradientCircle));
sk_sp<SkImageFilter> checkerboard(SkImageFilters::Image(fCheckerboard));
SkPaint noisePaint;
noisePaint.setShader(SkPerlinNoiseShader::MakeFractalNoise(SkDoubleToScalar(0.1),
SkDoubleToScalar(0.05), 1, 0));
SkPoint3 pointLocation = SkPoint3::Make(0, 0, SkIntToScalar(10));
SkPoint3 spotLocation = SkPoint3::Make(SkIntToScalar(-10),
SkIntToScalar(-10),
@ -109,7 +105,8 @@ protected:
SkImageFilters::Erode(1, 1, checkerboard),
SkImageFilters::Offset(SkIntToScalar(32), 0, nullptr),
SkImageFilters::MatrixTransform(resizeMatrix, kNone_SkFilterQuality, nullptr),
SkImageFilters::Paint(noisePaint),
SkImageFilters::Shader(SkPerlinNoiseShader::MakeFractalNoise(
SkDoubleToScalar(0.1), SkDoubleToScalar(0.05), 1, 0)),
SkImageFilters::PointLitDiffuse(pointLocation, white, surfaceScale, kd, nullptr),
SkImageFilters::SpotLitDiffuse(spotLocation, spotTarget, spotExponent,
cutoffAngle, white, surfaceScale, kd, nullptr),

View File

@ -188,7 +188,6 @@ tests_sources = [
"$_tests/PDFTaggedTest.cpp",
"$_tests/PackBitsTest.cpp",
"$_tests/PackedConfigsTextureTest.cpp",
"$_tests/PaintImageFilterTest.cpp",
"$_tests/PaintTest.cpp",
"$_tests/ParametricStageTest.cpp",
"$_tests/ParseColorTest.cpp",
@ -244,6 +243,7 @@ tests_sources = [
"$_tests/ScaleToSidesTest.cpp",
"$_tests/SerialProcsTest.cpp",
"$_tests/SerializationTest.cpp",
"$_tests/ShaderImageFilterTest.cpp",
"$_tests/ShaderOpacityTest.cpp",
"$_tests/ShaderTest.cpp",
"$_tests/ShadowTest.cpp",

View File

@ -251,6 +251,9 @@ public:
* @param paint The paint to fill
* @param cropRect Optional rectangle that will be filled. If null, the source bitmap's bounds
* are filled even though the source bitmap itself is not used.
*
* DEPRECATED: Use Shader() instead, since many features of SkPaint are ignored when filling
* the target output, and paint color/alpha can be emulated with SkShaders::Color().
*/
static sk_sp<SkImageFilter> Paint(const SkPaint& paint, const SkIRect* cropRect = nullptr);
@ -268,6 +271,18 @@ public:
return Picture(std::move(pic), target);
}
/**
* Create a filter that fills the output with the per-pixel evaluation of the SkShader. The
* shader is defined in the image filter's local coordinate system, so will automatically
* be affected by SkCanvas' transform.
*
* Like Image() and Picture(), this is a leaf filter that can be used to introduce inputs to
* a complex filter graph, but should generally be combined with a filter that as at least
* one null input to use the implicit source image.
* @param shader The shader that
*/
static sk_sp<SkImageFilter> Shader(sk_sp<SkShader> shader, const SkIRect* cropRect = nullptr);
/**
* Create a tile image filter.
* @param src Defines the pixels to tile

View File

@ -7,6 +7,8 @@
#include "include/effects/SkImageFilters.h"
#include "include/core/SkPaint.h"
// TODO (michaelludwig) - Right now there is a bit of a weird dependency where the implementations
// of the new, preferred filter factories depends on the per-filter headers in include/effects,
// which have themselves been marked as deprecated. But, once clients are updated to use the
@ -179,6 +181,13 @@ sk_sp<SkImageFilter> SkImageFilters::Picture(sk_sp<SkPicture> pic, const SkRect&
return SkPictureImageFilter::Make(std::move(pic), targetRect);
}
sk_sp<SkImageFilter> SkImageFilters::Shader(sk_sp<SkShader> shader, const SkIRect* cropRect) {
SkImageFilter::CropRect r = make_crop_rect(cropRect);
SkPaint paint;
paint.setShader(std::move(shader));
return SkPaintImageFilter::Make(paint, &r);
}
sk_sp<SkImageFilter> SkImageFilters::Tile(
const SkRect& src, const SkRect& dst, sk_sp<SkImageFilter> input) {
return SkTileImageFilter::Make(src, dst, std::move(input));

View File

@ -681,7 +681,7 @@ DEF_TEST(Canvas_degenerate_dimension, reporter) {
// Need a paint that will sneak us past the quickReject in SkCanvas, so we can test the
// raster code further downstream.
SkPaint paint;
paint.setImageFilter(SkImageFilters::Paint(SkPaint(), nullptr));
paint.setImageFilter(SkImageFilters::Shader(SkShaders::Color(SK_ColorBLACK), nullptr));
REPORTER_ASSERT(reporter, !paint.canComputeFastBounds());
const int big = 100 * 1024; // big enough to definitely trigger tiling

View File

@ -158,19 +158,18 @@ public:
this->addFilter("merge", SkImageFilters::Merge(input, input, cropRect));
{
SkPaint greenColorShaderPaint;
greenColorShaderPaint.setShader(SkShaders::Color(SK_ColorGREEN));
sk_sp<SkShader> greenColorShader = SkShaders::Color(SK_ColorGREEN);
SkIRect leftSideCropRect = SkIRect::MakeXYWH(0, 0, 32, 64);
sk_sp<SkImageFilter> paintFilterLeft(SkImageFilters::Paint(greenColorShaderPaint,
&leftSideCropRect));
sk_sp<SkImageFilter> shaderFilterLeft(SkImageFilters::Shader(greenColorShader,
&leftSideCropRect));
SkIRect rightSideCropRect = SkIRect::MakeXYWH(32, 0, 32, 64);
sk_sp<SkImageFilter> paintFilterRight(SkImageFilters::Paint(greenColorShaderPaint,
&rightSideCropRect));
sk_sp<SkImageFilter> shaderFilterRight(SkImageFilters::Shader(greenColorShader,
&rightSideCropRect));
this->addFilter("merge with disjoint inputs", SkImageFilters::Merge(
std::move(paintFilterLeft), std::move(paintFilterRight), cropRect));
std::move(shaderFilterLeft), std::move(shaderFilterRight), cropRect));
}
this->addFilter("offset", SkImageFilters::Offset(SK_Scalar1, SK_Scalar1, input, cropRect));
@ -210,9 +209,8 @@ public:
kBlurSigma, kBlurSigma, std::move(pictureFilter), cropRect));
}
{
SkPaint paint;
paint.setShader(SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
sk_sp<SkImageFilter> paintFilter(SkImageFilters::Paint(paint));
sk_sp<SkImageFilter> paintFilter(SkImageFilters::Shader(
SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0)));
this->addFilter("paint and blur", SkImageFilters::Blur(
kBlurSigma, kBlurSigma, std::move(paintFilter), cropRect));
@ -1936,10 +1934,9 @@ DEF_TEST(ArithmeticImageFilterBounds, reporter) {
// Test SkDisplacementMapEffect::filterBounds.
DEF_TEST(DisplacementMapBounds, reporter) {
SkPaint greenPaint;
greenPaint.setColor(SK_ColorGREEN);
SkIRect floodBounds(SkIRect::MakeXYWH(20, 30, 10, 10));
sk_sp<SkImageFilter> flood(SkImageFilters::Paint(greenPaint, &floodBounds));
sk_sp<SkImageFilter> flood(SkImageFilters::Shader(SkShaders::Color(SK_ColorGREEN),
&floodBounds));
SkIRect tilingBounds(SkIRect::MakeXYWH(0, 0, 200, 100));
sk_sp<SkImageFilter> tiling(SkImageFilters::Tile(SkRect::Make(floodBounds),
SkRect::Make(tilingBounds),

View File

@ -33,20 +33,21 @@ static void test_unscaled(skiatest::Reporter* reporter) {
SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
SkScalar radius = SkIntToScalar(5);
SkPaint gradientPaint;
gradientPaint.setShader(SkGradientShader::MakeRadial(
center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkTileMode::kClamp));
sk_sp<SkShader> gradient = SkGradientShader::MakeRadial(
center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkTileMode::kClamp);
// Test using the image filter
{
SkPaint paint;
paint.setImageFilter(SkImageFilters::Paint(gradientPaint, &ir));
paint.setImageFilter(SkImageFilters::Shader(gradient, &ir));
canvasFilter.drawRect(SkRect::Make(ir), paint);
}
// Test using the paint directly
{
canvasPaint.drawRect(SkRect::Make(ir), gradientPaint);
SkPaint paint;
paint.setShader(gradient);
canvasPaint.drawRect(SkRect::Make(ir), paint);
}
// Assert that both paths yielded the same result
@ -80,22 +81,23 @@ static void test_scaled(skiatest::Reporter* reporter) {
SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
SkScalar radius = SkIntToScalar(5);
SkPaint gradientPaint;
gradientPaint.setShader(SkGradientShader::MakeRadial(
center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkTileMode::kClamp));
sk_sp<SkShader> gradient = SkGradientShader::MakeRadial(
center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkTileMode::kClamp);
// Test using the image filter
{
SkPaint paint;
paint.setImageFilter(SkImageFilters::Paint(gradientPaint, &ir));
paint.setImageFilter(SkImageFilters::Shader(gradient, &ir));
canvasFilter.scale(SkIntToScalar(2), SkIntToScalar(2));
canvasFilter.drawRect(SkRect::Make(ir), paint);
}
// Test using the paint directly
{
SkPaint paint;
paint.setShader(gradient);
canvasPaint.scale(SkIntToScalar(2), SkIntToScalar(2));
canvasPaint.drawRect(SkRect::Make(ir), gradientPaint);
canvasPaint.drawRect(SkRect::Make(ir), paint);
}
// Assert that both paths yielded the same result