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