1 /* 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/private/base/SkTArray.h" 20 #include "src/core/SkCanvasPriv.h" 21 #include "src/gpu/ganesh/GrCanvas.h" 22 #include "src/gpu/ganesh/GrCaps.h" 23 #include "src/gpu/ganesh/GrDirectContextPriv.h" 24 #include "src/gpu/ganesh/GrProxyProvider.h" 25 #include "src/gpu/ganesh/GrSamplerState.h" 26 #include "src/gpu/ganesh/SkGr.h" 27 #include "src/gpu/ganesh/SurfaceDrawContext.h" 28 #include "src/gpu/ganesh/effects/GrTextureEffect.h" 29 #include "tools/DecodeUtils.h" 30 #include "tools/Resources.h" 31 #include "tools/gpu/TestOps.h" 32 33 #include <memory> 34 #include <utility> 35 36 using namespace skia_private; 37 38 using MipmapMode = GrSamplerState::MipmapMode; 39 using Filter = GrSamplerState::Filter; 40 using Wrap = GrSamplerState::WrapMode; 41 42 namespace skiagm { 43 /** 44 * This GM directly exercises GrTextureEffect::MakeTexelSubset. 45 */ 46 class TexelSubset : public GpuGM { 47 public: TexelSubset(Filter filter,MipmapMode mm,bool upscale)48 TexelSubset(Filter filter, MipmapMode mm, bool upscale) 49 : fFilter(filter), fMipmapMode(mm), fUpscale(upscale) { 50 this->setBGColor(0xFFFFFFFF); 51 } 52 53 protected: getName() const54 SkString getName() const override { 55 SkString name("texel_subset"); 56 switch (fFilter) { 57 case Filter::kNearest: 58 name.append("_nearest"); 59 break; 60 case Filter::kLinear: 61 name.append("_linear"); 62 break; 63 } 64 switch (fMipmapMode) { 65 case MipmapMode::kNone: 66 break; 67 case MipmapMode::kNearest: 68 name.append("_mipmap_nearest"); 69 break; 70 case MipmapMode::kLinear: 71 name.append("_mipmap_linear"); 72 break; 73 } 74 name.append(fUpscale ? "_up" : "_down"); 75 return name; 76 } 77 getISize()78 SkISize getISize() override { 79 static constexpr int kN = GrSamplerState::kWrapModeCount; 80 int w = kTestPad + 2*kN*(kImageSize.width() + 2*kDrawPad + kTestPad); 81 int h = kTestPad + 2*kN*(kImageSize.height() + 2*kDrawPad + kTestPad); 82 return {w, h}; 83 } 84 onOnceBeforeDraw()85 void onOnceBeforeDraw() override { 86 SkAssertResult(ToolUtils::GetResourceAsBitmap("images/mandrill_128.png", &fBitmap)); 87 // Make the bitmap non-square to detect any width/height confusion. 88 fBitmap.extractSubset(&fBitmap, SkIRect::MakeSize(fBitmap.dimensions()).makeInset(0, 20)); 89 SkASSERT(fBitmap.dimensions() == kImageSize); 90 } 91 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)92 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override { 93 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas); 94 if (!sdc) { 95 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 96 return DrawResult::kSkip; 97 } 98 99 skgpu::Mipmapped mipmapped = 100 (fMipmapMode != MipmapMode::kNone) ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo; 101 if (mipmapped == skgpu::Mipmapped::kYes && !rContext->priv().caps()->mipmapSupport()) { 102 return DrawResult::kSkip; 103 } 104 auto view = std::get<0>(GrMakeCachedBitmapProxyView( 105 rContext, fBitmap, /*label=*/"DrawResult_Draw_BitMap", mipmapped)); 106 if (!view) { 107 *errorMsg = "Failed to create proxy."; 108 return DrawResult::kFail; 109 } 110 111 SkIRect texelSubset; 112 // Use a smaller subset when upscaling so that wrap is hit on all sides of the rect we 113 // will draw. 114 if (fUpscale) { 115 texelSubset = SkIRect::MakeXYWH(fBitmap.width()/3 - 1, 2*fBitmap.height()/5 - 1, 116 fBitmap.width()/4 + 2, fBitmap.height()/5 + 2); 117 } else { 118 texelSubset = SkIRect::MakeXYWH( fBitmap.width()/8 - 1, fBitmap.height()/7 - 1, 119 3*fBitmap.width()/5 + 2, 4*fBitmap.height()/5 + 2); 120 } 121 122 TArray<SkMatrix> textureMatrices; 123 124 SkRect a = SkRect::Make(texelSubset); 125 SkRect b = fUpscale ? a.makeInset (.31f * a.width(), .31f * a.height()) 126 : a.makeOutset(.25f * a.width(), .25f * a.height()); 127 textureMatrices.push_back() = SkMatrix::RectToRect(a, b); 128 129 b = fUpscale ? a.makeInset (.25f * a.width(), .35f * a.height()) 130 : a.makeOutset(.20f * a.width(), .35f * a.height()); 131 textureMatrices.push_back() = SkMatrix::RectToRect(a, b); 132 textureMatrices.back().preRotate(45.f, a.centerX(), a.centerY()); 133 textureMatrices.back().postSkew(.05f, -.05f); 134 135 SkBitmap subsetBmp; 136 fBitmap.extractSubset(&subsetBmp, texelSubset); 137 subsetBmp.setImmutable(); 138 auto subsetView = std::get<0>(GrMakeCachedBitmapProxyView( 139 rContext, subsetBmp, /*label=*/"DrawResult_Draw_SubsetBitMap", mipmapped)); 140 141 SkRect localRect = SkRect::Make(fBitmap.bounds()).makeOutset(kDrawPad, kDrawPad); 142 143 auto size = this->getISize(); 144 145 SkScalar y = kDrawPad + kTestPad; 146 SkRect drawRect; 147 for (int tm = 0; tm < textureMatrices.size(); ++tm) { 148 for (int my = 0; my < GrSamplerState::kWrapModeCount; ++my) { 149 SkScalar x = kDrawPad + kTestPad; 150 auto wmy = static_cast<Wrap>(my); 151 for (int mx = 0; mx < GrSamplerState::kWrapModeCount; ++mx) { 152 auto wmx = static_cast<Wrap>(mx); 153 154 const auto& caps = *rContext->priv().caps(); 155 156 GrSamplerState sampler(wmx, wmy, fFilter, fMipmapMode); 157 158 drawRect = localRect.makeOffset(x, y); 159 160 std::unique_ptr<GrFragmentProcessor> fp1; 161 fp1 = GrTextureEffect::MakeSubset(view, 162 fBitmap.alphaType(), 163 textureMatrices[tm], 164 sampler, 165 SkRect::Make(texelSubset), 166 caps); 167 if (!fp1) { 168 continue; 169 } 170 171 // Throw a translate in the local matrix just to test having something other 172 // than identity. Compensate with an offset local rect. 173 static constexpr SkVector kT = {-100, 300}; 174 if (auto op = sk_gpu_test::test_ops::MakeRect(rContext, 175 std::move(fp1), 176 drawRect, 177 localRect.makeOffset(kT), 178 SkMatrix::Translate(-kT))) { 179 sdc->addDrawOp(std::move(op)); 180 } 181 182 x += localRect.width() + kTestPad; 183 184 // Now draw with a subsetted proxy using fixed function texture sampling 185 // rather than a texture subset as a comparison. 186 drawRect = localRect.makeOffset(x, y); 187 SkMatrix subsetTextureMatrix = SkMatrix::Concat( 188 SkMatrix::Translate(-texelSubset.topLeft()), textureMatrices[tm]); 189 190 auto fp2 = GrTextureEffect::Make(subsetView, 191 fBitmap.alphaType(), 192 subsetTextureMatrix, 193 sampler, 194 caps); 195 if (auto op = sk_gpu_test::test_ops::MakeRect(rContext, std::move(fp2), 196 drawRect, localRect)) { 197 sdc->addDrawOp(std::move(op)); 198 } 199 200 if (mx < GrSamplerState::kWrapModeCount - 1) { 201 SkScalar midX = 202 SkScalarFloorToScalar(drawRect.right() + kTestPad/2.f) + 0.5f; 203 canvas->drawLine({midX, -1}, {midX, (float)size.fHeight+1}, {}); 204 } 205 x += localRect.width() + kTestPad; 206 } 207 if (my < GrSamplerState::kWrapModeCount - 1) { 208 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f; 209 canvas->drawLine({-1, midY}, {(float)size.fWidth+1, midY}, {}); 210 } 211 y += localRect.height() + kTestPad; 212 } 213 if (tm < textureMatrices.size() - 1) { 214 SkPaint paint; 215 paint.setColor(SK_ColorRED); 216 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f; 217 canvas->drawLine({-1, midY}, {(float)size.fWidth + 1, midY}, paint); 218 } 219 } 220 return DrawResult::kOk; 221 } 222 223 private: 224 inline static constexpr SkISize kImageSize = {128, 88}; 225 inline static constexpr SkScalar kDrawPad = 10.f; 226 inline static constexpr SkScalar kTestPad = 10.f; 227 SkBitmap fBitmap; 228 Filter fFilter; 229 MipmapMode fMipmapMode; 230 bool fUpscale; 231 232 using INHERITED = GM; 233 }; 234 235 DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kNone , false);) 236 DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kNone , true );) 237 DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kNone , false);) 238 DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kNone , true );) 239 // It doesn't make sense to have upscaling MIP map. 240 DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kNearest, false);) 241 DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kNearest, false);) 242 DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kLinear , false);) 243 DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kLinear , false);) 244 245 } // namespace skiagm 246