Add clipShader with perspective GM

Bug: skia:10206
Change-Id: Iad24cb1134e8f501bce6434ea8511b21039abea2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/293565
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Auto-Submit: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2020-06-03 10:22:49 -04:00 committed by Skia Commit-Bot
parent 89dd4e774d
commit 88b3b15eec

View File

@ -290,3 +290,156 @@ DEF_SIMPLE_GM(clip_shader_nested, canvas, 256, 256) {
canvas->drawRect(SkRect::MakeWH(w, h), p);
canvas->restore();
}
namespace {
// Where is canvas->concat(persp) called relative to the clipShader calls.
enum ConcatPerspective {
kConcatBeforeClips,
kConcatAfterClips,
kConcatBetweenClips
};
// Order in which clipShader(image) and clipShader(gradient) are specified; only meaningful
// when CanvasPerspective is kConcatBetweenClips.
enum ClipOrder {
kClipImageFirst,
kClipGradientFirst,
kDoesntMatter = kClipImageFirst
};
// Which shaders have perspective applied as a local matrix.
enum LocalMatrix {
kNoLocalMat,
kImageWithLocalMat,
kGradientWithLocalMat,
kBothWithLocalMat
};
struct Config {
ConcatPerspective fConcat;
ClipOrder fOrder;
LocalMatrix fLM;
};
static void draw_banner(SkCanvas* canvas, Config config) {
SkString banner;
banner.append("Persp: ");
if (config.fConcat == kConcatBeforeClips || config.fLM == kBothWithLocalMat) {
banner.append("Both Clips");
} else {
SkASSERT((config.fConcat == kConcatBetweenClips && config.fLM == kNoLocalMat) ||
(config.fConcat == kConcatAfterClips && (config.fLM == kImageWithLocalMat ||
config.fLM == kGradientWithLocalMat)));
if ((config.fConcat == kConcatBetweenClips && config.fOrder == kClipImageFirst) ||
config.fLM == kGradientWithLocalMat) {
banner.append("Gradient");
} else {
SkASSERT(config.fOrder == kClipGradientFirst || config.fLM == kImageWithLocalMat);
banner.append("Image");
}
}
if (config.fLM != kNoLocalMat) {
banner.append(" (w/ LM, should equal top row)");
}
static const SkFont kFont(ToolUtils::create_portable_typeface(), 12);
canvas->drawString(banner.c_str(), 20.f, -30.f, kFont, SkPaint());
};
} // anonymous
DEF_SIMPLE_GM(clip_shader_persp, canvas, 1370, 1030) {
// Each draw has a clipShader(image-shader), a clipShader(gradient-shader), a concat(persp-mat),
// and each shader may or may not be wrapped with a perspective local matrix.
// Pairs of configs that should match in appearance where first config doesn't use a local
// matrix (top row of GM) and the second does (bottom row of GM).
Config matches[][2] = {
// Everything has perspective
{{kConcatBeforeClips, kDoesntMatter, kNoLocalMat},
{kConcatAfterClips, kDoesntMatter, kBothWithLocalMat}},
// Image shader has perspective
{{kConcatBetweenClips, kClipGradientFirst, kNoLocalMat},
{kConcatAfterClips, kDoesntMatter, kImageWithLocalMat}},
// Gradient shader has perspective
{{kConcatBetweenClips, kClipImageFirst, kNoLocalMat},
{kConcatAfterClips, kDoesntMatter, kGradientWithLocalMat}}
};
// The image that is drawn
auto img = GetResourceAsImage("images/yellow_rose.png");
// Scale factor always applied to the image shader so that it tiles
SkMatrix scale = SkMatrix::Scale(1.f / 4.f, 1.f / 4.f);
// The perspective matrix applied wherever needed
SkPoint src[4];
SkRect::Make(img->dimensions()).toQuad(src);
SkPoint dst[4] = {{0, 80.f},
{img->width() + 28.f, -100.f},
{img->width() - 28.f, img->height() + 100.f},
{0.f, img->height() - 80.f}};
SkMatrix persp;
SkAssertResult(persp.setPolyToPoly(src, dst, 4));
SkMatrix perspScale = SkMatrix::Concat(persp, scale);
auto drawConfig = [&](Config config) {
canvas->save();
draw_banner(canvas, config);
// Make clipShaders (possibly with local matrices)
bool gradLM = config.fLM == kGradientWithLocalMat || config.fLM == kBothWithLocalMat;
const SkColor gradColors[] = {SK_ColorBLACK, SkColorSetARGB(128, 128, 128, 128)};
auto gradShader = SkGradientShader::MakeRadial({0.5f * img->width(), 0.5f * img->height()},
0.1f * img->width(), gradColors, nullptr, 2,
SkTileMode::kRepeat, 0,
gradLM ? &persp : nullptr);
bool imageLM = config.fLM == kImageWithLocalMat || config.fLM == kBothWithLocalMat;
auto imgShader = img->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
imageLM ? perspScale : scale);
// Perspective before any clipShader
if (config.fConcat == kConcatBeforeClips) {
canvas->concat(persp);
}
// First clipshader
canvas->clipShader(config.fOrder == kClipImageFirst ? imgShader : gradShader);
// Perspective between clipShader
if (config.fConcat == kConcatBetweenClips) {
canvas->concat(persp);
}
// Second clipShader
canvas->clipShader(config.fOrder == kClipImageFirst ? gradShader : imgShader);
// Perspective after clipShader
if (config.fConcat == kConcatAfterClips) {
canvas->concat(persp);
}
// Actual draw and clip boundary are the same for all configs
canvas->clipRect(SkRect::MakeIWH(img->width(), img->height()));
canvas->clear(SK_ColorBLACK);
canvas->drawImage(img, 0, 0);
canvas->restore();
};
SkIRect grid = persp.mapRect(SkRect::Make(img->dimensions())).roundOut();
grid.fLeft -= 20; // manual adjust to look nicer
canvas->translate(10.f, 10.f);
for (size_t i = 0; i < SK_ARRAY_COUNT(matches); ++i) {
canvas->save();
canvas->translate(-grid.fLeft, -grid.fTop);
drawConfig(matches[i][0]);
canvas->translate(0.f, grid.height());
drawConfig(matches[i][1]);
canvas->restore();
canvas->translate(grid.width(), 0.f);
}
}