Reland "Add anisotropic option to SkSamplingOptions."

This is a reland of commit 9be2d572d4

Original change's description:
> Add anisotropic option to SkSamplingOptions.
>
> Implement on GPU.
>
> Bug: skia:13036
> Change-Id: I35d760596c4f8faaec27fccf284b70802fcf3f9b
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/524757
> Commit-Queue: Brian Salomon <bsalomon@google.com>
> Reviewed-by: Brian Osman <brianosman@google.com>
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>

Bug: skia:13036
Change-Id: I3e411aae389dc880ce32bba78852705059fb88b0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/535197
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2022-04-29 09:15:19 -04:00 committed by SkCQ
parent 3f8806ce0f
commit e6f23f98de
29 changed files with 341 additions and 86 deletions

View File

@ -2,6 +2,12 @@ Skia Graphics Release Notes
This file includes a list of high level updates for each milestone release.
Milestone 103
-------------
* SkSamplingOptions now includes anisotropic filtering. Implemented on GPU only.
* * *
Milestone 102
-------------
* Add glGetFloatv and glSamplerParameterf to GrGLInterface.

View File

@ -21,13 +21,39 @@ namespace skiagm {
// This GM exercises anisotropic image scaling.
class AnisotropicGM : public GM {
public:
AnisotropicGM() : fSampling(SkFilterMode::kLinear, SkMipmapMode::kLinear) {
enum class Mode { kLinear, kMip, kAniso };
AnisotropicGM(Mode mode) : fMode(mode) {
switch (fMode) {
case Mode::kLinear:
fSampling = SkSamplingOptions(SkFilterMode::kLinear);
break;
case Mode::kMip:
fSampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
break;
case Mode::kAniso:
fSampling = SkSamplingOptions::Aniso(16);
break;
}
this->setBGColor(0xFFCCCCCC);
}
protected:
SkString onShortName() override { return SkString("anisotropic_image_scale_mip"); }
SkString onShortName() override {
SkString name("anisotropic_image_scale_");
switch (fMode) {
case Mode::kLinear:
name += "linear";
break;
case Mode::kMip:
name += "mip";
break;
case Mode::kAniso:
name += "aniso";
break;
}
return name;
}
SkISize onISize() override {
return SkISize::Make(2*kImageSize + 3*kSpacer,
@ -112,11 +138,116 @@ private:
sk_sp<SkImage> fImage;
SkSamplingOptions fSampling;
Mode fMode;
using INHERITED = GM;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new AnisotropicGM;)
DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kLinear);)
DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kMip);)
DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kAniso);)
//////////////////////////////////////////////////////////////////////////////
class AnisoMipsGM : public GM {
public:
AnisoMipsGM() = default;
protected:
SkString onShortName() override { return SkString("anisomips"); }
SkISize onISize() override { return SkISize::Make(520, 260); }
sk_sp<SkImage> updateImage(SkSurface* surf, SkColor color) {
surf->getCanvas()->clear(color);
SkPaint paint;
paint.setColor(~color | 0xFF000000);
surf->getCanvas()->drawRect(SkRect::MakeLTRB(surf->width() *2/5.f,
surf->height()*2/5.f,
surf->width() *3/5.f,
surf->height()*3/5.f),
paint);
return surf->makeImageSnapshot()->withDefaultMipmaps();
}
void onDraw(SkCanvas* canvas) override {
auto ct = canvas->imageInfo().colorType() == kUnknown_SkColorType
? kRGBA_8888_SkColorType
: canvas->imageInfo().colorType();
auto ii = SkImageInfo::Make(kImageSize,
kImageSize,
ct,
kPremul_SkAlphaType,
canvas->imageInfo().refColorSpace());
// In GPU mode we want a surface that is created with mipmaps to ensure that we exercise the
// case where the SkSurface and SkImage share a texture. If the surface texture isn't
// created with MIPs then asking for a mipmapped image will cause a copy to a mipped
// texture.
sk_sp<SkSurface> surface;
if (auto rc = canvas->recordingContext()) {
surface = SkSurface::MakeRenderTarget(rc,
SkBudgeted::kYes,
ii,
1,
kTopLeft_GrSurfaceOrigin,
/*surfaceProps=*/nullptr,
/*shouldCreateWithMips=*/true);
if (!surface) {
// We could be in an abandoned context situation.
return;
}
} else {
surface = canvas->makeSurface(ii);
if (!surface) { // could be a recording canvas.
surface = SkSurface::MakeRaster(ii);
}
}
static constexpr float kScales[] = {1.f, 0.5f, 0.25f, 0.125f};
SkColor kColors[] = {0xFFF0F0F0, SK_ColorBLUE, SK_ColorGREEN, SK_ColorRED};
static const SkSamplingOptions kSampling = SkSamplingOptions::Aniso(16);
for (bool shader : {false, true}) {
int c = 0;
canvas->save();
for (float sy : kScales) {
canvas->save();
for (float sx : kScales) {
canvas->save();
canvas->scale(sx, sy);
auto image = this->updateImage(surface.get(), kColors[c]);
if (shader) {
SkPaint paint;
paint.setShader(image->makeShader(kSampling));
canvas->drawRect(SkRect::Make(image->dimensions()), paint);
} else {
canvas->drawImage(image, 0, 0, kSampling);
}
canvas->restore();
canvas->translate(ii.width() * sx + kPad, 0);
c = (c + 1) % SK_ARRAY_COUNT(kColors);
}
canvas->restore();
canvas->translate(0, ii.width() * sy + kPad);
}
canvas->restore();
for (float sx : kScales) {
canvas->translate(ii.width() * sx + kPad, 0);
}
}
}
private:
inline static constexpr int kImageSize = 128;
inline static constexpr int kPad = 5;
using INHERITED = GM;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new AnisoMipsGM();)
} // namespace skiagm

