239 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright 2014 Google Inc.
 | |
|  *
 | |
|  * Use of this source code is governed by a BSD-style license that can be
 | |
|  * found in the LICENSE file.
 | |
|  */
 | |
| 
 | |
| // This test only works with the GPU backend.
 | |
| 
 | |
| #include "gm/gm.h"
 | |
| #include "include/core/SkBitmap.h"
 | |
| #include "include/core/SkCanvas.h"
 | |
| #include "include/core/SkColor.h"
 | |
| #include "include/core/SkMatrix.h"
 | |
| #include "include/core/SkPaint.h"
 | |
| #include "include/core/SkRect.h"
 | |
| #include "include/core/SkSize.h"
 | |
| #include "include/core/SkString.h"
 | |
| #include "include/private/SkTArray.h"
 | |
| #include "src/core/SkCanvasPriv.h"
 | |
| #include "src/gpu/GrDirectContextPriv.h"
 | |
| #include "src/gpu/GrProxyProvider.h"
 | |
| #include "src/gpu/GrSamplerState.h"
 | |
| #include "src/gpu/SkGr.h"
 | |
| #include "src/gpu/effects/GrTextureEffect.h"
 | |
| #include "src/gpu/v1/SurfaceDrawContext_v1.h"
 | |
| #include "tools/Resources.h"
 | |
| #include "tools/gpu/TestOps.h"
 | |
| 
 | |
| #include <memory>
 | |
| #include <utility>
 | |
| 
 | |
| using MipmapMode = GrSamplerState::MipmapMode;
 | |
| using Filter     = GrSamplerState::Filter;
 | |
| using Wrap       = GrSamplerState::WrapMode;
 | |
| 
 | |
