blob: 08f27b7390ae1ebb0b0796fc4c59506f4f8a731a [file] [log] [blame]
Brian Salomonf0334042020-01-31 16:16:39 -05001/*
2 * Copyright 2014 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// This test only works with the GPU backend.
9
10#include "gm/gm.h"
11#include "include/core/SkBitmap.h"
12#include "include/core/SkCanvas.h"
13#include "include/core/SkColor.h"
14#include "include/core/SkMatrix.h"
15#include "include/core/SkPaint.h"
16#include "include/core/SkRect.h"
17#include "include/core/SkSize.h"
18#include "include/core/SkString.h"
19#include "include/effects/SkGradientShader.h"
20#include "include/private/GrTypesPriv.h"
21#include "include/private/SkTArray.h"
22#include "src/gpu/GrBitmapTextureMaker.h"
23#include "src/gpu/GrContextPriv.h"
24#include "src/gpu/GrProxyProvider.h"
25#include "src/gpu/GrRenderTargetContext.h"
26#include "src/gpu/GrRenderTargetContextPriv.h"
27#include "src/gpu/GrSamplerState.h"
28#include "src/gpu/GrTextureProxy.h"
29#include "tools/Resources.h"
30#include "tools/gpu/TestOps.h"
31
32#include <memory>
33#include <utility>
34
35namespace skiagm {
36/**
37 * This GM directly exercises GrTextureEffect::MakeTexelSubset.
38 */
39class TexelSubset : public GpuGM {
40public:
41 TexelSubset(GrSamplerState::Filter filter, bool upscale) : fFilter(filter), fUpscale(upscale) {
42 this->setBGColor(0xFFFFFFFF);
43 }
44
45protected:
46 SkString onShortName() override {
47 SkString name("texel_subset");
48 switch (fFilter) {
49 case GrSamplerState::Filter::kNearest:
50 name.append("_nearest");
51 break;
52 case GrSamplerState::Filter::kBilerp:
53 name.append("_bilerp");
54 break;
55 case GrSamplerState::Filter::kMipMap:
56 name.append("_mip_map");
57 break;
58 }
59 name.append(fUpscale ? "_up" : "_down");
60 return name;
61 }
62
63 SkISize onISize() override {
64 int n = GrSamplerState::kWrapModeCount;
65 if (fFilter != GrSamplerState::Filter::kNearest) {
66 // Account for not supporting kMirror or kMirrorRepeat with filtering.
67 n -= 2;
68 }
69 int w = kTestPad + 2 * n * (kImageSize.width() + 2 * kDrawPad + kTestPad);
70 int h = kTestPad + 2 * n * (kImageSize.height() + 2 * kDrawPad + kTestPad);
71 return {w, h};
72 }
73
74 void onOnceBeforeDraw() override {
75 GetResourceAsBitmap("images/mandrill_128.png", &fBitmap);
76 // Make the bitmap non-square to detect any width/height confusion.
77 fBitmap.extractSubset(&fBitmap, SkIRect::MakeSize(fBitmap.dimensions()).makeInset(0, 20));
78 SkASSERT(fBitmap.dimensions() == kImageSize);
79 }
80
81 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
82 SkCanvas* canvas, SkString* errorMsg) override {
83 GrMipMapped mipMapped = fFilter == GrSamplerState::Filter::kMipMap &&
84 context->priv().caps()->mipMapSupport()
85 ? GrMipMapped::kYes : GrMipMapped::kNo;
86 GrBitmapTextureMaker maker(context, fBitmap);
Greg Danielc61d7e32020-02-04 14:27:45 -050087 auto[view, grCT] = maker.view(mipMapped);
Greg Danielcc104db2020-02-03 14:17:08 -050088 if (!view.proxy()) {
Brian Salomonf0334042020-01-31 16:16:39 -050089 *errorMsg = "Failed to create proxy.";
90 return DrawResult::kFail;
91 }
92
93 SkIRect texelSubset;
94 // Use a smaller subset when upscaling so that wrap is hit on all sides of the rect we
95 // will draw.
96 if (fUpscale) {
97 texelSubset = SkIRect::MakeXYWH(fBitmap.width()/3 - 1, 2*fBitmap.height()/5 - 1,
98 fBitmap.width()/4 + 2, fBitmap.height()/5 + 2);
99 } else {
100 texelSubset = SkIRect::MakeXYWH( fBitmap.width()/8 - 1, fBitmap.height()/7 - 1,
101 3*fBitmap.width()/5 + 2, 4*fBitmap.height()/5 + 2);
102 }
103
104 SkTArray<SkMatrix> textureMatrices;
105
106 SkRect a = SkRect::Make(texelSubset);
Brian Salomon63a04a82020-02-03 10:47:05 -0500107 SkRect b = fUpscale ? a.makeInset (.31f * a.width(), .31f * a.height())
Brian Salomonf0334042020-01-31 16:16:39 -0500108 : a.makeOutset(.25f * a.width(), .25f * a.height());
109 textureMatrices.push_back().setRectToRect(a, b, SkMatrix::kFill_ScaleToFit);
110
111 b = fUpscale ? a.makeInset (.25f * a.width(), .35f * a.height())
112 : a.makeOutset(.20f * a.width(), .35f * a.height());
113 textureMatrices.push_back().setRectToRect(a, b, SkMatrix::kFill_ScaleToFit);
114 textureMatrices.back().preRotate(45.f, a.centerX(), a.centerY());
115 textureMatrices.back().postSkew(.05f, -.05f);
116
Brian Salomonf0334042020-01-31 16:16:39 -0500117 SkBitmap subsetBmp;
118 fBitmap.extractSubset(&subsetBmp, texelSubset);
119 subsetBmp.setImmutable();
120 GrBitmapTextureMaker subsetMaker(context, subsetBmp);
Greg Danielc61d7e32020-02-04 14:27:45 -0500121 auto[subsetView, subsetCT] = subsetMaker.view(mipMapped);
Brian Salomonf0334042020-01-31 16:16:39 -0500122
123 SkRect localRect = SkRect::Make(fBitmap.bounds()).makeOutset(kDrawPad, kDrawPad);
124
125 auto size = this->onISize();
126
127 SkScalar y = kDrawPad + kTestPad;
128 SkRect drawRect;
129 for (int tm = 0; tm < textureMatrices.count(); ++tm) {
130 for (int mx = 0; mx < GrSamplerState::kWrapModeCount; ++mx) {
131 SkScalar x = kDrawPad + kTestPad;
132 auto wmx = static_cast<GrSamplerState::WrapMode>(mx);
133 if (fFilter != GrSamplerState::Filter::kNearest &&
134 (wmx == GrSamplerState::WrapMode::kRepeat ||
135 wmx == GrSamplerState::WrapMode::kMirrorRepeat)) {
136 // [Mirror] Repeat mode doesn't produce correct results with bilerp
137 // filtering
138 continue;
139 }
140 for (int my = 0; my < GrSamplerState::kWrapModeCount; ++my) {
141 auto wmy = static_cast<GrSamplerState::WrapMode>(my);
142 if (fFilter != GrSamplerState::Filter::kNearest &&
143 (wmy == GrSamplerState::WrapMode::kRepeat ||
144 wmy == GrSamplerState::WrapMode::kMirrorRepeat)) {
145 continue;
146 }
147 GrSamplerState sampler(wmx, wmy, fFilter);
148 const auto& caps = *context->priv().caps();
Greg Danield2ccbb52020-02-05 10:45:39 -0500149 auto fp1 = GrTextureEffect::MakeTexelSubset(view,
Brian Salomonf0334042020-01-31 16:16:39 -0500150 fBitmap.alphaType(),
151 textureMatrices[tm],
152 sampler,
153 texelSubset,
154 caps);
155 if (!fp1) {
156 continue;
157 }
158 drawRect = localRect.makeOffset(x, y);
159 // Throw a translate in the local matrix just to test having something other
160 // than identity. Compensate with an offset local rect.
161 static constexpr SkVector kT = {-100, 300};
162 if (auto op = sk_gpu_test::test_ops::MakeRect(context,
163 std::move(fp1),
164 drawRect,
165 localRect.makeOffset(kT),
166 SkMatrix::MakeTrans(-kT))) {
167 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
168 }
169 x += localRect.width() + kTestPad;
170
171 SkMatrix subsetTextureMatrix = SkMatrix::Concat(
172 SkMatrix::MakeTrans(-texelSubset.topLeft()), textureMatrices[tm]);
173
174 // Now draw with a subsetted proxy using fixed function texture sampling rather
175 // than a texture subset as a comparison.
176 drawRect = localRect.makeOffset(x, y);
Greg Danield2ccbb52020-02-05 10:45:39 -0500177 auto fp2 = GrTextureEffect::Make(subsetView, fBitmap.alphaType(),
178 subsetTextureMatrix,
Brian Salomonf0334042020-01-31 16:16:39 -0500179 GrSamplerState(wmx, wmy, fFilter), caps);
180 if (auto op = sk_gpu_test::test_ops::MakeRect(context, std::move(fp2), drawRect,
181 localRect)) {
182 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
183 }
184 if (my < GrSamplerState::kWrapModeCount - 1) {
Brian Salomonf1e316f2020-02-02 07:57:38 -0500185 SkScalar midX =
186 SkScalarFloorToScalar(drawRect.right() + kTestPad/2.f) + 0.5f;
187 canvas->drawLine({midX, -1}, {midX, (float)size.fHeight+1}, {});
Brian Salomonf0334042020-01-31 16:16:39 -0500188 }
189 x += localRect.width() + kTestPad;
190 }
191 if (mx < GrSamplerState::kWrapModeCount - 1) {
Brian Salomonf1e316f2020-02-02 07:57:38 -0500192 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f;
193 canvas->drawLine({-1, midY}, {(float)size.fWidth+1, midY}, {});
Brian Salomonf0334042020-01-31 16:16:39 -0500194 }
195 y += localRect.height() + kTestPad;
196 }
197 if (tm < textureMatrices.count() - 1) {
198 SkPaint paint;
199 paint.setColor(SK_ColorRED);
Brian Salomonf1e316f2020-02-02 07:57:38 -0500200 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f;
201 canvas->drawLine({-1, midY}, {(float)size.fWidth + 1, midY}, paint);
Brian Salomonf0334042020-01-31 16:16:39 -0500202 }
203 }
204 return DrawResult::kOk;
205 }
206
207private:
208 static constexpr SkISize kImageSize = {128, 88};
209 static constexpr SkScalar kDrawPad = 10.f;
210 static constexpr SkScalar kTestPad = 10.f;
211 SkBitmap fBitmap;
212 GrSamplerState::Filter fFilter;
213 bool fUpscale;
214
215 typedef GM INHERITED;
216};
217
218DEF_GM(return new TexelSubset(GrSamplerState::Filter::kNearest, false);)
219DEF_GM(return new TexelSubset(GrSamplerState::Filter::kNearest, true);)
220DEF_GM(return new TexelSubset(GrSamplerState::Filter::kBilerp , false);)
221DEF_GM(return new TexelSubset(GrSamplerState::Filter::kBilerp , true);)
222// It doesn't make sense to have upscaling MIP map.
223DEF_GM(return new TexelSubset(GrSamplerState::Filter::kMipMap, false);)
224
225}