Skip to content

Commit

Permalink
Impl SkFilterOptions for raster-images
Browse files Browse the repository at this point in the history
Maintains the old and new code-paths:
- if the shader was made with explicit filteroptions, use those
- if not, infer the filteroptions from the filterquality enum
  (and the legacy heuristics of sniffing/snapping the ctm)

In either case, the bulk of the onProgram() is shared, driving off
the (possibly computed locally) filteroptions.

bench looks sort like we expect:

    509.28  	filteroptions_sampling_0_mipmap_0	8888
    495.76  	filteroptions_sampling_0_mipmap_1	8888
    642.52  	filteroptions_sampling_0_mipmap_2	8888

    942.40  	filteroptions_sampling_1_mipmap_0	8888
    976.94  	filteroptions_sampling_1_mipmap_1	8888
   1686.34  	filteroptions_sampling_1_mipmap_2	8888

Bug: skia:10344
Change-Id: I77a79f79f640986fdd6b14f163c1a03462c55dc0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/297561
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
  • Loading branch information
reed-at-google authored and Skia Commit-Bot committed Jul 10, 2020
1 parent 9f82148 commit f8a6b5b
Show file tree
Hide file tree
Showing 12 changed files with 546 additions and 133 deletions.
64 changes: 64 additions & 0 deletions bench/FilteringBench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include "bench/Benchmark.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkShader.h"
#include "include/core/SkString.h"
#include "tools/Resources.h"

class FilteringBench : public Benchmark {
public:
FilteringBench(SkFilterOptions options) : fOptions(options) {
fName.printf("filteroptions_sampling_%d_mipmap_%d",
(int)options.fSampling, (int)options.fMipmap);
}

protected:
const char* onGetName() override {
return fName.c_str();
}

void onDelayedSetup() override {
auto img = GetResourceAsImage("images/ship.png");
// need to force raster since lazy doesn't support filteroptions yet
img = img->makeRasterImage();

fRect = SkRect::MakeIWH(img->width(), img->height());
fShader = img->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, fOptions);
}

void onDraw(int loops, SkCanvas* canvas) override {
// scale so we will trigger lerping between levels if we mipmapping
canvas->scale(0.75f, 0.75f);

SkPaint paint;
paint.setShader(fShader);
for (int i = 0; i < loops; ++i) {
for (int j = 0; j < 10; ++j) {
canvas->drawRect(fRect, paint);
}
}
}

private:
SkString fName;
SkRect fRect;
sk_sp<SkShader> fShader;
SkFilterOptions fOptions;

typedef Benchmark INHERITED;
};

DEF_BENCH( return new FilteringBench({SkSamplingMode::kLinear, SkMipmapMode::kLinear}); )
DEF_BENCH( return new FilteringBench({SkSamplingMode::kLinear, SkMipmapMode::kNearest}); )
DEF_BENCH( return new FilteringBench({SkSamplingMode::kLinear, SkMipmapMode::kNone}); )

DEF_BENCH( return new FilteringBench({SkSamplingMode::kNearest, SkMipmapMode::kLinear}); )
DEF_BENCH( return new FilteringBench({SkSamplingMode::kNearest, SkMipmapMode::kNearest}); )
DEF_BENCH( return new FilteringBench({SkSamplingMode::kNearest, SkMipmapMode::kNone}); )
53 changes: 53 additions & 0 deletions gm/showmiplevels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,56 @@ DEF_GM( return new ShowMipLevels2(255, 255); )
DEF_GM( return new ShowMipLevels2(256, 255); )
DEF_GM( return new ShowMipLevels2(255, 256); )
DEF_GM( return new ShowMipLevels2(256, 256); )

#include "tools/Resources.h"

class ShowMipLevels3 : public skiagm::GM {
sk_sp<SkImage> fImg;

SkString onShortName() override { return SkString("showmiplevels_explicit"); }

SkISize onISize() override { return {1290, 990}; }

SkScalar draw_downscaling(SkCanvas* canvas, SkFilterOptions options) {
SkAutoCanvasRestore acr(canvas, true);

SkPaint paint;
SkRect r = {0, 0, 150, 150};
for (float scale = 1; scale >= 0.125f; scale *= 0.75f) {
SkMatrix matrix = SkMatrix::Scale(scale, scale);
paint.setShader(fImg->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
options, &matrix));
canvas->drawRect(r, paint);
// r.offset(r.width() + 10, 0);
canvas->translate(r.width() + 10, 0);
}
return 160;
}

void onOnceBeforeDraw() override {
fImg = GetResourceAsImage("images/ship.png");
}

void onDraw(SkCanvas* canvas) override {
canvas->drawColor(0xFFDDDDDD);

const SkSamplingMode samplings[] = {
SkSamplingMode::kNearest, SkSamplingMode::kLinear
};
const SkMipmapMode mipmodes[] = {
SkMipmapMode::kNone, SkMipmapMode::kNearest, SkMipmapMode::kLinear
};

canvas->translate(10, 10);
for (auto mm : mipmodes) {
for (auto sa : samplings) {
canvas->translate(0, draw_downscaling(canvas, {sa, mm}));
}
canvas->translate(0, 10);
}
}

