Skip to content

Commit f8a6b5b

Browse files
reed-at-googleSkia Commit-Bot
authored and
Skia Commit-Bot
committed
Impl SkFilterOptions for raster-images
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 <[email protected]> Reviewed-by: Mike Klein <[email protected]>
1 parent 9f82148 commit f8a6b5b

12 files changed

+546
-133
lines changed

bench/FilteringBench.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2012 Google Inc.
3+
*
4+
* Use of this source code is governed by a BSD-style license that can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include "bench/Benchmark.h"
9+
#include "include/core/SkCanvas.h"
10+
#include "include/core/SkPaint.h"
11+
#include "include/core/SkShader.h"
12+
#include "include/core/SkString.h"
13+
#include "tools/Resources.h"
14+
15+
class FilteringBench : public Benchmark {
16+
public:
17+
FilteringBench(SkFilterOptions options) : fOptions(options) {
18+
fName.printf("filteroptions_sampling_%d_mipmap_%d",
19+
(int)options.fSampling, (int)options.fMipmap);
20+
}
21+
22+
protected:
23+
const char* onGetName() override {
24+
return fName.c_str();
25+
}
26+
27+
void onDelayedSetup() override {
28+
auto img = GetResourceAsImage("images/ship.png");
29+
// need to force raster since lazy doesn't support filteroptions yet
30+
img = img->makeRasterImage();
31+
32+
fRect = SkRect::MakeIWH(img->width(), img->height());
33+
fShader = img->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, fOptions);
34+
}
35+
36+
void onDraw(int loops, SkCanvas* canvas) override {
37+
// scale so we will trigger lerping between levels if we mipmapping
38+
canvas->scale(0.75f, 0.75f);
39+
40+
SkPaint paint;
41+
paint.setShader(fShader);
42+
for (int i = 0; i < loops; ++i) {
43+
for (int j = 0; j < 10; ++j) {
44+
canvas->drawRect(fRect, paint);
45+
}
46+
}
47+
}
48+
49+
private:
50+
SkString fName;
51+
SkRect fRect;
52+
sk_sp<SkShader> fShader;
53+
SkFilterOptions fOptions;
54+
55+
typedef Benchmark INHERITED;
56+
};
57+
58+
DEF_BENCH( return new FilteringBench({SkSamplingMode::kLinear, SkMipmapMode::kLinear}); )
59+
DEF_BENCH( return new FilteringBench({SkSamplingMode::kLinear, SkMipmapMode::kNearest}); )
60+
DEF_BENCH( return new FilteringBench({SkSamplingMode::kLinear, SkMipmapMode::kNone}); )
61+
62+
DEF_BENCH( return new FilteringBench({SkSamplingMode::kNearest, SkMipmapMode::kLinear}); )
63+
DEF_BENCH( return new FilteringBench({SkSamplingMode::kNearest, SkMipmapMode::kNearest}); )
64+
DEF_BENCH( return new FilteringBench({SkSamplingMode::kNearest, SkMipmapMode::kNone}); )

gm/showmiplevels.cpp

