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>
diff --git a/gm/complexclip.cpp b/gm/complexclip.cpp
index 7974136..e6bef93 100644
--- a/gm/complexclip.cpp
+++ b/gm/complexclip.cpp
@@ -290,3 +290,156 @@
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);
+ }
+}