1 /* 2 * Copyright 2012 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 #include "gm/gm.h" 9 #include "include/core/SkBitmap.h" 10 #include "include/core/SkCanvas.h" 11 #include "include/core/SkColor.h" 12 #include "include/core/SkFont.h" 13 #include "include/core/SkImageFilter.h" 14 #include "include/core/SkPaint.h" 15 #include "include/core/SkPoint.h" 16 #include "include/core/SkRect.h" 17 #include "include/core/SkScalar.h" 18 #include "include/core/SkShader.h" 19 #include "include/core/SkSize.h" 20 #include "include/core/SkString.h" 21 #include "include/core/SkTileMode.h" 22 #include "include/core/SkTypeface.h" 23 #include "include/effects/SkGradientShader.h" 24 #include "include/effects/SkImageFilters.h" 25 #include "src/gpu/BlurUtils.h" 26 #include "tools/ToolUtils.h" 27 #include "tools/fonts/FontToolUtils.h" 28 29 #include <vector> 30 31 namespace skiagm { 32 33 enum KernelFixture { 34 kBasic_KernelFixture, 35 kLarge_KernelFixture, 36 kLarger_KernelFixture, 37 kLargest_KernelFixture, 38 }; 39 40 class MatrixConvolutionGM : public GM { 41 public: MatrixConvolutionGM(SkColor colorOne,SkColor colorTwo,KernelFixture kernelFixture,const char * nameSuffix)42 MatrixConvolutionGM(SkColor colorOne, SkColor colorTwo, KernelFixture kernelFixture, const char* nameSuffix) 43 : fNameSuffix(nameSuffix), 44 fKernelFixture(kernelFixture) { 45 this->setBGColor(0x00000000); 46 fColors[0] = colorOne; 47 fColors[1] = colorTwo; 48 } 49 50 protected: runAsBench() const51 bool runAsBench() const override { return true; } 52 getName() const53 SkString getName() const override { return SkStringPrintf("matrixconvolution%s", fNameSuffix); } 54 makeBitmap()55 void makeBitmap() { 56 // Draw our bitmap in N32, so legacy devices get "premul" values they understand 57 auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(80, 80)); 58 SkPaint paint; 59 paint.setColor(0xFFFFFFFF); 60 SkPoint pts[2] = { {0, 0}, 61 {0, 80.0f} }; 62 SkScalar pos[2] = { 0, 80.0f }; 63 paint.setShader(SkGradientShader::MakeLinear( 64 pts, fColors, pos, 2, SkTileMode::kClamp)); 65 SkFont font(ToolUtils::DefaultPortableTypeface(), 180.0f); 66 surf->getCanvas()->drawString("e", -10.0f, 80.0f, font, paint); 67 fImage = surf->makeImageSnapshot(); 68 } 69 getISize()70 SkISize getISize() override { return SkISize::Make(500, 300); } 71 makeFilter(const SkIPoint & kernelOffsetIn,SkTileMode tileMode,bool convolveAlpha)72 sk_sp<SkImageFilter> makeFilter(const SkIPoint &kernelOffsetIn, 73 SkTileMode tileMode, 74 bool convolveAlpha) { 75 // The kernelOffset is specified in a 0..2 coordinate space. 76 float normalizedXOffset = kernelOffsetIn.fX / 2.0f; 77 float normalizedYOffset = kernelOffsetIn.fY / 2.0f; 78 // Must provide a cropping geometry in order for 'tileMode' to be well defined. 79 SkIRect tileBoundary = fImage->bounds(); 80 switch (fKernelFixture) { 81 case kBasic_KernelFixture: { 82 SkIPoint kernelOffset {SkScalarRoundToInt(2*normalizedXOffset), 83 SkScalarRoundToInt(2*normalizedYOffset)}; 84 // All 1s except center value, which is -7 (sum of 1). 85 std::vector<SkScalar> kernel(9, SkIntToScalar(1)); 86 kernel[4] = SkIntToScalar(-7); 87 return SkImageFilters::MatrixConvolution( 88 {3,3}, kernel.data(), /* gain= */ 0.3f, /* bias= */ 100.0f, 89 kernelOffset, tileMode, convolveAlpha, nullptr, tileBoundary); 90 } 91 case kLarge_KernelFixture: { 92 SkIPoint kernelOffset {SkScalarRoundToInt(6*normalizedXOffset), 93 SkScalarRoundToInt(6*normalizedYOffset)}; 94 // This ensures the texture fallback path will be taken 95 static_assert(49 > skgpu::kMaxBlurSamples); 96 // All 1s except center value, which is -47 (sum of 1). 97 std::vector<SkScalar> kernel(49, SkIntToScalar(1)); 98 kernel[24] = SkIntToScalar(-47); 99 return SkImageFilters::MatrixConvolution( 100 {7,7}, kernel.data(), /* gain= */ 0.3f, /* bias= */ 100.0f, 101 kernelOffset, tileMode, convolveAlpha, nullptr, tileBoundary); 102 } 103 case kLarger_KernelFixture: { 104 SkIPoint kernelOffset {SkScalarRoundToInt(127*normalizedXOffset), 0}; 105 // This ensures the texture fallback path will be taken 106 static_assert(128 > skgpu::kMaxBlurSamples); 107 std::vector<float> kernel(128, 0.0f); 108 kernel[64] = 0.5f; 109 kernel[65] = -0.5f; 110 return SkImageFilters::MatrixConvolution( 111 {128,1}, kernel.data(), /* gain= */ 0.3f, /* bias= */ 100.0f, 112 kernelOffset, tileMode, convolveAlpha, nullptr, tileBoundary); 113 } 114 case kLargest_KernelFixture: { 115 SkIPoint kernelOffset {0, SkScalarRoundToInt(254*normalizedYOffset)}; 116 // This ensures the texture fallback path will be taken 117 static_assert(255 > skgpu::kMaxBlurSamples); 118 std::vector<float> kernel(255, 0.0f); 119 kernel[126] = 0.5f; 120 kernel[128] = -0.5f; 121 return SkImageFilters::MatrixConvolution( 122 {1,255}, kernel.data(), /* gain= */ 0.3f, /* bias= */ 100.0f, 123 kernelOffset, tileMode, convolveAlpha, nullptr, tileBoundary); 124 } 125 default: 126 return nullptr; 127 } 128 } 129 draw(SkCanvas * canvas,int x,int y,const SkIPoint & kernelOffset,SkTileMode tileMode,bool convolveAlpha,const SkIRect * cropRect=nullptr)130 void draw(SkCanvas* canvas, int x, int y, const SkIPoint& kernelOffset, 131 SkTileMode tileMode, bool convolveAlpha, 132 const SkIRect* cropRect = nullptr) { 133 SkPaint paint; 134 auto filter = this->makeFilter(kernelOffset, tileMode, convolveAlpha); 135 if (cropRect) { 136 filter = SkImageFilters::Crop(SkRect::Make(*cropRect), std::move(filter)); 137 } 138 paint.setImageFilter(std::move(filter)); 139 canvas->save(); 140 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 141 canvas->drawImage(fImage, 0, 0, {}, &paint); 142 canvas->restore(); 143 } 144 onOnceBeforeDraw()145 void onOnceBeforeDraw() override { 146 this->makeBitmap(); 147 } 148 onDraw(SkCanvas * canvas)149 void onDraw(SkCanvas* canvas) override { 150 canvas->clear(SK_ColorBLACK); 151 SkIPoint kernelOffset = SkIPoint::Make(1, 0); 152 for (int x = 10; x < 310; x += 100) { 153 this->draw(canvas, x, 10, kernelOffset, SkTileMode::kClamp, true); 154 this->draw(canvas, x, 110, kernelOffset, SkTileMode::kDecal, true); 155 this->draw(canvas, x, 210, kernelOffset, SkTileMode::kRepeat, true); 156 kernelOffset.fY++; 157 } 158 kernelOffset.fY = 1; 159 SkIRect smallRect = SkIRect::MakeXYWH(10, 5, 60, 60); 160 this->draw(canvas, 310, 10, kernelOffset, SkTileMode::kClamp, true, &smallRect); 161 this->draw(canvas, 310, 110, kernelOffset, SkTileMode::kDecal, true, &smallRect); 162 this->draw(canvas, 310, 210, kernelOffset, SkTileMode::kRepeat, true, &smallRect); 163 164 this->draw(canvas, 410, 10, kernelOffset, SkTileMode::kClamp, false); 165 this->draw(canvas, 410, 110, kernelOffset, SkTileMode::kDecal, false); 166 this->draw(canvas, 410, 210, kernelOffset, SkTileMode::kRepeat, false); 167 } 168 169 private: 170 sk_sp<SkImage> fImage; 171 SkColor fColors[2]; 172 const char* fNameSuffix; 173 KernelFixture fKernelFixture; 174 175 using INHERITED = GM; 176 }; 177 178 ////////////////////////////////////////////////////////////////////////////// 179 180 DEF_GM(return new MatrixConvolutionGM(0xFFFFFFFF, 0x40404040, KernelFixture::kBasic_KernelFixture, "");) 181 DEF_GM(return new MatrixConvolutionGM(0xFFFF0000, 0xFF00FF00, KernelFixture::kBasic_KernelFixture, "_color");) 182 DEF_GM(return new MatrixConvolutionGM(0xFFFFFFFF, 0x40404040, KernelFixture::kLarge_KernelFixture, "_big");) 183 DEF_GM(return new MatrixConvolutionGM(0xFFFF0000, 0xFF00FF00, KernelFixture::kLarge_KernelFixture, "_big_color");) 184 DEF_GM(return new MatrixConvolutionGM(0xFFFFFFFF, 0x40404040, KernelFixture::kLarger_KernelFixture, "_bigger");) 185 DEF_GM(return new MatrixConvolutionGM(0xFFFFFFFF, 0x40404040, KernelFixture::kLargest_KernelFixture, "_biggest");) 186 187 } // namespace skiagm 188