blob: dbf092e175f7a1a1e8f6beb98cbc809ab0e84f05 [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"
Brian Salomon16033c92020-02-13 11:25:11 -050029#include "src/gpu/effects/generated/GrConstColorProcessor.h"
Brian Salomonf0334042020-01-31 16:16:39 -050030#include "tools/Resources.h"
31#include "tools/gpu/TestOps.h"
32
33#include <memory>
34#include <utility>
35
36namespace skiagm {
37/**
38 * This GM directly exercises GrTextureEffect::MakeTexelSubset.
39 */
40class TexelSubset : public GpuGM {
41public:
42 TexelSubset(GrSamplerState::Filter filter, bool upscale) : fFilter(filter), fUpscale(upscale) {
43 this->setBGColor(0xFFFFFFFF);
44 }
45
46protected:
47 SkString onShortName() override {
48 SkString name("texel_subset");
49 switch (fFilter) {
50 case GrSamplerState::Filter::kNearest:
51 name.append("_nearest");
52 break;
53 case GrSamplerState::Filter::kBilerp:
54 name.append("_bilerp");
55 break;
56 case GrSamplerState::Filter::kMipMap:
57 name.append("_mip_map");
58 break;
59 }
60 name.append(fUpscale ? "_up" : "_down");
61 return name;
62 }
63
64 SkISize onISize() override {
Brian Salomon16033c92020-02-13 11:25:11 -050065 static constexpr int kN = GrSamplerState::kWrapModeCount;
Brian Salomonf46f19b2020-02-13 14:11:28 -050066 int w = kTestPad + 2*kN*(kImageSize.width() + 2*kDrawPad + kTestPad);
67 int h = kTestPad + 2*kN*(kImageSize.height() + 2*kDrawPad + kTestPad);
Brian Salomonf0334042020-01-31 16:16:39 -050068 return {w, h};
69 }
70
71 void onOnceBeforeDraw() override {
72 GetResourceAsBitmap("images/mandrill_128.png", &fBitmap);
73 // Make the bitmap non-square to detect any width/height confusion.
74 fBitmap.extractSubset(&fBitmap, SkIRect::MakeSize(fBitmap.dimensions()).makeInset(0, 20));
75 SkASSERT(fBitmap.dimensions() == kImageSize);
76 }
77
Robert Phillips95c250c2020-06-29 15:36:12 -040078 DrawResult onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
Brian Salomonf0334042020-01-31 16:16:39 -050079 SkCanvas* canvas, SkString* errorMsg) override {
80 GrMipMapped mipMapped = fFilter == GrSamplerState::Filter::kMipMap &&
81 context->priv().caps()->mipMapSupport()
82 ? GrMipMapped::kYes : GrMipMapped::kNo;
Brian Salomonbc074a62020-03-18 10:06:13 -040083 GrBitmapTextureMaker maker(context, fBitmap, GrImageTexGenPolicy::kDraw);
Brian Salomonecbb0fb2020-02-28 18:07:32 -050084 auto view = maker.view(mipMapped);
85 if (!view) {
Brian Salomonf0334042020-01-31 16:16:39 -050086 *errorMsg = "Failed to create proxy.";
87 return DrawResult::kFail;
88 }
89
90 SkIRect texelSubset;
91 // Use a smaller subset when upscaling so that wrap is hit on all sides of the rect we
92 // will draw.
93 if (fUpscale) {
94 texelSubset = SkIRect::MakeXYWH(fBitmap.width()/3 - 1, 2*fBitmap.height()/5 - 1,
95 fBitmap.width()/4 + 2, fBitmap.height()/5 + 2);
96 } else {
97 texelSubset = SkIRect::MakeXYWH( fBitmap.width()/8 - 1, fBitmap.height()/7 - 1,
98 3*fBitmap.width()/5 + 2, 4*fBitmap.height()/5 + 2);
99 }
100
101 SkTArray<SkMatrix> textureMatrices;
102
103 SkRect a = SkRect::Make(texelSubset);
Brian Salomon63a04a82020-02-03 10:47:05 -0500104 SkRect b = fUpscale ? a.makeInset (.31f * a.width(), .31f * a.height())
Brian Salomonf0334042020-01-31 16:16:39 -0500105 : a.makeOutset(.25f * a.width(), .25f * a.height());
106 textureMatrices.push_back().setRectToRect(a, b, SkMatrix::kFill_ScaleToFit);
107
108 b = fUpscale ? a.makeInset (.25f * a.width(), .35f * a.height())
109 : a.makeOutset(.20f * a.width(), .35f * a.height());
110 textureMatrices.push_back().setRectToRect(a, b, SkMatrix::kFill_ScaleToFit);
111 textureMatrices.back().preRotate(45.f, a.centerX(), a.centerY());
112 textureMatrices.back().postSkew(.05f, -.05f);
113
Brian Salomonf0334042020-01-31 16:16:39 -0500114 SkBitmap subsetBmp;
115 fBitmap.extractSubset(&subsetBmp, texelSubset);
116 subsetBmp.setImmutable();
Brian Salomonbc074a62020-03-18 10:06:13 -0400117 GrBitmapTextureMaker subsetMaker(context, subsetBmp, GrImageTexGenPolicy::kDraw);
Brian Salomonecbb0fb2020-02-28 18:07:32 -0500118 auto subsetView = subsetMaker.view(mipMapped);
Brian Salomonf0334042020-01-31 16:16:39 -0500119
120 SkRect localRect = SkRect::Make(fBitmap.bounds()).makeOutset(kDrawPad, kDrawPad);
121
122 auto size = this->onISize();
123
124 SkScalar y = kDrawPad + kTestPad;
125 SkRect drawRect;
126 for (int tm = 0; tm < textureMatrices.count(); ++tm) {
Brian Salomon16033c92020-02-13 11:25:11 -0500127 for (int my = 0; my < GrSamplerState::kWrapModeCount; ++my) {
Brian Salomonf0334042020-01-31 16:16:39 -0500128 SkScalar x = kDrawPad + kTestPad;
Brian Salomon16033c92020-02-13 11:25:11 -0500129 auto wmy = static_cast<GrSamplerState::WrapMode>(my);
130 for (int mx = 0; mx < GrSamplerState::kWrapModeCount; ++mx) {
131 auto wmx = static_cast<GrSamplerState::WrapMode>(mx);
132
Brian Salomonf0334042020-01-31 16:16:39 -0500133 const auto& caps = *context->priv().caps();
Brian Salomon16033c92020-02-13 11:25:11 -0500134
135 GrSamplerState sampler(wmx, wmy, fFilter);
136
Brian Salomonf0334042020-01-31 16:16:39 -0500137 drawRect = localRect.makeOffset(x, y);
Brian Salomon16033c92020-02-13 11:25:11 -0500138
139 std::unique_ptr<GrFragmentProcessor> fp1;
Brian Salomonb3779f52020-02-14 11:22:52 -0500140 fp1 = GrTextureEffect::MakeSubset(view,
141 fBitmap.alphaType(),
142 textureMatrices[tm],
143 sampler,
144 SkRect::Make(texelSubset),
145 caps);
Brian Salomonf46f19b2020-02-13 14:11:28 -0500146 if (!fp1) {
147 continue;
Brian Salomon16033c92020-02-13 11:25:11 -0500148 }
Brian Salomonf46f19b2020-02-13 14:11:28 -0500149
Brian Salomonf0334042020-01-31 16:16:39 -0500150 // Throw a translate in the local matrix just to test having something other
151 // than identity. Compensate with an offset local rect.
152 static constexpr SkVector kT = {-100, 300};
153 if (auto op = sk_gpu_test::test_ops::MakeRect(context,
154 std::move(fp1),
155 drawRect,
156 localRect.makeOffset(kT),
Mike Reed1f607332020-05-21 12:11:27 -0400157 SkMatrix::Translate(-kT))) {
Brian Salomonf0334042020-01-31 16:16:39 -0500158 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
159 }
Brian Salomon16033c92020-02-13 11:25:11 -0500160
Brian Salomonf0334042020-01-31 16:16:39 -0500161 x += localRect.width() + kTestPad;
162
Brian Salomon16033c92020-02-13 11:25:11 -0500163 // Now draw with a subsetted proxy using fixed function texture sampling
164 // rather than a texture subset as a comparison.
165 drawRect = localRect.makeOffset(x, y);
Brian Salomonf0334042020-01-31 16:16:39 -0500166 SkMatrix subsetTextureMatrix = SkMatrix::Concat(
Mike Reed1f607332020-05-21 12:11:27 -0400167 SkMatrix::Translate(-texelSubset.topLeft()), textureMatrices[tm]);
Brian Salomonf0334042020-01-31 16:16:39 -0500168
Greg Danield2ccbb52020-02-05 10:45:39 -0500169 auto fp2 = GrTextureEffect::Make(subsetView, fBitmap.alphaType(),
170 subsetTextureMatrix,
Brian Salomonf0334042020-01-31 16:16:39 -0500171 GrSamplerState(wmx, wmy, fFilter), caps);
172 if (auto op = sk_gpu_test::test_ops::MakeRect(context, std::move(fp2), drawRect,
173 localRect)) {
174 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
175 }
Brian Salomon16033c92020-02-13 11:25:11 -0500176
177 if (mx < GrSamplerState::kWrapModeCount - 1) {
Brian Salomonf1e316f2020-02-02 07:57:38 -0500178 SkScalar midX =
179 SkScalarFloorToScalar(drawRect.right() + kTestPad/2.f) + 0.5f;
180 canvas->drawLine({midX, -1}, {midX, (float)size.fHeight+1}, {});
Brian Salomonf0334042020-01-31 16:16:39 -0500181 }
182 x += localRect.width() + kTestPad;
183 }
Brian Salomon16033c92020-02-13 11:25:11 -0500184 if (my < GrSamplerState::kWrapModeCount - 1) {
Brian Salomonf1e316f2020-02-02 07:57:38 -0500185 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f;
186 canvas->drawLine({-1, midY}, {(float)size.fWidth+1, midY}, {});
Brian Salomonf0334042020-01-31 16:16:39 -0500187 }
188 y += localRect.height() + kTestPad;
189 }
190 if (tm < textureMatrices.count() - 1) {
191 SkPaint paint;
192 paint.setColor(SK_ColorRED);
Brian Salomonf1e316f2020-02-02 07:57:38 -0500193 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f;
194 canvas->drawLine({-1, midY}, {(float)size.fWidth + 1, midY}, paint);
Brian Salomonf0334042020-01-31 16:16:39 -0500195 }
196 }
197 return DrawResult::kOk;
198 }
199
200private:
201 static constexpr SkISize kImageSize = {128, 88};
202 static constexpr SkScalar kDrawPad = 10.f;
203 static constexpr SkScalar kTestPad = 10.f;
204 SkBitmap fBitmap;
205 GrSamplerState::Filter fFilter;
206 bool fUpscale;
207
208 typedef GM INHERITED;
209};
210
211DEF_GM(return new TexelSubset(GrSamplerState::Filter::kNearest, false);)
212DEF_GM(return new TexelSubset(GrSamplerState::Filter::kNearest, true);)
213DEF_GM(return new TexelSubset(GrSamplerState::Filter::kBilerp , false);)
214DEF_GM(return new TexelSubset(GrSamplerState::Filter::kBilerp , true);)
215// It doesn't make sense to have upscaling MIP map.
216DEF_GM(return new TexelSubset(GrSamplerState::Filter::kMipMap, false);)
217
218}