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.h" 9 #include "sk_tool_utils.h" 10 #include "SkColor.h" 11 #include "SkGradientShader.h" 12 #include "SkMatrixConvolutionImageFilter.h" 13 #include "SkPixelRef.h" 14 15 namespace skiagm { 16 17 class MatrixConvolutionGM : public GM { 18 public: MatrixConvolutionGM(SkColor colorOne,SkColor colorTwo,const char * nameSuffix)19 MatrixConvolutionGM(SkColor colorOne, SkColor colorTwo, const char* nameSuffix) 20 : fNameSuffix(nameSuffix) { 21 this->setBGColor(0x00000000); 22 fColors[0] = colorOne; 23 fColors[1] = colorTwo; 24 } 25 26 protected: 27 onShortName()28 SkString onShortName() override { 29 return SkStringPrintf("matrixconvolution%s", fNameSuffix); 30 } 31 makeBitmap()32 void makeBitmap() { 33 // Draw our bitmap in N32, so legacy devices get "premul" values they understand 34 SkBitmap n32Bitmap; 35 n32Bitmap.allocN32Pixels(80, 80); 36 SkCanvas canvas(n32Bitmap); 37 canvas.clear(0x00000000); 38 SkPaint paint; 39 paint.setAntiAlias(true); 40 sk_tool_utils::set_portable_typeface(&paint); 41 paint.setColor(0xFFFFFFFF); 42 paint.setTextSize(SkIntToScalar(180)); 43 SkPoint pts[2] = { SkPoint::Make(0, 0), 44 SkPoint::Make(0, SkIntToScalar(80)) }; 45 SkScalar pos[2] = { 0, SkIntToScalar(80) }; 46 paint.setShader(SkGradientShader::MakeLinear( 47 pts, fColors, pos, 2, SkShader::kClamp_TileMode)); 48 const char* str = "e"; 49 canvas.drawString(str, SkIntToScalar(-10), SkIntToScalar(80), paint); 50 51 // ... tag the data as sRGB, so color-aware devices do gamut adjustment, etc... 52 fBitmap.setInfo(SkImageInfo::MakeS32(80, 80, kPremul_SkAlphaType)); 53 fBitmap.setPixelRef(sk_ref_sp(n32Bitmap.pixelRef()), 0, 0); 54 } 55 onISize()56 SkISize onISize() override { 57 return SkISize::Make(500, 300); 58 } 59 draw(SkCanvas * canvas,int x,int y,const SkIPoint & kernelOffset,SkMatrixConvolutionImageFilter::TileMode tileMode,bool convolveAlpha,const SkImageFilter::CropRect * cropRect=nullptr)60 void draw(SkCanvas* canvas, int x, int y, const SkIPoint& kernelOffset, 61 SkMatrixConvolutionImageFilter::TileMode tileMode, bool convolveAlpha, 62 const SkImageFilter::CropRect* cropRect = nullptr) { 63 SkScalar kernel[9] = { 64 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), 65 SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1), 66 SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1), 67 }; 68 SkISize kernelSize = SkISize::Make(3, 3); 69 SkScalar gain = 0.3f, bias = SkIntToScalar(100); 70 if (canvas->imageInfo().colorSpace()) { 71 // TODO: Gain and bias are poorly specified (in the feConvolveMatrix SVG documentation, 72 // there is obviously no mention of gamma or color spaces). Eventually, we need to 73 // decide what to do with these (they generally have an extreme brightening effect). 74 // For now, I'm modifying this GM to use values tuned to preserve luminance across the 75 // range of input values (compared to the legacy math and values). 76 // 77 // It's impossible to match the results exactly, because legacy math produces a flat 78 // response (when looking at sRGB encoded results), while gamma-correct math produces 79 // a curve. 80 gain = 0.25f; 81 bias = 16.5f; 82 } 83 SkPaint paint; 84 paint.setImageFilter(SkMatrixConvolutionImageFilter::Make(kernelSize, 85 kernel, 86 gain, 87 bias, 88 kernelOffset, 89 tileMode, 90 convolveAlpha, 91 nullptr, 92 cropRect)); 93 canvas->save(); 94 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 95 canvas->clipRect(SkRect::MakeWH(SkIntToScalar(fBitmap.width()), 96 SkIntToScalar(fBitmap.height()))); 97 canvas->drawBitmap(fBitmap, 0, 0, &paint); 98 canvas->restore(); 99 } 100 101 typedef SkMatrixConvolutionImageFilter MCIF; 102 onOnceBeforeDraw()103 void onOnceBeforeDraw() override { 104 this->makeBitmap(); 105 } 106 onDraw(SkCanvas * canvas)107 void onDraw(SkCanvas* canvas) override { 108 canvas->clear(SK_ColorBLACK); 109 SkIPoint kernelOffset = SkIPoint::Make(1, 0); 110 SkImageFilter::CropRect rect(SkRect::Make(fBitmap.bounds())); 111 for (int x = 10; x < 310; x += 100) { 112 this->draw(canvas, x, 10, kernelOffset, MCIF::kClamp_TileMode, true, &rect); 113 this->draw(canvas, x, 110, kernelOffset, MCIF::kClampToBlack_TileMode, true, &rect); 114 this->draw(canvas, x, 210, kernelOffset, MCIF::kRepeat_TileMode, true, &rect); 115 kernelOffset.fY++; 116 } 117 kernelOffset.fY = 1; 118 SkImageFilter::CropRect smallRect(SkRect::MakeXYWH(10, 5, 60, 60)); 119 this->draw(canvas, 310, 10, kernelOffset, MCIF::kClamp_TileMode, true, &smallRect); 120 this->draw(canvas, 310, 110, kernelOffset, MCIF::kClampToBlack_TileMode, true, &smallRect); 121 this->draw(canvas, 310, 210, kernelOffset, MCIF::kRepeat_TileMode, true, &smallRect); 122 123 this->draw(canvas, 410, 10, kernelOffset, MCIF::kClamp_TileMode, false, &rect); 124 this->draw(canvas, 410, 110, kernelOffset, MCIF::kClampToBlack_TileMode, false, &rect); 125 this->draw(canvas, 410, 210, kernelOffset, MCIF::kRepeat_TileMode, false, &rect); 126 } 127 128 private: 129 SkBitmap fBitmap; 130 SkColor fColors[2]; 131 const char* fNameSuffix; 132 133 typedef GM INHERITED; 134 }; 135 136 ////////////////////////////////////////////////////////////////////////////// 137 138 DEF_GM(return new MatrixConvolutionGM(0xFFFFFFFF, 0x40404040, "");) 139 DEF_GM(return new MatrixConvolutionGM(0xFFFF0000, 0xFF00FF00, "_color");) 140 141 } 142