private:
typedef skiagm::GM INHERITED;
};
DEF_GM( return new ShowMipLevels3; )
1 change: 1 addition & 0 deletions gn/bench.gni
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ bench_sources = [
"$_bench/DrawBitmapAABench.cpp",
"$_bench/EncodeBench.cpp",
"$_bench/FSRectBench.cpp",
"$_bench/FilteringBench.cpp",
"$_bench/FontCacheBench.cpp",
"$_bench/GMBench.cpp",
"$_bench/GameBench.cpp",
Expand Down
20 changes: 20 additions & 0 deletions include/core/SkImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class SkData;
class SkCanvas;
class SkImageFilter;
class SkImageGenerator;
class SkM44;
class SkPaint;
class SkPicture;
class SkSurface;
Expand All @@ -35,6 +36,22 @@ class GrContextThreadSafeProxy;

struct SkYUVAIndex;

enum class SkSamplingMode {
kNearest, // single sample point (nearest neighbor)
kLinear, // interporate between 2x2 sample points (bilinear interpolation)
};

enum class SkMipmapMode {
kNone, // ignore mipmap levels, sample from the "base"
kNearest, // sample from the nearest level
kLinear, // interpolate between the two nearest levels
};

struct SkFilterOptions {
SkSamplingMode fSampling;
SkMipmapMode fMipmap;
};

/** \class SkImage
SkImage describes a two dimensional array of pixels to draw. The pixels may be
decoded in a raster bitmap, encoded in a SkPicture or compressed data stream,
Expand Down Expand Up @@ -770,6 +787,9 @@ class SK_API SkImage : public SkRefCnt {
*/
bool isOpaque() const { return SkAlphaTypeIsOpaque(this->alphaType()); }

sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, const SkFilterOptions&,
const SkMatrix* localMatrix = nullptr) const;

