1 /* 2 * Copyright 2013 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/SkBlendMode.h" 11 #include "include/core/SkCanvas.h" 12 #include "include/core/SkColor.h" 13 #include "include/core/SkImage.h" 14 #include "include/core/SkImageFilter.h" 15 #include "include/core/SkPaint.h" 16 #include "include/core/SkRect.h" 17 #include "include/core/SkRefCnt.h" 18 #include "include/core/SkScalar.h" 19 #include "include/core/SkSize.h" 20 #include "include/core/SkString.h" 21 #include "include/core/SkTypes.h" 22 #include "include/effects/SkImageFilters.h" 23 #include "tools/ToolUtils.h" 24 #include "tools/fonts/FontToolUtils.h" 25 26 #include <utility> 27 28 #define WIDTH 600 29 #define HEIGHT 700 30 #define MARGIN 12 31 32 namespace skiagm { 33 34 class XfermodeImageFilterGM : public GM { 35 public: XfermodeImageFilterGM()36 XfermodeImageFilterGM(){ 37 this->setBGColor(0xFF000000); 38 } 39 40 protected: getName() const41 SkString getName() const override { return SkString("xfermodeimagefilter"); } 42 getISize()43 SkISize getISize() override { return SkISize::Make(WIDTH, HEIGHT); } 44 onOnceBeforeDraw()45 void onOnceBeforeDraw() override { 46 fBitmap = ToolUtils::CreateStringBitmap(80, 80, 0xD000D000, 15, 65, 96, "e"); 47 48 fCheckerboard = ToolUtils::create_checkerboard_image(80, 80, 0xFFA0A0A0, 0xFF404040, 8); 49 } 50 onDraw(SkCanvas * canvas)51 void onDraw(SkCanvas* canvas) override { 52 canvas->clear(SK_ColorBLACK); 53 SkPaint paint; 54 55 const SkBlendMode gModes[] = { 56 SkBlendMode::kClear, 57 SkBlendMode::kSrc, 58 SkBlendMode::kDst, 59 SkBlendMode::kSrcOver, 60 SkBlendMode::kDstOver, 61 SkBlendMode::kSrcIn, 62 SkBlendMode::kDstIn, 63 SkBlendMode::kSrcOut, 64 SkBlendMode::kDstOut, 65 SkBlendMode::kSrcATop, 66 SkBlendMode::kDstATop, 67 SkBlendMode::kXor, 68 69 SkBlendMode::kPlus, 70 SkBlendMode::kModulate, 71 SkBlendMode::kScreen, 72 SkBlendMode::kOverlay, 73 SkBlendMode::kDarken, 74 SkBlendMode::kLighten, 75 SkBlendMode::kColorDodge, 76 SkBlendMode::kColorBurn, 77 SkBlendMode::kHardLight, 78 SkBlendMode::kSoftLight, 79 SkBlendMode::kDifference, 80 SkBlendMode::kExclusion, 81 SkBlendMode::kMultiply, 82 SkBlendMode::kHue, 83 SkBlendMode::kSaturation, 84 SkBlendMode::kColor, 85 SkBlendMode::kLuminosity, 86 }; 87 88 int x = 0, y = 0; 89 sk_sp<SkImageFilter> background(SkImageFilters::Image(fCheckerboard, 90 SkFilterMode::kNearest)); 91 for (size_t i = 0; i < std::size(gModes); i++) { 92 paint.setImageFilter(SkImageFilters::Blend(gModes[i], background)); 93 DrawClippedBitmap(canvas, fBitmap, paint, x, y); 94 x += fBitmap.width() + MARGIN; 95 if (x + fBitmap.width() > WIDTH) { 96 x = 0; 97 y += fBitmap.height() + MARGIN; 98 } 99 } 100 // Test arithmetic mode as image filter 101 paint.setImageFilter(SkImageFilters::Arithmetic(0, 1, 1, 0, true, background, nullptr)); 102 DrawClippedBitmap(canvas, fBitmap, paint, x, y); 103 x += fBitmap.width() + MARGIN; 104 if (x + fBitmap.width() > WIDTH) { 105 x = 0; 106 y += fBitmap.height() + MARGIN; 107 } 108 // Test nullptr mode 109 paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kSrcOver, background)); 110 DrawClippedBitmap(canvas, fBitmap, paint, x, y); 111 x += fBitmap.width() + MARGIN; 112 if (x + fBitmap.width() > WIDTH) { 113 x = 0; 114 y += fBitmap.height() + MARGIN; 115 } 116 SkRect clipRect = SkRect::MakeWH(SkIntToScalar(fBitmap.width() + 4), 117 SkIntToScalar(fBitmap.height() + 4)); 118 // Test offsets on SrcMode (uses fixed-function blend) 119 sk_sp<SkImage> bitmapImage(fBitmap.asImage()); 120 sk_sp<SkImageFilter> foreground(SkImageFilters::Image(std::move(bitmapImage), 121 SkFilterMode::kNearest)); 122 sk_sp<SkImageFilter> offsetForeground(SkImageFilters::Offset(4, -4, foreground)); 123 sk_sp<SkImageFilter> offsetBackground(SkImageFilters::Offset(4, 4, background)); 124 paint.setImageFilter(SkImageFilters::Blend( 125 SkBlendMode::kSrcOver, offsetBackground, offsetForeground)); 126 DrawClippedPaint(canvas, clipRect, paint, x, y); 127 x += fBitmap.width() + MARGIN; 128 if (x + fBitmap.width() > WIDTH) { 129 x = 0; 130 y += fBitmap.height() + MARGIN; 131 } 132 // Test offsets on Darken (uses shader blend) 133 paint.setImageFilter(SkImageFilters::Blend( 134 SkBlendMode::kDarken, offsetBackground, offsetForeground)); 135 DrawClippedPaint(canvas, clipRect, paint, x, y); 136 x += fBitmap.width() + MARGIN; 137 if (x + fBitmap.width() > WIDTH) { 138 x = 0; 139 y += fBitmap.height() + MARGIN; 140 } 141 // Test cropping 142 constexpr size_t nbSamples = 3; 143 const SkBlendMode sampledModes[nbSamples] = { 144 SkBlendMode::kOverlay, SkBlendMode::kSrcOver, SkBlendMode::kPlus 145 }; 146 int offsets[nbSamples][4] = {{ 10, 10, -16, -16}, 147 { 10, 10, 10, 10}, 148 {-10, -10, -6, -6}}; 149 for (size_t i = 0; i < nbSamples; ++i) { 150 SkIRect cropRect = SkIRect::MakeXYWH(offsets[i][0], 151 offsets[i][1], 152 fBitmap.width() + offsets[i][2], 153 fBitmap.height() + offsets[i][3]); 154 paint.setImageFilter(SkImageFilters::Blend(sampledModes[i], offsetBackground, 155 offsetForeground, &cropRect)); 156 DrawClippedPaint(canvas, clipRect, paint, x, y); 157 x += fBitmap.width() + MARGIN; 158 if (x + fBitmap.width() > WIDTH) { 159 x = 0; 160 y += fBitmap.height() + MARGIN; 161 } 162 } 163 // Test small bg, large fg with Screen (uses shader blend) 164 SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 60, 60); 165 sk_sp<SkImageFilter> cropped(SkImageFilters::Offset(0, 0, foreground, &cropRect)); 166 paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kScreen, cropped, background)); 167 DrawClippedPaint(canvas, clipRect, paint, x, y); 168 x += fBitmap.width() + MARGIN; 169 if (x + fBitmap.width() > WIDTH) { 170 x = 0; 171 y += fBitmap.height() + MARGIN; 172 } 173 // Test small fg, large bg with Screen (uses shader blend) 174 paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kScreen, background, cropped)); 175 DrawClippedPaint(canvas, clipRect, paint, x, y); 176 x += fBitmap.width() + MARGIN; 177 if (x + fBitmap.width() > WIDTH) { 178 x = 0; 179 y += fBitmap.height() + MARGIN; 180 } 181 // Test small fg, large bg with SrcIn with a crop that forces it to full size. 182 // This tests that SkXfermodeImageFilter correctly applies the compositing mode to 183 // the region outside the foreground. 184 SkIRect cropRectFull = SkIRect::MakeXYWH(0, 0, 80, 80); 185 paint.setImageFilter(SkImageFilters::Blend(SkBlendMode::kSrcIn, background, cropped, 186 &cropRectFull)); 187 DrawClippedPaint(canvas, clipRect, paint, x, y); 188 x += fBitmap.width() + MARGIN; 189 if (x + fBitmap.width() > WIDTH) { 190 x = 0; 191 y += fBitmap.height() + MARGIN; 192 } 193 } 194 195 private: DrawClippedBitmap(SkCanvas * canvas,const SkBitmap & bitmap,const SkPaint & paint,int x,int y)196 static void DrawClippedBitmap(SkCanvas* canvas, const SkBitmap& bitmap, const SkPaint& paint, 197 int x, int y) { 198 canvas->save(); 199 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 200 canvas->clipIRect(bitmap.bounds()); 201 canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); 202 canvas->restore(); 203 } 204 DrawClippedPaint(SkCanvas * canvas,const SkRect & rect,const SkPaint & paint,int x,int y)205 static void DrawClippedPaint(SkCanvas* canvas, const SkRect& rect, const SkPaint& paint, 206 int x, int y) { 207 canvas->save(); 208 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 209 canvas->clipRect(rect); 210 canvas->drawPaint(paint); 211 canvas->restore(); 212 } 213 214 SkBitmap fBitmap; 215 sk_sp<SkImage> fCheckerboard; 216 217 using INHERITED = GM; 218 }; 219 220 ////////////////////////////////////////////////////////////////////////////// 221 222 DEF_GM( return new XfermodeImageFilterGM; ); 223 224 } // namespace skiagm 225