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