Make GrTextureOp disable filtering/aa in more cases.
Previously we didn't do this at all for multiple-texture ops.
Improve the test detecting when filtering can be disabled.
Make draw_image_set GM create tiles with pixel of overlap for correct
filtering.
Add draw_image_set_rect_to_rect to exercise filtering/aa disablement
in combination with tiling.
Makes SkGpuDevice filter out inverted src rects (as is done implicitly
in SkBaseDevice by relying on drawImageRect).
Puts GrTextureOp::fFilter in bitfield.
Change-Id: Iee96cb54d665877c7f4aee422a3a7af2b249b1d6
Reviewed-on: https://skia-review.googlesource.com/c/161641
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/gm/drawimageset.cpp b/gm/drawimageset.cpp
index a979516..aa43f18 100644
--- a/gm/drawimageset.cpp
+++ b/gm/drawimageset.cpp
@@ -7,9 +7,78 @@
#include "gm.h"
+#include <algorithm>
#include "SkGradientShader.h"
#include "SkSurface.h"
+// Makes a set of m x n tiled images to be drawn with SkCanvas::experimental_drawImageSetV0().
+static void make_image_tiles(int tileW, int tileH, int m, int n, const SkColor colors[4],
+ SkCanvas::ImageSetEntry set[]) {
+ const int w = tileW * m;
+ const int h = tileH * n;
+ auto surf = SkSurface::MakeRaster(
+ SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
+ surf->getCanvas()->clear(SK_ColorLTGRAY);
+
+ static constexpr SkScalar kStripeW = 10;
+ static constexpr SkScalar kStripeSpacing = 30;
+ SkPaint paint;
+
+ SkPoint pts1[] = {{0.f, 0.f}, {(SkScalar)w, (SkScalar)h}};
+ auto grad = SkGradientShader::MakeLinear(pts1, colors, nullptr, 2, SkShader::kClamp_TileMode);
+ paint.setShader(std::move(grad));
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(kStripeW);
+ SkPoint stripePts[] = {{-w - kStripeW, -kStripeW}, {kStripeW, h + kStripeW}};
+ while (stripePts[0].fX <= w) {
+ surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
+ stripePts[0].fX += kStripeSpacing;
+ stripePts[1].fX += kStripeSpacing;
+ }
+
+ SkPoint pts2[] = {{0.f, (SkScalar)h}, {(SkScalar)w, 0.f}};
+ grad = SkGradientShader::MakeLinear(pts2, colors + 2, nullptr, 2, SkShader::kClamp_TileMode);
+ paint.setShader(std::move(grad));
+ paint.setBlendMode(SkBlendMode::kMultiply);
+ stripePts[0] = {-w - kStripeW, h + kStripeW};
+ stripePts[1] = {kStripeW, -kStripeW};
+ while (stripePts[0].fX <= w) {
+ surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
+ stripePts[0].fX += kStripeSpacing;
+ stripePts[1].fX += kStripeSpacing;
+ }
+ auto fullImage = surf->makeImageSnapshot();
+ for (int y = 0; y < n; ++y) {
+ for (int x = 0; x < m; ++x) {
+ // Images will have 1 pixel of overlap at interior seams for filtering continuity.
+ SkIRect subset = SkIRect::MakeXYWH(x * tileW - 1, y * tileH - 1, tileW + 2, tileH + 2);
+ set[y * m + x].fAAFlags = SkCanvas::kNone_QuadAAFlags;
+ if (x == 0) {
+ subset.fLeft = 0;
+ set[y * m + x].fAAFlags |= SkCanvas::kLeft_QuadAAFlag;
+ }
+ if (x == m - 1) {
+ subset.fRight = w;
+ set[y * m + x].fAAFlags |= SkCanvas::kRight_QuadAAFlag;
+ }
+ if (y == 0) {
+ subset.fTop = 0;
+ set[y * m + x].fAAFlags |= SkCanvas::kTop_QuadAAFlag;
+ }
+ if (y == n - 1) {
+ subset.fBottom = h;
+ set[y * m + x].fAAFlags |= SkCanvas::kBottom_QuadAAFlag;
+ }
+ set[y * m + x].fImage = fullImage->makeSubset(subset);
+ set[y * m + x].fSrcRect =
+ SkRect::MakeXYWH(x == 0 ? 0 : 1, y == 0 ? 0 : 1, tileW, tileH);
+ set[y * m + x].fDstRect = SkRect::MakeXYWH(x * tileW, y * tileH, tileW, tileH);
+ SkASSERT(set[y * m + x].fImage);
+ }
+ }
+}
+
namespace skiagm {
class DrawImageSetGM : public GM {
@@ -17,52 +86,9 @@
SkString onShortName() final { return SkString("draw_image_set"); }
SkISize onISize() override { return SkISize::Make(1000, 725); }
void onOnceBeforeDraw() override {
- static constexpr SkScalar kW = SkIntToScalar(kTileW * kM);
- static constexpr SkScalar kH = SkIntToScalar(kTileH * kN);
-
- auto surf = SkSurface::MakeRaster(
- SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
- surf->getCanvas()->clear(SK_ColorLTGRAY);
-
- static constexpr SkScalar kStripeW = 10;
- static constexpr SkScalar kStripeSpacing = 30;
- SkPaint paint;
-
- static constexpr SkPoint pts1[] = {{0.f, 0.f}, {kW, kH}};
- static constexpr SkColor kColors1[] = {SK_ColorCYAN, SK_ColorBLACK};
- auto grad =
- SkGradientShader::MakeLinear(pts1, kColors1, nullptr, 2, SkShader::kClamp_TileMode);
- paint.setShader(std::move(grad));
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(kStripeW);
- SkPoint stripePts[] = {{-kW - kStripeW, -kStripeW}, {kStripeW, kH + kStripeW}};
- while (stripePts[0].fX <= kW) {
- surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
- stripePts[0].fX += kStripeSpacing;
- stripePts[1].fX += kStripeSpacing;
- }
-
- static constexpr SkPoint pts2[] = {{0.f, kH}, {kW, 0.f}};
- static constexpr SkColor kColors2[] = {SK_ColorMAGENTA, SK_ColorBLACK};
- grad = SkGradientShader::MakeLinear(pts2, kColors2, nullptr, 2, SkShader::kClamp_TileMode);
- paint.setShader(std::move(grad));
- paint.setBlendMode(SkBlendMode::kMultiply);
- stripePts[0] = {-kW - kStripeW, kH + kStripeW};
- stripePts[1] = {kStripeW, -kStripeW};
- while (stripePts[0].fX <= kW) {
- surf->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, 2, stripePts, paint);
- stripePts[0].fX += kStripeSpacing;
- stripePts[1].fX += kStripeSpacing;
- }
-
- auto img = surf->makeImageSnapshot();
- for (int y = 0; y < kN; ++y) {
- for (int x = 0; x < kM; ++x) {
- fImage[x][y] =
- img->makeSubset(SkIRect::MakeXYWH(x * kTileW, y * kTileH, kTileW, kTileH));
- }
- }
+ static constexpr SkColor kColors[] = {SK_ColorCYAN, SK_ColorBLACK,
+ SK_ColorMAGENTA, SK_ColorBLACK};
+ make_image_tiles(kTileW, kTileH, kM, kN, kColors, fSet);
}
void onDraw(SkCanvas* canvas) override {
@@ -92,27 +118,6 @@
dst[2] = {1.f / 3.f * kM * kTileW, 1 / 2.f * kN * kTileH - 0.1f * kTileH};
SkAssertResult(matrices[3].setPolyToPoly(src, dst, 4));
matrices[3].postTranslate(100.f, d);
- SkCanvas::ImageSetEntry set[kM * kN];
- for (int x = 0; x < kM; ++x) {
- for (int y = 0; y < kN; ++y) {
- set[y * kM + x].fAAFlags = SkCanvas::kNone_QuadAAFlags;
- if (x == 0) {
- set[y * kM + x].fAAFlags |= SkCanvas::kLeft_QuadAAFlag;
- }
- if (x == kM - 1) {
- set[y * kM + x].fAAFlags |= SkCanvas::kRight_QuadAAFlag;
- }
- if (y == 0) {
- set[y * kM + x].fAAFlags |= SkCanvas::kTop_QuadAAFlag;
- }
- if (y == kN - 1) {
- set[y * kM + x].fAAFlags |= SkCanvas::kBottom_QuadAAFlag;
- }
- set[y * kM + x].fSrcRect = SkRect::MakeWH(kTileW, kTileH);
- set[y * kM + x].fDstRect = SkRect::MakeXYWH(x * kTileW, y * kTileH, kTileW, kTileH);
- set[y * kM + x].fImage = fImage[x][y];
- }
- }
for (auto fm : {kNone_SkFilterQuality, kLow_SkFilterQuality}) {
for (size_t m = 0; m < SK_ARRAY_COUNT(matrices); ++m) {
// Draw grid of red lines at interior tile boundaries.
@@ -138,19 +143,19 @@
}
canvas->save();
canvas->concat(matrices[m]);
- canvas->experimental_DrawImageSetV0(set, kM * kN, 1.f, fm, SkBlendMode::kSrcOver);
+ canvas->experimental_DrawImageSetV0(fSet, kM * kN, 1.f, fm, SkBlendMode::kSrcOver);
canvas->restore();
}
- // A more exotic case with an unusual blend mode, all aa flags set, and alpha, and
+ // A more exotic case with an unusual blend mode, all aa flags set, and alpha,
// subsets the image
SkCanvas::ImageSetEntry entry;
entry.fSrcRect = SkRect::MakeWH(kTileW, kTileH).makeInset(kTileW / 4.f, kTileH / 4.f);
entry.fDstRect = SkRect::MakeWH(2 * kTileW, 2 * kTileH).makeOffset(d / 4, 2 * d);
- entry.fImage = fImage[0][0];
+ entry.fImage = fSet[0].fImage;
entry.fAAFlags = SkCanvas::kAll_QuadAAFlags;
canvas->save();
canvas->rotate(3.f);
- canvas->experimental_DrawImageSetV0(&entry, 1, 0.7f, fm, SkBlendMode::kLuminosity);
+ canvas->experimental_DrawImageSetV0(&entry, 1, 0.7f, fm, SkBlendMode::kExclusion);
canvas->restore();
canvas->translate(2 * d, 0);
}
@@ -159,9 +164,89 @@
static constexpr int kN = 3;
static constexpr SkScalar kTileW = 30;
static constexpr SkScalar kTileH = 60;
- sk_sp<SkImage> fImage[kM][kN];
+ SkCanvas::ImageSetEntry fSet[kM * kN];
+};
+
+// This GM exercises rect-stays-rect type matrices to test that filtering and antialiasing are not
+// incorrectly disabled.
+class DrawImageSetRectToRectGM : public GM {
+private:
+ SkString onShortName() final { return SkString("draw_image_set_rect_to_rect"); }
+ SkISize onISize() override { return SkISize::Make(1250, 850); }
+ void onOnceBeforeDraw() override {
+ static constexpr SkColor kColors[] = {SK_ColorBLUE, SK_ColorWHITE,
+ SK_ColorRED, SK_ColorWHITE};
+ make_image_tiles(kTileW, kTileH, kM, kN, kColors, fSet);
+ }
+
+ void onDraw(SkCanvas* canvas) override {
+ static constexpr SkScalar kW = kM * kTileW;
+ static constexpr SkScalar kH = kN * kTileH;
+ SkMatrix matrices[5];
+ // Identity
+ matrices[0].reset();
+ // 90 degree rotation
+ matrices[1].setRotate(90, kW / 2.f, kH / 2.f);
+ // Scaling
+ matrices[2].setScale(2.f, 0.5f);
+ // Mirror in x and y
+ matrices[3].setScale(-1.f, -1.f);
+ matrices[3].postTranslate(kW, kH);
+ // Mirror in y, rotate, and scale.
+ matrices[4].setScale(1.f, -1.f);
+ matrices[4].postTranslate(0, kH);
+ matrices[4].postRotate(90, kW / 2.f, kH / 2.f);
+ matrices[4].postScale(2.f, 0.5f);
+
+ static constexpr SkScalar kTranslate = SkTMax(kW, kH) * 2.f + 10.f;
+ canvas->translate(5.f, 5.f);
+ canvas->save();
+ for (SkScalar frac : {0.f, 0.5f}) {
+ canvas->save();
+ canvas->translate(frac, frac);
+ for (size_t m = 0; m < SK_ARRAY_COUNT(matrices); ++m) {
+ canvas->save();
+ canvas->concat(matrices[m]);
+ canvas->experimental_DrawImageSetV0(fSet, kM * kN, 1.f, kLow_SkFilterQuality,
+ SkBlendMode::kSrcOver);
+ canvas->restore();
+ canvas->translate(kTranslate, 0);
+ }
+ canvas->restore();
+ canvas->restore();
+ canvas->translate(0, kTranslate);
+ canvas->save();
+ }
+ for (SkVector scale : {SkVector{2.f, 0.5f}, SkVector{0.5, 2.f}}) {
+ SkCanvas::ImageSetEntry scaledSet[kM * kN];
+ std::copy_n(fSet, kM * kN, scaledSet);
+ for (int i = 0; i < kM * kN; ++i) {
+ scaledSet[i].fDstRect.fLeft *= scale.fX;
+ scaledSet[i].fDstRect.fTop *= scale.fY;
+ scaledSet[i].fDstRect.fRight *= scale.fX;
+ scaledSet[i].fDstRect.fBottom *= scale.fY;
+ }
+ for (size_t m = 0; m < SK_ARRAY_COUNT(matrices); ++m) {
+ canvas->save();
+ canvas->concat(matrices[m]);
+ canvas->experimental_DrawImageSetV0(scaledSet, kM * kN, 1.f, kLow_SkFilterQuality,
+ SkBlendMode::kSrcOver);
+ canvas->restore();
+ canvas->translate(kTranslate, 0);
+ }
+ canvas->restore();
+ canvas->translate(0, kTranslate);
+ canvas->save();
+ }
+ }
+ static constexpr int kM = 2;
+ static constexpr int kN = 2;
+ static constexpr int kTileW = 40;
+ static constexpr int kTileH = 50;
+ SkCanvas::ImageSetEntry fSet[kM * kN];
};
DEF_GM(return new DrawImageSetGM();)
+DEF_GM(return new DrawImageSetRectToRectGM();)
} // namespace skiagm
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index c8132a0..61e8fb5 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -1847,6 +1847,7 @@
* drawing images independently, though may in the future. The antialiasing flags are intended
* to allow control over each edge's AA status, to allow perfect seaming for tile sets. The
* current implementation only antialiases if all edges are flagged, however.
+ * Results are undefined if an image's src rect is not within the image's bounds.
*/
void experimental_DrawImageSetV0(const ImageSetEntry imageSet[], int cnt, float alpha,
SkFilterQuality quality, SkBlendMode mode);
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
index cbe8c06..2f9ac8b 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
@@ -365,6 +365,10 @@
"serialize-8888",
"gm",
"_",
+ "draw_image_set_rect_to_rect",
+ "serialize-8888",
+ "gm",
+ "_",
"analytic_antialias_convex",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
index c6f6efb..b3248d9 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
@@ -441,6 +441,10 @@
"serialize-8888",
"gm",
"_",
+ "draw_image_set_rect_to_rect",
+ "serialize-8888",
+ "gm",
+ "_",
"analytic_antialias_convex",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
index 26e6bd5..9d089e5 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
@@ -359,6 +359,10 @@
"serialize-8888",
"gm",
"_",
+ "draw_image_set_rect_to_rect",
+ "serialize-8888",
+ "gm",
+ "_",
"analytic_antialias_convex",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
index aa28bf4..d4098c7 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
@@ -360,6 +360,10 @@
"serialize-8888",
"gm",
"_",
+ "draw_image_set_rect_to_rect",
+ "serialize-8888",
+ "gm",
+ "_",
"analytic_antialias_convex",
"serialize-8888",
"gm",
diff --git a/infra/bots/recipes/test.py b/infra/bots/recipes/test.py
index e3ccd62..31abb62 100644
--- a/infra/bots/recipes/test.py
+++ b/infra/bots/recipes/test.py
@@ -469,6 +469,7 @@
bad_serialize_gms.append('all_bitmap_configs')
bad_serialize_gms.append('makecolorspace')
bad_serialize_gms.append('readpixels')
+ bad_serialize_gms.append('draw_image_set_rect_to_rect')
# This GM forces a path to be convex. That property doesn't survive
# serialization.
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 5e8cfca..9df0734 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1473,6 +1473,14 @@
}
};
for (int i = 0; i < count; ++i) {
+ // The default SkBaseDevice implementation is based on drawImageRect which does not allow
+ // non-sorted src rects. TODO: Decide this is OK or make sure we handle it.
+ if (!set[i].fSrcRect.isSorted()) {
+ draw();
+ base = i + 1;
+ n = 0;
+ continue;
+ }
textures[i].fProxy =
as_IB(set[i].fImage.get())
->asTextureProxyRef(fContext.get(), GrSamplerState::ClampBilerp(), nullptr,
@@ -1486,12 +1494,13 @@
draw();
base = i + 1;
n = 0;
- } else if (n > 0 &&
- (textures[i].fProxy->textureType() != textures[base].fProxy->textureType() ||
- textures[i].fProxy->config() != textures[base].fProxy->config() ||
- set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
- !SkColorSpace::Equals(set[i].fImage->colorSpace(),
- set[base].fImage->colorSpace()))) {
+ continue;
+ }
+ if (n > 0 &&
+ (textures[i].fProxy->textureType() != textures[base].fProxy->textureType() ||
+ textures[i].fProxy->config() != textures[base].fProxy->config() ||
+ set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
+ !SkColorSpace::Equals(set[i].fImage->colorSpace(), set[base].fImage->colorSpace()))) {
draw();
base = i;
n = 1;
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 857b809..94e5ad5 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -571,6 +571,29 @@
DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
}
+static bool aa_has_effect_for_rect_stays_rect(const GrPerspQuad& quad) {
+ SkASSERT((quad.w4f() == Sk4f(1)).allTrue());
+ float ql = quad.x(0);
+ float qt = quad.y(0);
+ float qr = quad.x(3);
+ float qb = quad.y(3);
+ return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
+}
+
+static bool filter_has_effect_for_rect_stays_rect(const GrPerspQuad& quad, const SkRect& srcRect) {
+ SkASSERT((quad.w4f() == Sk4f(1)).allTrue());
+ float ql = quad.x(0);
+ float qt = quad.y(0);
+ float qr = quad.x(3);
+ float qb = quad.y(3);
+ // Disable filtering when there is no scaling of the src rect and the src rect and dst rect
+ // align fractionally. If we allow inverted src rects this logic needs to consider that.
+ SkASSERT(srcRect.isSorted());
+ return (qr - ql) != srcRect.width() || (qb - qt) != srcRect.height() ||
+ SkScalarFraction(ql) != SkScalarFraction(srcRect.fLeft) ||
+ SkScalarFraction(qt) != SkScalarFraction(srcRect.fTop);
+}
+
/**
* Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
* the texture by color. The blend with the destination is always src-over. The edges are non-AA.
@@ -681,7 +704,7 @@
: INHERITED(ClassID())
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fPaintColorSpaceXform(std::move(paintColorSpaceXform))
- , fFilter(filter)
+ , fFilter(static_cast<unsigned>(filter))
, fAAType(static_cast<unsigned>(aaType))
, fFinalized(0) {
switch (aaType) {
@@ -702,41 +725,31 @@
}
fPerspective = static_cast<unsigned>(viewMatrix.hasPerspective());
auto quad = GrPerspQuad(dstRect, viewMatrix);
- auto bounds = quad.bounds();
// We expect our caller to have already caught this optimization.
SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
constraint == SkCanvas::kFast_SrcRectConstraint);
if (viewMatrix.rectStaysRect()) {
- // Disable filtering when there is no scaling or fractional translation.
- // Disable coverage AA when rect falls on integers in device space.
- if (SkScalarIsInt(bounds.fLeft) && SkScalarIsInt(bounds.fTop) &&
- SkScalarIsInt(bounds.fRight) && SkScalarIsInt(bounds.fBottom)) {
- if (viewMatrix.isScaleTranslate()) {
- if (bounds.width() == srcRect.width() && bounds.height() == srcRect.height()) {
- fFilter = GrSamplerState::Filter::kNearest;
- }
- } else {
- if (bounds.width() == srcRect.height() && bounds.height() == srcRect.width()) {
- fFilter = GrSamplerState::Filter::kNearest;
- }
- }
- if (GrAAType::kCoverage == this->aaType()) {
- fAAType = static_cast<unsigned>(GrAAType::kNone);
- aaFlags = GrQuadAAFlags::kNone;
- }
+ if (this->aaType() == GrAAType::kCoverage && !aa_has_effect_for_rect_stays_rect(quad)) {
+ fAAType = static_cast<unsigned>(GrAAType::kNone);
+ aaFlags = GrQuadAAFlags::kNone;
+ }
+ if (this->filter() != GrSamplerState::Filter::kNearest &&
+ !filter_has_effect_for_rect_stays_rect(quad, srcRect)) {
+ fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
}
}
// We may have had a strict constraint with nearest filter solely due to possible AA bloat.
// If we don't have (or determined we don't need) coverage AA then we can skip using a
// domain.
if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
- fFilter == GrSamplerState::Filter::kNearest &&
+ this->filter() == GrSamplerState::Filter::kNearest &&
this->aaType() != GrAAType::kCoverage) {
constraint = SkCanvas::kFast_SrcRectConstraint;
}
const auto& draw = fQuads.emplace_back(srcRect, quad, aaFlags, constraint, color);
fProxyCnt = 1;
fProxies[0] = {proxy.release(), 1};
+ auto bounds = quad.bounds();
this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
fDomain = static_cast<unsigned>(draw.domain());
fCanSkipAllocatorGather =
@@ -749,14 +762,16 @@
: INHERITED(ClassID())
, fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fPaintColorSpaceXform(std::move(paintColorSpaceXform))
- , fFilter(filter)
+ , fFilter(static_cast<unsigned>(filter))
, fAAType(static_cast<unsigned>(aaType))
, fFinalized(0) {
fQuads.reserve(cnt);
fProxyCnt = SkToUInt(cnt);
SkRect bounds = SkRectPriv::MakeLargestInverted();
- bool aa = false;
+ bool needAA = false;
+ bool mustFilter = false;
fCanSkipAllocatorGather = static_cast<unsigned>(true);
+ bool rectStaysRect = viewMatrix.rectStaysRect();
for (unsigned p = 0; p < fProxyCnt; ++p) {
fProxies[p].fProxy = SkRef(set[p].fProxy.get());
fProxies[p].fQuadCnt = 1;
@@ -773,6 +788,12 @@
aaFlags = GrQuadAAFlags::kNone;
break;
case GrAAType::kCoverage:
+ if (rectStaysRect) {
+ if (aaFlags != GrQuadAAFlags::kNone &&
+ !aa_has_effect_for_rect_stays_rect(quad)) {
+ aaFlags = GrQuadAAFlags::kNone;
+ }
+ }
break;
case GrAAType::kMSAA:
aaFlags = GrQuadAAFlags::kAll;
@@ -781,13 +802,20 @@
SK_ABORT("Should not use mixed sample AA");
break;
}
- aa = aa || (set[p].fAAFlags != GrQuadAAFlags::kNone);
+ needAA = needAA || (set[p].fAAFlags != GrQuadAAFlags::kNone);
+ if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
+ mustFilter = !rectStaysRect ||
+ filter_has_effect_for_rect_stays_rect(quad, set[p].fSrcRect);
+ }
fQuads.emplace_back(set[p].fSrcRect, quad, aaFlags, SkCanvas::kFast_SrcRectConstraint,
color);
}
- if (!aa) {
+ if (!needAA) {
fAAType = static_cast<unsigned>(GrAAType::kNone);
}
+ if (!mustFilter) {
+ fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
+ }
this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
fPerspective = static_cast<unsigned>(viewMatrix.hasPerspective());
fDomain = static_cast<unsigned>(false);
@@ -807,8 +835,8 @@
for (int i = start; i < start + cnt; ++i) {
const auto q = fQuads[i];
- tessellate_quad<Vertex>(q.quad(), q.aaFlags(), q.srcRect(), q.color(), origin, fFilter,
- vertices, iw, ih, q.domain());
+ tessellate_quad<Vertex>(q.quad(), q.aaFlags(), q.srcRect(), q.color(), origin,
+ this->filter(), vertices, iw, ih, q.domain());
vertices += 4;
}
}
@@ -840,7 +868,7 @@
bool coverageAA = GrAAType::kCoverage == this->aaType();
sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
- textureType, config, fFilter, std::move(fTextureColorSpaceXform),
+ textureType, config, this->filter(), std::move(fTextureColorSpaceXform),
std::move(fPaintColorSpaceXform), coverageAA, hasPerspective, domain,
*target->caps().shaderCaps());
GrPipeline::InitArgs args;
@@ -996,6 +1024,7 @@
}
GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
+ GrSamplerState::Filter filter() const { return static_cast<GrSamplerState::Filter>(fFilter); }
class Quad {
public:
@@ -1028,14 +1057,14 @@
SkSTArray<1, Quad, true> fQuads;
sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
- GrSamplerState::Filter fFilter;
+ unsigned fFilter : 2;
unsigned fAAType : 2;
unsigned fPerspective : 1;
unsigned fDomain : 1;
// Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
unsigned fFinalized : 1;
unsigned fCanSkipAllocatorGather : 1;
- unsigned fProxyCnt : 32 - 6;
+ unsigned fProxyCnt : 32 - 8;
Proxy fProxies[1];
typedef GrMeshDrawOp INHERITED;