+53
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,56 @@ DEF_GM( return new ShowMipLevels2(255, 255); )
312312
DEF_GM( return new ShowMipLevels2(256, 255); )
313313
DEF_GM( return new ShowMipLevels2(255, 256); )
314314
DEF_GM( return new ShowMipLevels2(256, 256); )
315+
316+
#include "tools/Resources.h"
317+
318+
class ShowMipLevels3 : public skiagm::GM {
319+
sk_sp<SkImage> fImg;
320+
321+
SkString onShortName() override { return SkString("showmiplevels_explicit"); }
322+
323+
SkISize onISize() override { return {1290, 990}; }
324+
325+
SkScalar draw_downscaling(SkCanvas* canvas, SkFilterOptions options) {
326+
SkAutoCanvasRestore acr(canvas, true);
327+
328+
SkPaint paint;
329+
SkRect r = {0, 0, 150, 150};
330+
for (float scale = 1; scale >= 0.125f; scale *= 0.75f) {
331+
SkMatrix matrix = SkMatrix::Scale(scale, scale);
332+
paint.setShader(fImg->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
333+
options, &matrix));
334+
canvas->drawRect(r, paint);
335+
// r.offset(r.width() + 10, 0);
336+
canvas->translate(r.width() + 10, 0);
337+
}
338+
return 160;
339+
}
340+
341+
void onOnceBeforeDraw() override {
342+
fImg = GetResourceAsImage("images/ship.png");
343+
}
344+
345+
void onDraw(SkCanvas* canvas) override {
346+
canvas->drawColor(0xFFDDDDDD);
347+
348+
const SkSamplingMode samplings[] = {
349+
SkSamplingMode::kNearest, SkSamplingMode::kLinear
350+
};
351+
const SkMipmapMode mipmodes[] = {
352+
SkMipmapMode::kNone, SkMipmapMode::kNearest, SkMipmapMode::kLinear
353+
};
354+
355+
canvas->translate(10, 10);
356+
for (auto mm : mipmodes) {
357+
for (auto sa : samplings) {
358+
canvas->translate(0, draw_downscaling(canvas, {sa, mm}));
359+
}
360+
canvas->translate(0, 10);
361+
}
362+
}
363+
364+
private:
365+
typedef skiagm::GM INHERITED;
366+
};
367+
DEF_GM( return new ShowMipLevels3; )

