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