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