Add filter param to picture-shader
Bug: skia:7650
Change-Id: I5eb6d73cf92c22b3846d4f4b81c7a8c06e2889a3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/354659
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/gm/pictureshader.cpp b/gm/pictureshader.cpp
index c5a067e..6f22043 100644
--- a/gm/pictureshader.cpp
+++ b/gm/pictureshader.cpp
@@ -225,3 +225,65 @@
p.setShader(picture->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat));
canvas->drawPaint(p);
}
+
+/*
+ Test picture-shader's filtering (after the tile is created.
+ The GM draws a 2x2 grid of tiled images (circle, square, X)
+
+ Column 0 should be hard-edged
+ Column 1 should be filtered
+
+ Row 0 deduces this from the paint (legacy behavior)
+ Row 1 takes this as an explicit parameter (SkFilterMode)
+ */
+DEF_SIMPLE_GM(picture_shader_filter, canvas, 230, 230) {
+ auto pic = [&] {
+ SkRect r = SkRect::MakeWH(100, 100);
+ SkPictureRecorder recorder;
+ SkCanvas* c = recorder.beginRecording(r);
+ SkPaint paint;
+ paint.setStroke(true);
+ c->drawRect({5, 5, 95, 95}, paint);
+ c->drawCircle(50, 50, 30, paint);
+ c->drawLine(5, 1, 95,95, paint);
+ c->drawLine(5,95, 95, 1, paint);
+ return recorder.finishRecordingAsPicture();
+ }();
+
+ struct {
+ SkPoint fLoc;
+ SkFilterMode fFilter;
+ bool fInheritFromPaint;
+
+ void setup(SkPaint* paint, sk_sp<SkPicture> pic) const {
+ SkTileMode tm = SkTileMode::kRepeat;
+ sk_sp<SkShader> sh;
+ if (fInheritFromPaint) {
+ sh = pic->makeShader(tm, tm, nullptr, nullptr);
+ paint->setFilterQuality(fFilter == SkFilterMode::kNearest ? kNone_SkFilterQuality
+ : kLow_SkFilterQuality);
+ } else {
+ sh = pic->makeShader(tm, tm, fFilter, nullptr, nullptr);
+ // the draw should ignore paint's filterquality,
+ // but we'll set it to something wacky just to be test that
+ paint->setFilterQuality(kHigh_SkFilterQuality);
+ }
+ paint->setShader(sh);
+ }
+ } recs[] = {
+ { {0, 0}, SkFilterMode::kNearest, true },
+ { {1, 0}, SkFilterMode::kLinear, true },
+ { {0, 1}, SkFilterMode::kNearest, false },
+ { {1, 1}, SkFilterMode::kLinear, false },
+ };
+
+ canvas->translate(10, 10);
+ canvas->scale(1.0f/3, 1.0f/3);
+ for (const auto& r : recs) {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(r.fLoc.fX * 330, r.fLoc.fY * 330);
+ SkPaint paint;
+ r.setup(&paint, pic);
+ canvas->drawRect({0, 0, 300, 300}, paint);
+ }
+}
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 919840c..ebac1c9 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -10,6 +10,7 @@
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
+#include "include/core/SkSamplingOptions.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypes.h"
@@ -218,6 +219,7 @@
*
* @param tmx The tiling mode to use when sampling in the x-direction.
* @param tmy The tiling mode to use when sampling in the y-direction.
+ * @param mode How to filter the tiles
* @param localMatrix Optional matrix used when sampling
* @param tile The tile rectangle in picture coordinates: this represents the subset
* (or superset) of the picture used when building a tile. It is not
@@ -226,6 +228,10 @@
* bounds.
* @return Returns a new shader object. Note: this function never returns null.
*/
+ sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode mode,
+ const SkMatrix* localMatrix, const SkRect* tileRect) const;
+
+ // DEPRECATED
sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy,
const SkMatrix* localMatrix, const SkRect* tileRect) const;
sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy,
diff --git a/src/core/SkPicturePriv.h b/src/core/SkPicturePriv.h
index 9a40143..101f4c9 100644
--- a/src/core/SkPicturePriv.h
+++ b/src/core/SkPicturePriv.h
@@ -93,10 +93,11 @@
kCubicResamplerImageShader_Version = 79,
kSamplingInImageShader_Version = 80,
kSamplingInDrawImage_Version = 81,
+ kPictureShaderFilterParam_Version = 82,
// Only SKPs within the min/current picture version range (inclusive) can be read.
kMin_Version = kEdgeAAQuadColor4f_Version,
- kCurrent_Version = kSamplingInDrawImage_Version
+ kCurrent_Version = kPictureShaderFilterParam_Version
};
static_assert(SkPicturePriv::kMin_Version <= SkPicturePriv::kCubicResamplerImageShader_Version,
diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp
index 58a2ac1..3005e93 100644
--- a/src/shaders/SkPictureShader.cpp
+++ b/src/shaders/SkPictureShader.cpp
@@ -32,12 +32,22 @@
#include "src/gpu/SkGr.h"
#endif
+sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
+ const SkMatrix* localMatrix, const SkRect* tile) const {
+ if (localMatrix && !localMatrix->invert(nullptr)) {
+ return nullptr;
+ }
+ return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, (SkPictureShader::FilterEnum)filter,
+ localMatrix, tile);
+}
+
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, const SkMatrix* localMatrix,
const SkRect* tile) const {
if (localMatrix && !localMatrix->invert(nullptr)) {
return nullptr;
}
- return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, localMatrix, tile);
+ return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, SkPictureShader::kInheritFromPaint,
+ localMatrix, tile);
}
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy,
@@ -123,12 +133,13 @@
} // namespace
SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
- const SkMatrix* localMatrix, const SkRect* tile)
+ FilterEnum filter, const SkMatrix* localMatrix, const SkRect* tile)
: INHERITED(localMatrix)
, fPicture(std::move(picture))
, fTile(tile ? *tile : fPicture->cullRect())
, fTmx(tmx)
, fTmy(tmy)
+ , fFilter(filter)
, fUniqueID(next_id())
, fAddedToCache(false) {}
@@ -139,11 +150,11 @@
}
sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
- const SkMatrix* localMatrix, const SkRect* tile) {
+ FilterEnum filter, const SkMatrix* lm, const SkRect* tile) {
if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
return SkShaders::Empty();
}
- return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
+ return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, filter, lm, tile));
}
sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
@@ -151,16 +162,21 @@
buffer.readMatrix(&lm);
auto tmx = buffer.read32LE(SkTileMode::kLastTileMode);
auto tmy = buffer.read32LE(SkTileMode::kLastTileMode);
- SkRect tile;
- buffer.readRect(&tile);
+ SkRect tile = buffer.readRect();
sk_sp<SkPicture> picture;
-
- bool didSerialize = buffer.readBool();
- if (didSerialize) {
+ FilterEnum filter;
+ if (buffer.isVersionLT(SkPicturePriv::kPictureShaderFilterParam_Version)) {
+ filter = kInheritFromPaint;
+ bool didSerialize = buffer.readBool();
+ if (didSerialize) {
+ picture = SkPicturePriv::MakeFromBuffer(buffer);
+ }
+ } else {
+ filter = buffer.read32LE(SkPictureShader::kLastFilterEnum);
picture = SkPicturePriv::MakeFromBuffer(buffer);
}
- return SkPictureShader::Make(picture, tmx, tmy, &lm, &tile);
+ return SkPictureShader::Make(picture, tmx, tmy, filter, &lm, &tile);
}
void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
@@ -168,8 +184,7 @@
buffer.write32((unsigned)fTmx);
buffer.write32((unsigned)fTmy);
buffer.writeRect(fTile);
-
- buffer.writeBool(true);
+ buffer.write32((unsigned)fFilter);
SkPicturePriv::Flatten(fPicture, buffer);
}
@@ -179,6 +194,7 @@
SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
SkColorType dstColorType,
SkColorSpace* dstColorSpace,
+ SkFilterQuality paintFQ,
const int maxTextureSize) const {
SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
@@ -256,12 +272,13 @@
#ifdef SK_SUPPORT_LEGACY_INHERITED_PICTURE_SHADER_FILTER
tileShader = SkImage_makeShaderImplicitFilterQuality(tileImage.get(), fTmx, fTmy, nullptr);
#else
-
-#ifdef SK_SUPPORT_NEAREST_PICTURESHADER_POSTFILTER
- SkFilterMode filter = SkFilterMode::kNearest;
-#else
- SkFilterMode filter = SkFilterMode::kLinear;
-#endif
+ SkFilterMode filter;
+ if (fFilter == kInheritFromPaint) {
+ filter = (paintFQ == kNone_SkFilterQuality) ? SkFilterMode::kNearest
+ : SkFilterMode::kLinear;
+ } else {
+ filter = (SkFilterMode)fFilter;
+ }
tileShader = tileImage->makeShader(fTmx, fTmy, SkSamplingOptions(filter), nullptr);
#endif
@@ -282,7 +299,8 @@
// Keep bitmapShader alive by using alloc instead of stack memory
auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
bitmapShader = this->refBitmapShader(rec.fMatrixProvider.localToDevice(), &lm,
- rec.fDstColorType, rec.fDstCS);
+ rec.fDstColorType, rec.fDstCS,
+ rec.fPaint.getFilterQuality());
if (!bitmapShader) {
return false;
@@ -304,7 +322,7 @@
// Keep bitmapShader alive by using alloc instead of stack memory
auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
bitmapShader = this->refBitmapShader(matrices.localToDevice(), &lm,
- dst.colorType(), dst.colorSpace());
+ dst.colorType(), dst.colorSpace(), quality);
if (!bitmapShader) {
return {};
}
@@ -322,7 +340,9 @@
const {
auto lm = this->totalLocalMatrix(rec.fLocalMatrix);
sk_sp<SkShader> bitmapShader = this->refBitmapShader(*rec.fMatrix, &lm, rec.fDstColorType,
- rec.fDstColorSpace);
+ rec.fDstColorSpace,
+ rec.fPaint ? rec.fPaint->getFilterQuality()
+ : kNone_SkFilterQuality);
if (!bitmapShader) {
return nullptr;
}
@@ -363,6 +383,11 @@
#if SK_SUPPORT_GPU
+// Legacy: reconstruct what was in the paint (just care about none/low)
+static SkFilterQuality toFilterQuality(const SkSamplingOptions& sampling) {
+ return sampling == SkSamplingOptions() ? kNone_SkFilterQuality : kLow_SkFilterQuality;
+}
+
std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
const GrFPArgs& args) const {
int maxTextureSize = 0;
@@ -377,7 +402,8 @@
}
sk_sp<SkShader> bitmapShader(
this->refBitmapShader(args.fMatrixProvider.localToDevice(), &lm, dstColorType,
- args.fDstColorInfo->colorSpace(), maxTextureSize));
+ args.fDstColorInfo->colorSpace(), toFilterQuality(args.fSampling),
+ maxTextureSize));
if (!bitmapShader) {
return nullptr;
}
diff --git a/src/shaders/SkPictureShader.h b/src/shaders/SkPictureShader.h
index 1ab69a2..0f86999 100644
--- a/src/shaders/SkPictureShader.h
+++ b/src/shaders/SkPictureShader.h
@@ -26,8 +26,16 @@
public:
~SkPictureShader() override;
- static sk_sp<SkShader> Make(sk_sp<SkPicture>, SkTileMode, SkTileMode, const SkMatrix*,
- const SkRect*);
+ enum FilterEnum {
+ kNearest, // SkFilterMode::kNearest
+ kLinear, // SkFilterMode::kLinear
+ kInheritFromPaint,
+
+ kLastFilterEnum = kInheritFromPaint,
+ };
+
+ static sk_sp<SkShader> Make(sk_sp<SkPicture>, SkTileMode, SkTileMode, FilterEnum,
+ const SkMatrix*, const SkRect*);
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
@@ -49,10 +57,12 @@
private:
SK_FLATTENABLE_HOOKS(SkPictureShader)
- SkPictureShader(sk_sp<SkPicture>, SkTileMode, SkTileMode, const SkMatrix*, const SkRect*);
+ SkPictureShader(sk_sp<SkPicture>, SkTileMode, SkTileMode, FilterEnum,
+ const SkMatrix*, const SkRect*);
sk_sp<SkShader> refBitmapShader(const SkMatrix&, SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
SkColorType dstColorType, SkColorSpace* dstColorSpace,
+ SkFilterQuality fromPaint,
const int maxTextureSize = 0) const;
class PictureShaderContext : public Context {
@@ -74,6 +84,7 @@
sk_sp<SkPicture> fPicture;
SkRect fTile;
SkTileMode fTmx, fTmy;
+ FilterEnum fFilter;
const uint32_t fUniqueID;
mutable std::atomic<bool> fAddedToCache;