View File

@ -158,10 +158,12 @@ protected:
canvas->translate(0, SkIntToScalar(kCellSize));
this->drawRow(canvas, SkSamplingOptions(SkCubicResampler::Mitchell()));
canvas->translate(0, SkIntToScalar(kCellSize));
this->drawRow(canvas, SkSamplingOptions::Aniso(16));
canvas->translate(0, SkIntToScalar(kCellSize));
}
private:
inline static constexpr int kCellSize = 50;
inline static constexpr int kNumRows = 4;
inline static constexpr int kNumRows = 5;
inline static constexpr int kNumCols = 6;
bool fDoAA;

View File

@ -66,7 +66,7 @@ protected:
}
SkISize onISize() override {
return SkISize::Make(520, 100);
return SkISize::Make(630, 100);
}
void onDraw(SkCanvas* canvas) override {
@ -77,20 +77,15 @@ protected:
SkSamplingOptions(SkFilterMode::kLinear),
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear),
SkSamplingOptions(SkCubicResampler::Mitchell()),
SkSamplingOptions::Aniso(16),
};
const SkRect srcRect = SkRect::MakeWH(96, 96);
const SkSize deviceSize = SkSize::Make(16, 16);
this->draw(canvas, srcRect, deviceSize, samplings[0], nullptr);
canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
this->draw(canvas, srcRect, deviceSize, samplings[1], nullptr);
canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
this->draw(canvas, srcRect, deviceSize, samplings[2], nullptr);
canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
this->draw(canvas, srcRect, deviceSize, samplings[3], nullptr);
for (const auto& sampling : samplings) {
this->draw(canvas, srcRect, deviceSize, sampling, nullptr);
canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
}
{
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(16, 16));
@ -109,7 +104,6 @@ protected:
sk_sp<SkImageFilter> source(
SkImageFilters::Image(std::move(image), inRect, outRect,
SkSamplingOptions({1/3.0f, 1/3.0f})));
canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
this->draw(canvas, srcRect, deviceSize, samplings[3], std::move(source));
}
}

View File

