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:
parent
3f8806ce0f
commit
e6f23f98de
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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 };
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
],
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user