/** Creates SkShader from SkImage. SkShader dimensions are taken from SkImage. SkShader uses
SkTileMode rules to fill drawn area outside SkImage. localMatrix permits
transforming SkImage before SkCanvas matrix is applied.
Expand Down
72 changes: 72 additions & 0 deletions src/core/SkBitmapController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,75 @@ SkBitmapController::State::State(const SkImage_Base* image,
// and will destroy us if it is nullptr.
fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
}

///////////////////////////////////////////////////////////////////////////////////////////////////

SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& inv,
SkMipmapMode requestedMode) {
fResolvedMode = requestedMode;
fLowerWeight = 0;

auto load_upper_from_base = [&]() {
// only do this once
if (fBaseStorage.getPixels() == nullptr) {
(void)image->getROPixels(&fBaseStorage);
fUpper.reset(fBaseStorage.info(), fBaseStorage.getPixels(), fBaseStorage.rowBytes());
}
};

float level = 0;
if (requestedMode != SkMipmapMode::kNone) {
SkSize scale;
if (!inv.decomposeScale(&scale, nullptr)) {
fResolvedMode = SkMipmapMode::kNone;
} else {
level = SkMipMap::ComputeLevel({1/scale.width(), 1/scale.height()});
if (level <= 0) {
fResolvedMode = SkMipmapMode::kNone;
level = 0;
}
}
}

int levelNum = sk_float_floor2int(level);
float lowerWeight = level - levelNum; // fract(level)
SkASSERT(levelNum >= 0);

if (levelNum == 0) {
load_upper_from_base();
}
// load fCurrMip if needed
if (levelNum > 0 || (fResolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
// try to load from the cache
fCurrMip.reset(SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
if (!fCurrMip) {
fCurrMip.reset(SkMipMapCache::AddAndRef(image));
if (!fCurrMip) {
load_upper_from_base();
fResolvedMode = SkMipmapMode::kNone;
}
}
if (fCurrMip) {
SkMipMap::Level levelRec;

SkASSERT(fResolvedMode != SkMipmapMode::kNone);
if (levelNum > 0) {
if (fCurrMip->getLevel(levelNum - 1, &levelRec)) {
fUpper = levelRec.fPixmap;
} else {
load_upper_from_base();
fResolvedMode = SkMipmapMode::kNone;
}
}

if (fResolvedMode == SkMipmapMode::kLinear) {
if (fCurrMip->getLevel(levelNum, &levelRec)) {
fLower = levelRec.fPixmap;
fLowerWeight = lowerWeight;
} else {
fResolvedMode = SkMipmapMode::kNearest;
}
}
}
}
}
23 changes: 23 additions & 0 deletions src/core/SkBitmapController.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "include/core/SkBitmap.h"
#include "include/core/SkFilterQuality.h"
#include "include/core/SkImage.h"
#include "include/core/SkMatrix.h"
#include "src/core/SkBitmapCache.h"
#include "src/core/SkMipMap.h"
Expand Down Expand Up @@ -50,4 +51,26 @@ class SkBitmapController : ::SkNoncopyable {
SkBitmapController() = delete;
};

class SkMipmapAccessor : ::SkNoncopyable {
public:
SkMipmapAccessor(const SkImage_Base*, const SkMatrix& inv, SkMipmapMode requestedMode);

const SkPixmap& level() const { return fUpper; }
// only valid if mode() == kLinear
const SkPixmap& lowerLevel() const { return fLower; }
// 0....1. Will be 1.0 if there is no lowerLevel
float lowerWeight() const { return fLowerWeight; }
SkMipmapMode mode() const { return fResolvedMode; }

private:
SkPixmap fUpper,
fLower; // only valid for mip_linear
float fLowerWeight; // lower * weight + upper * (1 - weight)
SkMipmapMode fResolvedMode;

// these manage lifetime for the buffers
SkBitmap fBaseStorage;
sk_sp<const SkMipMap> fCurrMip;
};

#endif
25 changes: 16 additions & 9 deletions src/core/SkMipMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,11 +709,9 @@ SkISize SkMipMap::ComputeLevelSize(int baseWidth, int baseHeight, int level) {

///////////////////////////////////////////////////////////////////////////////

bool SkMipMap::extractLevel(const SkSize& scaleSize, Level* levelPtr) const {
if (nullptr == fLevels) {
return false;
}

// Returns fractional level value. floor(level) is the index of the larger level.
// < 0 means failure.
float SkMipMap::ComputeLevel(SkSize scaleSize) {
SkASSERT(scaleSize.width() >= 0 && scaleSize.height() >= 0);

#ifndef SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
Expand All @@ -727,17 +725,24 @@ bool SkMipMap::extractLevel(const SkSize& scaleSize, Level* levelPtr) const {
#endif

if (scale >= SK_Scalar1 || scale <= 0 || !SkScalarIsFinite(scale)) {
return false;
return -1;
}

SkScalar L = -SkScalarLog2(scale);
if (!SkScalarIsFinite(L)) {
return false;
return -1;
}
SkASSERT(L >= 0);
int level = SkScalarFloorToInt(L);
return L;
}

SkASSERT(level >= 0);
bool SkMipMap::extractLevel(SkSize scaleSize, Level* levelPtr) const {
if (nullptr == fLevels) {
return false;
}

float L = ComputeLevel(scaleSize);
int level = SkScalarFloorToInt(L);
if (level <= 0) {
return false;
}
Expand Down Expand Up @@ -779,6 +784,8 @@ bool SkMipMap::getLevel(int index, Level* levelPtr) const {
}
if (levelPtr) {
*levelPtr = fLevels[index];
// need to augment with our colorspace
levelPtr->fPixmap.setColorSpace(fCS);
}
return true;
}
5 changes: 4 additions & 1 deletion src/core/SkMipMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class SkMipMap : public SkCachedData {
// the base level. So index 0 represents mipmap level 1.
static SkISize ComputeLevelSize(int baseWidth, int baseHeight, int level);

// Computes the fractional level based on the scaling in X and Y.
static float ComputeLevel(SkSize scaleSize);

// We use a block of (possibly discardable) memory to hold an array of Level structs, followed
// by the pixel data for each level. On 32-bit platforms, Level would naturally be 4 byte
// aligned, so the pixel data could end up with 4 byte alignment. If the pixel data is F16,
Expand All @@ -52,7 +55,7 @@ class SkMipMap : public SkCachedData {
SkSize fScale; // < 1.0
};

bool extractLevel(const SkSize& scale, Level*) const;
bool extractLevel(SkSize scale, Level*) const;

// countLevels returns the number of mipmap levels generated (which does not
// include the base mipmap level).
Expand Down
5 changes: 3 additions & 2 deletions src/core/SkPicturePriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ class SkPicturePriv {
kEdgeAAQuadColor4f_Version = 73,
kMorphologyTakesScalar_Version = 74,
kVerticesUseReadBuffer_Version = 75,
kFilteringInImageShader_Version = 76,
kFilterEnumInImageShader_Version = 76,
kFilterOptionsInImageShader_Version = 77,

// Only SKPs within the min/current picture version range (inclusive) can be read.
kMin_Version = kTileModeInBlurImageFilter_Version,
kCurrent_Version = kFilteringInImageShader_Version
kCurrent_Version = kFilterOptionsInImageShader_Version
};

static_assert(kMin_Version <= 62, "Remove kFontAxes_bad from SkFontDescriptor.cpp");
Expand Down
7 changes: 7 additions & 0 deletions src/image/SkImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
SkImageShader::kInheritFromPaint);
}

sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
const SkFilterOptions& options,
const SkMatrix* localMatrix) const {
return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
options, localMatrix);
}

sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
const SkMatrix* localMatrix, SkFilterQuality filtering) const {
return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy, localMatrix,
Expand Down
Loading

0 comments on commit f8a6b5b

Please sign in to comment.