@ -31,6 +31,7 @@ const SkSamplingOptions gSamplings[] = {
SkSamplingOptions(SkFilterMode::kLinear),
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear),
SkSamplingOptions(SkCubicResampler::Mitchell()),
SkSamplingOptions::Aniso(16),
};
static void makebm(SkBitmap* bm, SkColorType ct, int w, int h) {
@ -81,7 +82,7 @@ protected:
return name;
}
SkISize onISize() override { return SkISize::Make(880, 760); }
SkISize onISize() override { return SkISize::Make(880, 880); }
void onOnceBeforeDraw() override {
int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
@ -102,7 +103,7 @@ protected:
const char* gColorTypeNames[] = { "8888" , "565", "4444" };
const char* gFilterNames[] = { "None", "Low", "Medium", "High" };
const char* gFilterNames[] = { "Nearest", "Linear", "Trilinear", "Mitchell", "Aniso" };
constexpr SkTileMode gModes[] = {
SkTileMode::kClamp, SkTileMode::kRepeat, SkTileMode::kMirror };

View File

@ -1444,8 +1444,8 @@ public:
/** \enum SkCanvas::SrcRectConstraint
SrcRectConstraint controls the behavior at the edge of source SkRect,
provided to drawImageRect() when there is any filtering. If kStrict is set,
then extra code is used to ensure it nevers samples outside of the src-rect.
kStrict_SrcRectConstraint disables the use of mipmaps.
then extra code is used to ensure it never samples outside of the src-rect.
kStrict_SrcRectConstraint disables the use of mipmaps and anisotropic filtering.
*/
enum SrcRectConstraint {
kStrict_SrcRectConstraint, //!< sample only inside bounds; slower

View File

@ -9,6 +9,8 @@
#define SkImageSampling_DEFINED
#include "include/core/SkTypes.h"
#include <algorithm>
#include <new>
enum class SkFilterMode {
@ -52,6 +54,7 @@ struct SkCubicResampler {
};
struct SK_API SkSamplingOptions {
const int maxAniso = 0;
const bool useCubic = false;
const SkCubicResampler cubic = {0, 0};
const SkFilterMode filter = SkFilterMode::kNearest;
@ -66,27 +69,35 @@ struct SK_API SkSamplingOptions {
}
SkSamplingOptions(SkFilterMode fm, SkMipmapMode mm)
: useCubic(false)
, filter(fm)
: filter(fm)
, mipmap(mm) {}
explicit SkSamplingOptions(SkFilterMode fm)
: useCubic(false)
, filter(fm)
: filter(fm)
, mipmap(SkMipmapMode::kNone) {}
explicit SkSamplingOptions(const SkCubicResampler& c)
: useCubic(true)
, cubic(c) {}
static SkSamplingOptions Aniso(int maxAniso) {
return SkSamplingOptions{std::max(maxAniso, 1)};
}
bool operator==(const SkSamplingOptions& other) const {
return useCubic == other.useCubic
return maxAniso == other.maxAniso
&& useCubic == other.useCubic
&& cubic.B == other.cubic.B
&& cubic.C == other.cubic.C
&& filter == other.filter
&& mipmap == other.mipmap;
}
bool operator!=(const SkSamplingOptions& other) const { return !(*this == other); }
bool isAniso() const { return maxAniso != 0; }
private:
SkSamplingOptions(int maxAniso) : maxAniso(maxAniso) {}
};
#endif

View File

@ -307,6 +307,7 @@ func (b *taskBuilder) dmFlags(internalHardwareLabel string) {
skip(ALL, "gm", ALL, "lattice_alpha")
skip(ALL, "gm", ALL, "localmatriximageshader")
skip(ALL, "gm", ALL, "savelayer_f16")
skip(ALL, "gm", ALL, "anisomips")
// TODO: re-enable - currently fail bc surface to image conversion isn't implemented
skip(ALL, "gm", ALL, "xfermodes3")

File diff suppressed because one or more lines are too long

View File

@ -184,6 +184,7 @@ bool SkBitmapProcState::init(const SkMatrix& inv, SkAlpha paintAlpha,
const SkSamplingOptions& sampling) {
SkASSERT(!inv.hasPerspective());
SkASSERT(SkOpts::S32_alpha_D32_filter_DXDY || inv.isScaleTranslate());
SkASSERT(!sampling.isAniso());
SkASSERT(!sampling.useCubic);
SkASSERT(sampling.mipmap != SkMipmapMode::kLinear);

View File

@ -2262,9 +2262,13 @@ void SkCanvas::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
static SkSamplingOptions clean_sampling_for_constraint(
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint) {
if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
sampling.mipmap != SkMipmapMode::kNone) {
return SkSamplingOptions(sampling.filter);
if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
if (sampling.mipmap != SkMipmapMode::kNone) {
return SkSamplingOptions(sampling.filter);
}
if (sampling.isAniso()) {
return SkSamplingOptions(SkFilterMode::kLinear);
}
}
return sampling;
}

View File

@ -100,6 +100,8 @@ public:
// V88: Add blender to ComposeShader and BlendImageFilter
// V89: Deprecated SkClipOps are no longer supported
// V90: Private API for backdrop scale factor in SaveLayerRec
// V91: Added raw image shaders
// V92: Added anisotropic filtering to SkSamplingOptions
enum Version {
kPictureShaderFilterParam_Version = 82,
@ -112,10 +114,11 @@ public:
kNoExpandingClipOps = 89,
kBackdropScaleFactor = 90,
kRawImageShaders = 91,
kAnisotropicFilter = 92,
// Only SKPs within the min/current picture version range (inclusive) can be read.
kMin_Version = kPictureShaderFilterParam_Version,
kCurrent_Version = kRawImageShaders
kCurrent_Version = kAnisotropicFilter
};
};

View File

@ -523,7 +523,7 @@ void SkPictureRecord::onDrawPath(const SkPath& path, const SkPaint& paint) {
void SkPictureRecord::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
const SkSamplingOptions& sampling, const SkPaint* paint) {
// op + paint_index + image_index + x + y
size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar) + SkSamplingPriv::kFlatSize;
size_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar) + SkSamplingPriv::FlatSize(sampling);
size_t initialOffset = this->addDraw(DRAW_IMAGE2, &size);
this->addPaintPtr(paint);
this->addImage(image);
@ -537,7 +537,8 @@ void SkPictureRecord::onDrawImageRect2(const SkImage* image, const SkRect& src,
const SkSamplingOptions& sampling, const SkPaint* paint,
SrcRectConstraint constraint) {
// id + paint_index + image_index + constraint
size_t size = 3 * kUInt32Size + 2 * sizeof(dst) + SkSamplingPriv::kFlatSize + kUInt32Size;
size_t size = 3 * kUInt32Size + 2 * sizeof(dst) + SkSamplingPriv::FlatSize(sampling) +
kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_IMAGE_RECT2, &size);
this->addPaintPtr(paint);
@ -684,7 +685,7 @@ void SkPictureRecord::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[]
const SkPaint* paint) {
// [op + paint-index + atlas-index + flags + count] + [xform] + [tex] + [*colors + mode] + cull
size_t size = 5 * kUInt32Size + count * sizeof(SkRSXform) + count * sizeof(SkRect);
size += SkSamplingPriv::kFlatSize;
size += SkSamplingPriv::FlatSize(sampling);
uint32_t flags = 0;
if (colors) {
flags |= DRAW_ATLAS_HAS_COLORS;
@ -780,7 +781,7 @@ void SkPictureRecord::onDrawEdgeAAImageSet2(const SkCanvas::ImageSetEntry set[],
size_t size = 6 * kUInt32Size + sizeof(SkPoint) * totalDstClipCount +
kMatrixSize * totalMatrixCount +
(4 * kUInt32Size + 2 * sizeof(SkRect) + sizeof(SkScalar)) * count +
SkSamplingPriv::kFlatSize;
SkSamplingPriv::FlatSize(sampling);
size_t initialOffset = this->addDraw(DRAW_EDGEAA_IMAGE_SET2, &size);
this->addInt(count);
this->addPaintPtr(paint);

View File

@ -210,6 +210,12 @@ SkRect SkReadBuffer::readRect() {
}
SkSamplingOptions SkReadBuffer::readSampling() {
if (!this->isVersionLT(SkPicturePriv::kAnisotropicFilter)) {
int maxAniso = this->readInt();
if (maxAniso != 0) {
return SkSamplingOptions::Aniso(maxAniso);
}
}
if (this->readBool()) {
float B = this->readScalar();
float C = this->readScalar();

View File

@ -32,17 +32,31 @@ enum SkMediumAs {
class SkSamplingPriv {
public:
enum {
kFlatSize = 3 * sizeof(uint32_t) // bool32 + [2 floats | 2 ints]
};
static size_t FlatSize(const SkSamplingOptions& options) {
size_t size = sizeof(uint32_t); // maxAniso
if (!options.isAniso()) {
size += 3 * sizeof(uint32_t); // bool32 + [2 floats | 2 ints]
}
return size;
}
// Returns true if the sampling can be ignored when the CTM is identity.
static bool NoChangeWithIdentityMatrix(const SkSamplingOptions& sampling) {
// If B == 0, the cubic resampler should have no effect for identity matrices
// https://entropymine.com/imageworsener/bicubic/
// We assume aniso has no effect with an identity transform.
return !sampling.useCubic || sampling.cubic.B == 0;
}
// Makes a fallback SkSamplingOptions for cases where anisotropic filtering is not allowed.
// anisotropic filtering can access mip levels if present, but we don't add mipmaps to non-
// mipmapped images when the user requests anisotropic. So we shouldn't fall back to a
// sampling that would trigger mip map creation.
static SkSamplingOptions AnisoFallback(bool imageIsMipped) {
auto mm = imageIsMipped ? SkMipmapMode::kLinear : SkMipmapMode::kNone;
return SkSamplingOptions(SkFilterMode::kLinear, mm);
}
static SkSamplingOptions FromFQ(SkLegacyFQ, SkMediumAs = kNearest_SkMediumAs);
};

View File

@ -18,13 +18,16 @@ void SkWriter32::writeMatrix(const SkMatrix& matrix) {
}
void SkWriter32::writeSampling(const SkSamplingOptions& sampling) {
this->writeBool(sampling.useCubic);
if (sampling.useCubic) {
this->writeScalar(sampling.cubic.B);
this->writeScalar(sampling.cubic.C);
} else {
this->write32((unsigned)sampling.filter);
this->write32((unsigned)sampling.mipmap);
this->write32(sampling.maxAniso);
if (!sampling.isAniso()) {
this->writeBool(sampling.useCubic);
if (sampling.useCubic) {
this->writeScalar(sampling.cubic.B);
this->writeScalar(sampling.cubic.C);
} else {
this->write32((unsigned)sampling.filter);
this->write32((unsigned)sampling.mipmap);
}
}
}

View File

@ -67,7 +67,7 @@ public:
sampler.fMaxAniso = SkTPin(maxAniso, 1, kMaxMaxAniso);
sampler.fFilter = Filter::kLinear;
sampler.fMipmapMode = viewIsMipped == GrMipmapped::kYes ? MipmapMode::kLinear
: MipmapMode::kNearest;
: MipmapMode::kNone;
return sampler;
}

View File

@ -58,15 +58,46 @@ GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
bool contains(Span r) const { return fA <= r.fA && fB >= r.fB; }
};
struct Result1D {
ShaderMode fShaderMode;
Span fShaderSubset;
Span fShaderClamp;
Wrap fHWWrap;
ShaderMode fShaderMode = ShaderMode::kNone;
Span fShaderSubset = {};
Span fShaderClamp = {};
Wrap fHWWrap = Wrap::kClamp;
};
auto type = proxy.asTextureProxy()->textureType();
auto type = proxy.asTextureProxy()->textureType();
auto filter = sampler.filter();
auto mm = sampler.mipmapMode();
auto mm = sampler.mipmapMode();
// TODO: Use HW border color when available.
// TODO: Move this back into resolve below when aniso is supported with shader-based subsetting.
bool needShaderBorder = false;
if ((sampler.wrapModeX() == Wrap::kClampToBorder ||
sampler.wrapModeY() == Wrap::kClampToBorder) &&
(!caps.clampToBorderSupport() || border[0] || border[1] || border[2] || border[3])) {
needShaderBorder = true;
}
// TODO: Right now if we use shader based subsetting for any reason we just completely drop
// aniso. Longer term allow shader subsetting, reusing the special repeat mode LOD selection
// logic for mip maps, and simply don't attempt to restrict ansiso's computed samples to the
// subset. That is use "subsetting" but not "clamping"/insetting in terms of the shader gen
// logic.
bool aniso = sampler.isAniso();
SkASSERT(!aniso || caps.anisoSupport());
if (aniso) {
bool anisoSubset = !proxy.backingStoreBoundsRect().contains(subset) &&
(!domain || !subset.contains(*domain));
if (needShaderBorder || anisoSubset) {
MipmapMode newMM = proxy.asTextureProxy()->mipmapped() == GrMipmapped::kYes
? MipmapMode::kLinear
: MipmapMode::kNone;
sampler = GrSamplerState(sampler.wrapModeX(),
sampler.wrapModeY(),
SkFilterMode::kLinear,
newMM);
aniso = false;
}
}
auto resolve = [&](int size, Wrap wrap, Span subset, Span domain, float linearFilterInset) {
Result1D r;
@ -119,19 +150,29 @@ GrTextureEffect::Sampling::Sampling(const GrSurfaceProxy& proxy,
return r;
};
SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
Result1D x, y;
if (!aniso) {
SkISize dim = proxy.isFullyLazy() ? SkISize{-1, -1} : proxy.backingStoreDimensions();
Span subsetX{subset.fLeft, subset.fRight};
auto domainX = domain ? Span{domain->fLeft, domain->fRight}
: Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
auto x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX, linearFilterInset.fX);
Span subsetX{subset.fLeft, subset.fRight};
auto domainX = domain ? Span{domain->fLeft, domain->fRight}
: Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
x = resolve(dim.width(), sampler.wrapModeX(), subsetX, domainX, linearFilterInset.fX);
Span subsetY{subset.fTop, subset.fBottom};
auto domainY = domain ? Span{domain->fTop, domain->fBottom}
: Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
auto y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY, linearFilterInset.fY);
Span subsetY{subset.fTop, subset.fBottom};
auto domainY = domain ? Span{domain->fTop, domain->fBottom}
: Span{SK_FloatNegativeInfinity, SK_FloatInfinity};
y = resolve(dim.height(), sampler.wrapModeY(), subsetY, domainY, linearFilterInset.fY);
} else {
x.fHWWrap = sampler.wrapModeX();
y.fHWWrap = sampler.wrapModeY();
}
fHWSampler = {x.fHWWrap, y.fHWWrap, filter, mm};
fHWSampler = aniso ? GrSamplerState::Aniso(x.fHWWrap,
y.fHWWrap,
sampler.maxAniso(),
proxy.asTextureProxy()->mipmapped())
: GrSamplerState{x.fHWWrap, y.fHWWrap, filter, mm};
fShaderModes[0] = x.fShaderMode;
fShaderModes[1] = y.fShaderMode;
fShaderSubset = {x.fShaderSubset.fA, y.fShaderSubset.fA,

View File

@ -2702,6 +2702,8 @@ void GrGLGpu::bindTexture(int unitIdx, GrSamplerState samplerState, const skgpu:
if (samplerState.mipmapped() == GrMipmapped::kYes) {
if (!this->caps()->mipmapSupport() || texture->mipmapped() == GrMipmapped::kNo) {
// We should have caught this already.
SkASSERT(!samplerState.isAniso());
samplerState = GrSamplerState(samplerState.wrapModeX(),
samplerState.wrapModeY(),
samplerState.filter(),

View File

@ -63,6 +63,7 @@ generated_cc_atom(
"//src/core:SkDraw_hdr",
"//src/core:SkImagePriv_hdr",
"//src/core:SkMaskFilterBase_hdr",
"//src/core:SkSamplingPriv_hdr",
"//src/core:SkSpecialImage_hdr",
"//src/gpu/ganesh:GrBlurUtils_hdr",
"//src/gpu/ganesh:GrCaps_hdr",

View File

@ -13,6 +13,7 @@
#include "src/core/SkDraw.h"
#include "src/core/SkImagePriv.h"
#include "src/core/SkMaskFilterBase.h"
#include "src/core/SkSamplingPriv.h"
#include "src/core/SkSpecialImage.h"
#include "src/gpu/ganesh/GrBlurUtils.h"
#include "src/gpu/ganesh/GrCaps.h"
@ -323,10 +324,10 @@ ImageDrawMode optimize_sample_area(const SkISize& image, const SkRect* origSrcRe
* Checks whether the paint is compatible with using SurfaceDrawContext::drawTexture. It is more
* efficient than the SkImage general case.
*/
bool can_use_draw_texture(const SkPaint& paint, bool useCubicResampler, SkMipmapMode mm) {
bool can_use_draw_texture(const SkPaint& paint, const SkSamplingOptions& sampling) {
return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() &&
!paint.getImageFilter() && !paint.getBlender() && !useCubicResampler &&
mm == SkMipmapMode::kNone);
!paint.getImageFilter() && !paint.getBlender() && !sampling.isAniso() &&
!sampling.useCubic && sampling.mipmap == SkMipmapMode::kNone);
}
SkPMColor4f texture_color(SkColor4f paintColor, float entryAlpha, GrColorType srcColorType,
@ -426,9 +427,7 @@ void draw_image(GrRecordingContext* rContext,
SkSamplingOptions sampling,
SkTileMode tm = SkTileMode::kClamp) {
const SkMatrix& ctm(matrixProvider.localToDevice());
if (tm == SkTileMode::kClamp &&
!image.isYUVA() &&
can_use_draw_texture(paint, sampling.useCubic, sampling.mipmap)) {
if (tm == SkTileMode::kClamp && !image.isYUVA() && can_use_draw_texture(paint, sampling)) {
// We've done enough checks above to allow us to pass ClampNearest() and not check for
// scaling adjustments.
auto [view, ct] = image.asView(rContext, GrMipmapped::kNo);
@ -476,7 +475,8 @@ void draw_image(GrRecordingContext* rContext,
// Check for optimization to drop the src rect constraint when using linear filtering.
// TODO: Just rely on image to handle this.
if (!sampling.useCubic &&
if (sampling.isAniso() &&
!sampling.useCubic &&
sampling.filter == SkFilterMode::kLinear &&
restrictToSubset &&
sampling.mipmap == SkMipmapMode::kNone &&
@ -579,6 +579,9 @@ void draw_tiled_bitmap(GrRecordingContext* rContext,
SkCanvas::SrcRectConstraint constraint,
SkSamplingOptions sampling,
SkTileMode tileMode) {
if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(/*imageIsMipped=*/false);
}
SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
int nx = bitmap.width() / tileSize;
@ -670,7 +673,7 @@ void draw_tiled_bitmap(GrRecordingContext* rContext,
SkFilterMode downgrade_to_filter(const SkSamplingOptions& sampling) {
SkFilterMode filter = sampling.filter;
if (sampling.useCubic || sampling.mipmap != SkMipmapMode::kNone) {
if (sampling.isAniso() || sampling.useCubic || sampling.mipmap != SkMipmapMode::kNone) {
// if we were "fancier" than just bilerp, only do bilerp
filter = SkFilterMode::kLinear;
}
@ -779,10 +782,11 @@ void Device::drawImageQuad(const SkImage* image,
int tileFilterPad;
if (sampling.useCubic) {
tileFilterPad = GrBicubicEffect::kFilterTexelPad;
} else if (sampling.filter == SkFilterMode::kNearest) {
tileFilterPad = 0;
} else {
} else if (sampling.filter == SkFilterMode::kLinear || sampling.isAniso()) {
// Aniso will fallback to linear filtering in the tiling case.
tileFilterPad = 1;
} else {
tileFilterPad = 0;
}
int maxTileSize = fContext->priv().caps()->maxTextureSize() - 2*tileFilterPad;
int tileSize;
@ -842,7 +846,7 @@ void Device::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry set[], int count,
const SkSamplingOptions& sampling, const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
SkASSERT(count > 0);
if (!can_use_draw_texture(paint, sampling.useCubic, sampling.mipmap)) {
if (!can_use_draw_texture(paint, sampling)) {
// Send every entry through drawImageQuad() to handle the more complicated paint
int dstClipIndex = 0;
for (int i = 0; i < count; ++i) {

View File

@ -119,6 +119,7 @@ generated_cc_atom(
"//include/gpu:GrYUVABackendTextures_hdr",
"//src/core:SkAutoPixmapStorage_hdr",
"//src/core:SkMipmap_hdr",
"//src/core:SkSamplingPriv_hdr",
"//src/core:SkScopeExit_hdr",
"//src/gpu/ganesh:GrClip_hdr",
"//src/gpu/ganesh:GrDirectContextPriv_hdr",

View File

@ -389,10 +389,20 @@ std::unique_ptr<GrFragmentProcessor> SkImage_Base::MakeFragmentProcessorFromView
GrBicubicEffect::Direction::kXY,
*rContext->priv().caps());
}
if (view.proxy()->asTextureProxy()->mipmapped() == GrMipmapped::kNo) {
if (sampling.isAniso()) {
if (!rContext->priv().caps()->anisoSupport()) {
// Fallback to linear
sampling = SkSamplingPriv::AnisoFallback(view.mipmapped() == GrMipmapped::kYes);
}
} else if (view.mipmapped() == GrMipmapped::kNo) {
sampling = SkSamplingOptions(sampling.filter);
}
GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);
GrSamplerState sampler;
if (sampling.isAniso()) {
sampler = GrSamplerState::Aniso(wmx, wmy, sampling.maxAniso, view.mipmapped());
} else {
sampler = GrSamplerState(wmx, wmy, sampling.filter, sampling.mipmap);
}
if (subset) {
if (domain) {
return GrTextureEffect::MakeSubset(std::move(view),

View File

@ -5,9 +5,7 @@
* found in the LICENSE file.
*/
#include <cstddef>
#include <cstring>
#include <type_traits>
#include "src/image/SkImage_GpuYUVA.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkYUVAPixmaps.h"
@ -16,6 +14,7 @@
#include "include/gpu/GrYUVABackendTextures.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkMipmap.h"
#include "src/core/SkSamplingPriv.h"
#include "src/core/SkScopeExit.h"
#include "src/gpu/ganesh/GrClip.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
@ -28,7 +27,6 @@
#include "src/gpu/ganesh/effects/GrBicubicEffect.h"
#include "src/gpu/ganesh/effects/GrYUVtoRGBEffect.h"
#include "src/image/SkImage_Gpu.h"
#include "src/image/SkImage_GpuYUVA.h"
static constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
@ -191,6 +189,11 @@ std::unique_ptr<GrFragmentProcessor> SkImage_GpuYUVA::onAsFragmentProcessor(
if (!fContext->priv().matches(context)) {
return {};
}
// At least for now we do not attempt aniso filtering on YUVA images.
if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(fYUVAProxies.mipmapped() == GrMipmapped::kYes);
}
auto wmx = SkTileModeToWrapMode(tileModes[0]);
auto wmy = SkTileModeToWrapMode(tileModes[1]);
GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);

View File

@ -444,7 +444,7 @@ std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Raster::onAsView(
if (fPinnedView) {
// We ignore the mipmap request here. If the pinned view isn't mipmapped then we will
// fallback to bilinear. The pin API is used by Android Framework which does not expose
// mipmapping .Moreover, we're moving towards requiring that images be made with mip levels
// mipmapping . Moreover, we're moving towards requiring that images be made with mip levels
// if mipmapping is desired (skbug.com/10411)
mipmapped = GrMipmapped::kNo;
if (policy != GrImageTexGenPolicy::kDraw) {

View File

@ -220,6 +220,11 @@ SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
return nullptr;
}
SkSamplingOptions sampling = fSampling;
if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
}
auto supported = [](const SkSamplingOptions& sampling) {
const std::tuple<SkFilterMode,SkMipmapMode> supported[] = {
{SkFilterMode::kNearest, SkMipmapMode::kNone}, // legacy None
@ -233,7 +238,7 @@ SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
}
return false;
};
if (fSampling.useCubic || !supported(fSampling)) {
if (sampling.useCubic || !supported(sampling)) {
return nullptr;
}
@ -262,7 +267,7 @@ SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
return nullptr;
}
return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, fSampling,
return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, sampling,
as_IB(fImage.get()), rec, alloc);
}
#endif
@ -471,6 +476,9 @@ bool SkImageShader::doStages(const SkStageRec& rec, TransformShader* updater) co
SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
// We only support certain sampling options in stages so far
auto sampling = fSampling;
if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
}
if (sampling.mipmap == SkMipmapMode::kLinear) {
return false;
}
@ -777,10 +785,14 @@ skvm::Color SkImageShader::makeProgram(
baseInv.normalizePerspective();
auto sampling = fSampling;
if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
}
auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap);
if (!access) {
return {};
}
auto [upper, upperInv] = access->level();
// If we are using a coordShader, then we can't make guesses about the state of the matrix.
if (!sampling.useCubic && !coordShader) {

View File

@ -211,6 +211,7 @@ generated_cc_atom(
":JsonWriteBuffer_hdr",
"//include/core:SkFlattenable_hdr",
"//include/core:SkPoint_hdr",
"//include/core:SkSamplingOptions_hdr",
"//include/core:SkString_hdr",
"//src/utils:SkJSONWriter_hdr",
],

View File

@ -572,6 +572,7 @@ void DrawCommand::MakeJsonRegion(SkJSONWriter& writer, const SkRegion& region) {
void DrawCommand::MakeJsonSampling(SkJSONWriter& writer, const SkSamplingOptions& sampling) {
writer.beginObject();
writer.appendS32("maxAniso", sampling.maxAniso);
writer.appendBool("useCubic", sampling.useCubic);
writer.appendS32("filter", (int)sampling.filter);
writer.appendS32("mipmap", (int)sampling.mipmap);

View File

@ -9,6 +9,7 @@
#include "include/core/SkFlattenable.h"
#include "include/core/SkPoint.h"
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkString.h"
#include "src/utils/SkJSONWriter.h"
#include "tools/debugger/DrawCommand.h"