gn/bench.gni

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ bench_sources = [
4646
"$_bench/DrawBitmapAABench.cpp",
4747
"$_bench/EncodeBench.cpp",
4848
"$_bench/FSRectBench.cpp",
49+
"$_bench/FilteringBench.cpp",
4950
"$_bench/FontCacheBench.cpp",
5051
"$_bench/GMBench.cpp",
5152
"$_bench/GameBench.cpp",

include/core/SkImage.h

+20
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class SkData;
2626
class SkCanvas;
2727
class SkImageFilter;
2828
class SkImageGenerator;
29+
class SkM44;
2930
class SkPaint;
3031
class SkPicture;
3132
class SkSurface;
@@ -35,6 +36,22 @@ class GrContextThreadSafeProxy;
3536

3637
struct SkYUVAIndex;
3738

39+
enum class SkSamplingMode {
40+
kNearest, // single sample point (nearest neighbor)
41+
kLinear, // interporate between 2x2 sample points (bilinear interpolation)
42+
};
43+
44+
enum class SkMipmapMode {
45+
kNone, // ignore mipmap levels, sample from the "base"
46+
kNearest, // sample from the nearest level
47+
kLinear, // interpolate between the two nearest levels
48+
};
49+
50+
struct SkFilterOptions {
51+
SkSamplingMode fSampling;
52+
SkMipmapMode fMipmap;
53+
};
54+
3855
/** \class SkImage
3956
SkImage describes a two dimensional array of pixels to draw. The pixels may be
4057
decoded in a raster bitmap, encoded in a SkPicture or compressed data stream,
@@ -770,6 +787,9 @@ class SK_API SkImage : public SkRefCnt {
770787
*/
771788
bool isOpaque() const { return SkAlphaTypeIsOpaque(this->alphaType()); }
772789

790+
sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, const SkFilterOptions&,
791+
const SkMatrix* localMatrix = nullptr) const;
792+
773793
/** Creates SkShader from SkImage. SkShader dimensions are taken from SkImage. SkShader uses
774794
SkTileMode rules to fill drawn area outside SkImage. localMatrix permits
775795
transforming SkImage before SkCanvas matrix is applied.

src/core/SkBitmapController.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,75 @@ SkBitmapController::State::State(const SkImage_Base* image,
104104
// and will destroy us if it is nullptr.
105105
fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
106106
}
107+
108+
///////////////////////////////////////////////////////////////////////////////////////////////////
109+
110+
SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& inv,
111+
SkMipmapMode requestedMode) {
112+
fResolvedMode = requestedMode;
113+
fLowerWeight = 0;
114+
115+
auto load_upper_from_base = [&]() {
116+
// only do this once
117+
if (fBaseStorage.getPixels() == nullptr) {
118+
(void)image->getROPixels(&fBaseStorage);
119+
fUpper.reset(fBaseStorage.info(), fBaseStorage.getPixels(), fBaseStorage.rowBytes());
120+
}
121+
};
122+
123+
float level = 0;
124+
if (requestedMode != SkMipmapMode::kNone) {
125+
SkSize scale;
126+
if (!inv.decomposeScale(&scale, nullptr)) {
127+
fResolvedMode = SkMipmapMode::kNone;
128+
} else {
129+
level = SkMipMap::ComputeLevel({1/scale.width(), 1/scale.height()});
130+
if (level <= 0) {
131+
fResolvedMode = SkMipmapMode::kNone;
132+
level = 0;
133+
}
134+
}
135+
}
136+
137+
int levelNum = sk_float_floor2int(level);
138+
float lowerWeight = level - levelNum; // fract(level)
139+
SkASSERT(levelNum >= 0);
140+
141+
if (levelNum == 0) {
142+
load_upper_from_base();
143+
}
144+
// load fCurrMip if needed
145+
if (levelNum > 0 || (fResolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
146+
// try to load from the cache
147+
fCurrMip.reset(SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
148+
if (!fCurrMip) {
149+
fCurrMip.reset(SkMipMapCache::AddAndRef(image));
150+
if (!fCurrMip) {
151+
load_upper_from_base();
152+
fResolvedMode = SkMipmapMode::kNone;
153+
}
154+
}
155+
if (fCurrMip) {
156+
SkMipMap::Level levelRec;
157+
158+
SkASSERT(fResolvedMode != SkMipmapMode::kNone);
159+
if (levelNum > 0) {
160+
if (fCurrMip->getLevel(levelNum - 1, &levelRec)) {
161+
fUpper = levelRec.fPixmap;
162+
} else {
163+
load_upper_from_base();
164+
fResolvedMode = SkMipmapMode::kNone;
165+
}
166+
}
167+
168+
if (fResolvedMode == SkMipmapMode::kLinear) {
169+
if (fCurrMip->getLevel(levelNum, &levelRec)) {
170+
fLower = levelRec.fPixmap;
171+
fLowerWeight = lowerWeight;
172+
} else {
173+
fResolvedMode = SkMipmapMode::kNearest;
174+
}
175+
}
176+
}
177+
}
178+
}

src/core/SkBitmapController.h

+23
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "include/core/SkBitmap.h"
1212
#include "include/core/SkFilterQuality.h"
13+
#include "include/core/SkImage.h"
1314
#include "include/core/SkMatrix.h"
1415
#include "src/core/SkBitmapCache.h"
1516
#include "src/core/SkMipMap.h"
@@ -50,4 +51,26 @@ class SkBitmapController : ::SkNoncopyable {
5051
SkBitmapController() = delete;
5152
};
5253

54+
class SkMipmapAccessor : ::SkNoncopyable {
55+
public:
56+
SkMipmapAccessor(const SkImage_Base*, const SkMatrix& inv, SkMipmapMode requestedMode);
57+
58+
const SkPixmap& level() const { return fUpper; }
59+
// only valid if mode() == kLinear
60+
const SkPixmap& lowerLevel() const { return fLower; }
61+
// 0....1. Will be 1.0 if there is no lowerLevel
62+
float lowerWeight() const { return fLowerWeight; }
63+
SkMipmapMode mode() const { return fResolvedMode; }
64+
65+
private:
66+
SkPixmap fUpper,
67+
fLower; // only valid for mip_linear
68+
float fLowerWeight; // lower * weight + upper * (1 - weight)
69+
SkMipmapMode fResolvedMode;
70+
71+
// these manage lifetime for the buffers
72+
SkBitmap fBaseStorage;
73+
sk_sp<const SkMipMap> fCurrMip;
74+
};
75+
5376
#endif

src/core/SkMipMap.cpp

+16-9
Original file line numberDiff line numberDiff line change
@@ -709,11 +709,9 @@ SkISize SkMipMap::ComputeLevelSize(int baseWidth, int baseHeight, int level) {
709709

710710
///////////////////////////////////////////////////////////////////////////////
711711

712-
bool SkMipMap::extractLevel(const SkSize& scaleSize, Level* levelPtr) const {
713-
if (nullptr == fLevels) {
714-
return false;
715-
}
716-
712+
// Returns fractional level value. floor(level) is the index of the larger level.
713+
// < 0 means failure.
714+
float SkMipMap::ComputeLevel(SkSize scaleSize) {
717715
SkASSERT(scaleSize.width() >= 0 && scaleSize.height() >= 0);
718716

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

729727
if (scale >= SK_Scalar1 || scale <= 0 || !SkScalarIsFinite(scale)) {
730-
return false;
728+
return -1;
731729
}
732730

733731
SkScalar L = -SkScalarLog2(scale);
734732
if (!SkScalarIsFinite(L)) {
735-
return false;
733+
return -1;
736734
}
737735
SkASSERT(L >= 0);
738-
int level = SkScalarFloorToInt(L);
736+
return L;
737+
}
739738

740-
SkASSERT(level >= 0);
739+
bool SkMipMap::extractLevel(SkSize scaleSize, Level* levelPtr) const {
740+
if (nullptr == fLevels) {
741+
return false;
742+
}
743+
744+
float L = ComputeLevel(scaleSize);
745+
int level = SkScalarFloorToInt(L);
741746
if (level <= 0) {
742747
return false;
743748
}
@@ -779,6 +784,8 @@ bool SkMipMap::getLevel(int index, Level* levelPtr) const {
779784
}
780785
if (levelPtr) {
781786
*levelPtr = fLevels[index];
787+
// need to augment with our colorspace
788+
levelPtr->fPixmap.setColorSpace(fCS);
782789
}
783790
return true;
784791
}

src/core/SkMipMap.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class SkMipMap : public SkCachedData {
4343
// the base level. So index 0 represents mipmap level 1.
4444
static SkISize ComputeLevelSize(int baseWidth, int baseHeight, int level);
4545

46+
// Computes the fractional level based on the scaling in X and Y.
47+
static float ComputeLevel(SkSize scaleSize);
48+
4649
// We use a block of (possibly discardable) memory to hold an array of Level structs, followed
4750
// by the pixel data for each level. On 32-bit platforms, Level would naturally be 4 byte
4851
// aligned, so the pixel data could end up with 4 byte alignment. If the pixel data is F16,
@@ -52,7 +55,7 @@ class SkMipMap : public SkCachedData {
5255
SkSize fScale; // < 1.0
5356
};
5457

55-
bool extractLevel(const SkSize& scale, Level*) const;
58+
bool extractLevel(SkSize scale, Level*) const;
5659

5760
// countLevels returns the number of mipmap levels generated (which does not
5861
// include the base mipmap level).

src/core/SkPicturePriv.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,12 @@ class SkPicturePriv {
9999
kEdgeAAQuadColor4f_Version = 73,
100100
kMorphologyTakesScalar_Version = 74,
101101
kVerticesUseReadBuffer_Version = 75,
102-
kFilteringInImageShader_Version = 76,
102+
kFilterEnumInImageShader_Version = 76,
103+
kFilterOptionsInImageShader_Version = 77,
103104

104105
// Only SKPs within the min/current picture version range (inclusive) can be read.
105106
kMin_Version = kTileModeInBlurImageFilter_Version,
106-
kCurrent_Version = kFilteringInImageShader_Version
107+
kCurrent_Version = kFilterOptionsInImageShader_Version
107108
};
108109

109110
static_assert(kMin_Version <= 62, "Remove kFontAxes_bad from SkFontDescriptor.cpp");

src/image/SkImage.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
127127
SkImageShader::kInheritFromPaint);
128128
}
129129

130+
sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
131+
const SkFilterOptions& options,
132+
const SkMatrix* localMatrix) const {
133+
return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
134+
options, localMatrix);
135+
}
136+
130137
sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
131138
const SkMatrix* localMatrix, SkFilterQuality filtering) const {
132139
return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy, localMatrix,

0 commit comments

Comments
 (0)