| namespace skiagm {
 | |
| /**
 | |
|  * This GM directly exercises GrTextureEffect::MakeTexelSubset.
 | |
|  */
 | |
| class TexelSubset : public GpuGM {
 | |
| public:
 | |
|     TexelSubset(Filter filter, MipmapMode mm, bool upscale)
 | |
|             : fFilter(filter), fMipmapMode(mm), fUpscale(upscale) {
 | |
|         this->setBGColor(0xFFFFFFFF);
 | |
|     }
 | |
| 
 | |
| protected:
 | |
|     SkString onShortName() override {
 | |
|         SkString name("texel_subset");
 | |
|         switch (fFilter) {
 | |
|             case Filter::kNearest:
 | |
|                 name.append("_nearest");
 | |
|                 break;
 | |
|             case Filter::kLinear:
 | |
|                 name.append("_linear");
 | |
|                 break;
 | |
|         }
 | |
|         switch (fMipmapMode) {
 | |
|             case MipmapMode::kNone:
 | |
|                 break;
 | |
|             case MipmapMode::kNearest:
 | |
|                 name.append("_mipmap_nearest");
 | |
|                 break;
 | |
|             case MipmapMode::kLinear:
 | |
|                 name.append("_mipmap_linear");
 | |
|                 break;
 | |
|         }
 | |
|         name.append(fUpscale ? "_up" : "_down");
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     SkISize onISize() override {
 | |
|         static constexpr int kN = GrSamplerState::kWrapModeCount;
 | |
|         int w = kTestPad + 2*kN*(kImageSize.width()  + 2*kDrawPad + kTestPad);
 | |
|         int h = kTestPad + 2*kN*(kImageSize.height() + 2*kDrawPad + kTestPad);
 | |
|         return {w, h};
 | |
|     }
 | |
| 
 | |
|     void onOnceBeforeDraw() override {
 | |
|         GetResourceAsBitmap("images/mandrill_128.png", &fBitmap);
 | |
|         // Make the bitmap non-square to detect any width/height confusion.
 | |
|         fBitmap.extractSubset(&fBitmap, SkIRect::MakeSize(fBitmap.dimensions()).makeInset(0, 20));
 | |
|         SkASSERT(fBitmap.dimensions() == kImageSize);
 | |
|     }
 | |
| 
 | |
|     DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override {
 | |
|         auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
 | |
|         if (!sdc) {
 | |
|             *errorMsg = kErrorMsg_DrawSkippedGpuOnly;
 | |
|             return DrawResult::kSkip;
 | |
|         }
 | |
| 
 | |
|         GrMipmapped mipmapped = (fMipmapMode != MipmapMode::kNone) ? GrMipmapped::kYes
 | |
|                                                                    : GrMipmapped::kNo;
 | |
|         if (mipmapped == GrMipmapped::kYes && !rContext->priv().caps()->mipmapSupport()) {
 | |
|             return DrawResult::kSkip;
 | |
|         }
 | |
|         auto view = std::get<0>(GrMakeCachedBitmapProxyView(rContext, fBitmap, mipmapped));
 | |
|         if (!view) {
 | |
|             *errorMsg = "Failed to create proxy.";
 | |
|             return DrawResult::kFail;
 | |
|         }
 | |
| 
 | |
|         SkIRect texelSubset;
 | |
|         // Use a smaller subset when upscaling so that wrap is hit on all sides of the rect we
 | |
|         // will draw.
 | |
|         if (fUpscale) {
 | |
|             texelSubset = SkIRect::MakeXYWH(fBitmap.width()/3 - 1, 2*fBitmap.height()/5 - 1,
 | |
|                                             fBitmap.width()/4 + 2,   fBitmap.height()/5 + 2);
 | |
|         } else {
 | |
|             texelSubset = SkIRect::MakeXYWH(  fBitmap.width()/8 - 1,   fBitmap.height()/7 - 1,
 | |
|                                             3*fBitmap.width()/5 + 2, 4*fBitmap.height()/5 + 2);
 | |
|         }
 | |
| 
 | |
|         SkTArray<SkMatrix> textureMatrices;
 | |
| 
 | |
|         SkRect a = SkRect::Make(texelSubset);
 | |
|         SkRect b = fUpscale ? a.makeInset (.31f * a.width(), .31f * a.height())
 | |
|                             : a.makeOutset(.25f * a.width(), .25f * a.height());
 | |
|         textureMatrices.push_back() = SkMatrix::RectToRect(a, b);
 | |
| 
 | |
|         b = fUpscale ? a.makeInset (.25f * a.width(), .35f * a.height())
 | |
|                      : a.makeOutset(.20f * a.width(), .35f * a.height());
 | |
|         textureMatrices.push_back() = SkMatrix::RectToRect(a, b);
 | |
|         textureMatrices.back().preRotate(45.f, a.centerX(), a.centerY());
 | |
|         textureMatrices.back().postSkew(.05f, -.05f);
 | |
| 
 | |
|         SkBitmap subsetBmp;
 | |
|         fBitmap.extractSubset(&subsetBmp, texelSubset);
 | |
|         subsetBmp.setImmutable();
 | |
|         auto subsetView = std::get<0>(GrMakeCachedBitmapProxyView(rContext, subsetBmp, mipmapped));
 | |
| 
 | |
|         SkRect localRect = SkRect::Make(fBitmap.bounds()).makeOutset(kDrawPad, kDrawPad);
 | |
| 
 | |
|         auto size = this->onISize();
 | |
| 
 | |
|         SkScalar y = kDrawPad + kTestPad;
 | |
|         SkRect drawRect;
 | |
|         for (int tm = 0; tm < textureMatrices.count(); ++tm) {
 | |
|             for (int my = 0; my < GrSamplerState::kWrapModeCount; ++my) {
 | |
|                 SkScalar x = kDrawPad + kTestPad;
 | |
|                 auto wmy = static_cast<Wrap>(my);
 | |
|                 for (int mx = 0; mx < GrSamplerState::kWrapModeCount; ++mx) {
 | |
|                     auto wmx = static_cast<Wrap>(mx);
 | |
| 
 | |
|                     const auto& caps = *rContext->priv().caps();
 | |
| 
 | |
|                     GrSamplerState sampler(wmx, wmy, fFilter, fMipmapMode);
 | |
| 
 | |
|                     drawRect = localRect.makeOffset(x, y);
 | |
| 
 | |
|                     std::unique_ptr<GrFragmentProcessor> fp1;
 | |
|                     fp1 = GrTextureEffect::MakeSubset(view,
 | |
|                                                       fBitmap.alphaType(),
 | |
|                                                       textureMatrices[tm],
 | |
|                                                       sampler,
 | |
|                                                       SkRect::Make(texelSubset),
 | |
|                                                       caps);
 | |
|                     if (!fp1) {
 | |
|                         continue;
 | |
|                     }
 | |
| 
 | |
|                     // Throw a translate in the local matrix just to test having something other
 | |
|                     // than identity. Compensate with an offset local rect.
 | |
|                     static constexpr SkVector kT = {-100, 300};
 | |
|                     if (auto op = sk_gpu_test::test_ops::MakeRect(rContext,
 | |
|                                                                   std::move(fp1),
 | |
|                                                                   drawRect,
 | |
|                                                                   localRect.makeOffset(kT),
 | |
|                                                                   SkMatrix::Translate(-kT))) {
 | |
|                         sdc->addDrawOp(std::move(op));
 | |
|                     }
 | |
| 
 | |
|                     x += localRect.width() + kTestPad;
 | |
| 
 | |
|                     // Now draw with a subsetted proxy using fixed function texture sampling
 | |
|                     // rather than a texture subset as a comparison.
 | |
|                     drawRect = localRect.makeOffset(x, y);
 | |
|                     SkMatrix subsetTextureMatrix = SkMatrix::Concat(
 | |
|                             SkMatrix::Translate(-texelSubset.topLeft()), textureMatrices[tm]);
 | |
| 
 | |
|                     auto fp2 = GrTextureEffect::Make(subsetView,
 | |
|                                                      fBitmap.alphaType(),
 | |
|                                                      subsetTextureMatrix,
 | |
|                                                      sampler,
 | |
|                                                      caps);
 | |
|                     if (auto op = sk_gpu_test::test_ops::MakeRect(rContext, std::move(fp2),
 | |
|                                                                   drawRect, localRect)) {
 | |
|                         sdc->addDrawOp(std::move(op));
 | |
|                     }
 | |
| 
 | |
|                     if (mx < GrSamplerState::kWrapModeCount - 1) {
 | |
|                         SkScalar midX =
 | |
|                                 SkScalarFloorToScalar(drawRect.right() + kTestPad/2.f) + 0.5f;
 | |
|                         canvas->drawLine({midX, -1}, {midX, (float)size.fHeight+1}, {});
 | |
|                     }
 | |
|                     x += localRect.width() + kTestPad;
 | |
|                 }
 | |
|                 if (my < GrSamplerState::kWrapModeCount - 1) {
 | |
|                     SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f;
 | |
|                     canvas->drawLine({-1, midY}, {(float)size.fWidth+1, midY}, {});
 | |
|                 }
 | |
|                 y += localRect.height() + kTestPad;
 | |
|             }
 | |
|             if (tm < textureMatrices.count() - 1) {
 | |
|                 SkPaint paint;
 | |
|                 paint.setColor(SK_ColorRED);
 | |
|                 SkScalar midY = SkScalarFloorToScalar(drawRect.bottom() + kTestPad/2.f) + 0.5f;
 | |
|                 canvas->drawLine({-1, midY}, {(float)size.fWidth + 1, midY}, paint);
 | |
|             }
 | |
|         }
 | |
|         return DrawResult::kOk;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     inline static constexpr SkISize kImageSize = {128, 88};
 | |
|     inline static constexpr SkScalar kDrawPad = 10.f;
 | |
|     inline static constexpr SkScalar kTestPad = 10.f;
 | |
|     SkBitmap fBitmap;
 | |
|     Filter fFilter;
 | |
|     MipmapMode fMipmapMode;
 | |
|     bool fUpscale;
 | |
| 
 | |
|     using INHERITED = GM;
 | |
| };
 | |
| 
 | |
| DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kNone   , false);)
 | |
| DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kNone   , true );)
 | |
| DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kNone   , false);)
 | |
| DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kNone   , true );)
 | |
| // It doesn't make sense to have upscaling MIP map.
 | |
| DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kNearest, false);)
 | |
| DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kNearest, false);)
 | |
| DEF_GM(return new TexelSubset(Filter::kNearest, MipmapMode::kLinear , false);)
 | |
| DEF_GM(return new TexelSubset(Filter::kLinear , MipmapMode::kLinear , false);)
 | |
| 
 | |
| }  // namespace skiagm
 |