1 /*
2 * Copyright 2018 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 "SkBlendModePriv.h"
11 #include "SkCanvas.h"
12 #include "SkImage.h"
13 #include "SkMaskFilter.h"
14 #include "SkPictureRecorder.h"
15 #include "SkShaderMaskFilter.h"
16 #include "SkTextUtils.h"
17
draw_masked_image(SkCanvas * canvas,const SkImage * image,SkScalar x,SkScalar y,const SkImage * mask,sk_sp<SkMaskFilter> outer,SkBlendMode mode)18 static void draw_masked_image(SkCanvas* canvas, const SkImage* image, SkScalar x, SkScalar y,
19 const SkImage* mask, sk_sp<SkMaskFilter> outer, SkBlendMode mode) {
20 SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(image->width()) / mask->width(),
21 SkIntToScalar(image->height() / mask->height()));
22 SkPaint paint;
23 auto mf = SkShaderMaskFilter::Make(mask->makeShader(&matrix));
24 if (outer) {
25 mf = SkMaskFilter::MakeCompose(outer, mf);
26 }
27 paint.setMaskFilter(mf);
28 paint.setAntiAlias(true);
29 paint.setBlendMode(mode);
30 canvas->drawImage(image, x, y, &paint);
31 }
32
33 #include "SkGradientShader.h"
make_shader(const SkRect & r)34 static sk_sp<SkShader> make_shader(const SkRect& r) {
35 const SkPoint pts[] = {
36 { r.fLeft, r.fTop }, { r.fRight, r.fBottom },
37 };
38 const SkColor colors[] = { 0, SK_ColorWHITE };
39 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kRepeat_TileMode);
40 }
41
42 DEF_SIMPLE_GM(shadermaskfilter_gradient, canvas, 512, 512) {
43 SkRect r = { 0, 0, 100, 150 };
44 auto shader = make_shader(r);
45 auto mf = SkShaderMaskFilter::Make(shader);
46
47 canvas->translate(20, 20);
48 canvas->scale(2, 2);
49
50 SkPaint paint;
51 paint.setMaskFilter(mf);
52 paint.setColor(SK_ColorRED);
53 paint.setAntiAlias(true);
54 canvas->drawOval(r, paint);
55 }
56
57 #include "Resources.h"
58 DEF_SIMPLE_GM(shadermaskfilter_image, canvas, 560, 370) {
59 canvas->scale(1.25f, 1.25f);
60
61 auto image = GetResourceAsImage("images/mandrill_128.png");
62 auto mask = GetResourceAsImage("images/color_wheel.png");
63 if (!image || !mask) {
64 return;
65 }
66 auto blurmf = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5);
67 auto gradmf = SkShaderMaskFilter::Make(make_shader(SkRect::MakeIWH(mask->width(),
68 mask->height())));
69
70 const sk_sp<SkMaskFilter> array[] = { nullptr , blurmf, gradmf };
71 for (SkBlendMode mode : {SkBlendMode::kSrcOver, SkBlendMode::kSrcIn}) {
72 canvas->save();
73 for (sk_sp<SkMaskFilter> mf : array) {
74 draw_masked_image(canvas, image.get(), 10, 10, mask.get(), mf, mode);
75 canvas->translate(image->width() + 20.f, 0);
76 }
77 canvas->restore();
78 canvas->translate(0, image->height() + 20.f);
79 }
80 }
81
82 ///////////////////////////////////////////////////////////////////////////////////////////////////
83
84 #include "SkPictureRecorder.h"
85 #include "SkPath.h"
86
make_path_mf(const SkPath & path,unsigned alpha)87 static sk_sp<SkMaskFilter> make_path_mf(const SkPath& path, unsigned alpha) {
88 SkPaint paint;
89 paint.setAntiAlias(true);
90 paint.setAlpha(alpha);
91
92 SkPictureRecorder recorder;
93 recorder.beginRecording(1000, 1000)->drawPath(path, paint);
94 auto shader = SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
95 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
96 nullptr, nullptr);
97 return SkShaderMaskFilter::Make(shader);
98 }
99
100 typedef void (*MakePathsProc)(const SkRect&, SkPath*, SkPath*);
101
102 const char* gCoverageName[] = {
103 "union", "sect", "diff", "rev-diff", "xor"
104 };
105
106 DEF_SIMPLE_GM(combinemaskfilter, canvas, 560, 510) {
107 const SkRect r = { 0, 0, 100, 100 };
108
109 SkPaint paint;
110 paint.setColor(SK_ColorRED);
111
112 SkFont font;
113 font.setSize(20);
114
115 const SkRect r2 = r.makeOutset(1.5f, 1.5f);
116 SkPaint strokePaint;
117 strokePaint.setStyle(SkPaint::kStroke_Style);
118
__anonece5b1a30102(const SkRect& r, SkPath* pathA, SkPath* pathB) 119 auto proc0 = [](const SkRect& r, SkPath* pathA, SkPath* pathB) {
120 pathA->moveTo(r.fLeft, r.fBottom);
121 pathA->lineTo(r.fRight, r.fTop);
122 pathA->lineTo(r.fRight, r.fBottom);
123 pathB->moveTo(r.fLeft, r.fTop);
124 pathB->lineTo(r.fRight, r.fBottom);
125 pathB->lineTo(r.fLeft, r.fBottom);
126 };
__anonece5b1a30202(const SkRect& r, SkPath* pathA, SkPath* pathB) 127 auto proc1 = [](const SkRect& r, SkPath* pathA, SkPath* pathB) {
128 pathA->addCircle(r.width()*0.25f, r.height()*0.25f, r.width()*0.5f);
129 pathB->addCircle(r.width()*0.75f, r.height()*0.75f, r.width()*0.5f);
130 };
131 MakePathsProc procs[] = { proc0, proc1 };
132
133 sk_sp<SkMaskFilter> mfA[2], mfB[2];
134 for (int i = 0; i < 2; ++i) {
135 SkPath a, b;
136 procs[i](r, &a, &b);
137 mfA[i] = make_path_mf(a, 1 * 0xFF / 3);
138 mfB[i] = make_path_mf(b, 2 * 0xFF / 3);
139 }
140
141 canvas->translate(10, 10 + 20);
142 canvas->save();
143 for (int i = 0; i < 5; ++i) {
144 SkTextUtils::DrawString(canvas, gCoverageName[i], r.width()*0.5f, -10, font, SkPaint(),
145 SkTextUtils::kCenter_Align);
146
147 SkCoverageMode cmode = static_cast<SkCoverageMode>(i);
148 canvas->save();
149 // esp. on gpu side, its valuable to exercise modes that do and do-not convolve coverage
150 // with alpha. SrcOver and SrcIn have these properties, but also happen to "look" the same
151 // for this test.
152 const SkBlendMode bmodes[] = { SkBlendMode::kSrcOver, SkBlendMode::kSrcIn };
153 SkASSERT( SkBlendMode_SupportsCoverageAsAlpha(bmodes[0])); // test as-alpha
154 SkASSERT(!SkBlendMode_SupportsCoverageAsAlpha(bmodes[1])); // test not-as-alpha
155 for (auto bmode : bmodes) {
156 paint.setBlendMode(bmode);
157 for (int j = 0; j < 2; ++j) {
158 paint.setMaskFilter(SkMaskFilter::MakeCombine(mfA[j], mfB[j], cmode));
159 canvas->drawRect(r2, strokePaint);
160 canvas->drawRect(r, paint);
161 canvas->translate(0, r.height() + 10);
162 }
163 canvas->translate(0, 40);
164 }
165 canvas->restore();
166 canvas->translate(r.width() + 10, 0);
167 }
168 canvas->restore();
169 }
170
171 #include "SkSurface.h"
172 #include "SkBlurImageFilter.h"
173 #include "SkMaskFilter.h"
make_circle_image(SkCanvas * canvas,SkScalar radius,int margin)174 static sk_sp<SkImage> make_circle_image(SkCanvas* canvas, SkScalar radius, int margin) {
175 const int n = SkScalarCeilToInt(radius) * 2 + margin * 2;
176 auto surf = sk_tool_utils::makeSurface(canvas, SkImageInfo::MakeN32Premul(n, n));
177 SkPaint paint;
178 paint.setAntiAlias(true);
179 surf->getCanvas()->drawCircle(n * 0.5f, n * 0.5f, radius, paint);
180 return surf->makeImageSnapshot();
181 }
182
183 DEF_SIMPLE_GM(savelayer_maskfilter, canvas, 450, 675) {
184 auto layerImage = GetResourceAsImage("images/mandrill_128.png");
185 auto maskImage = make_circle_image(canvas, 50, 1);
186 SkRect r = SkRect::MakeWH(102, 102);
187
188 SkPaint overlayPaint;
189 overlayPaint.setStyle(SkPaint::kStroke_Style);
190
191 // test that the maskfilter sees these changes to the ctm
192 canvas->translate(10, 10);
193 canvas->scale(2, 2);
194
195 sk_sp<SkMaskFilter> mfs[] = {
196 SkShaderMaskFilter::Make(maskImage->makeShader()),
197 SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 3.5f),
198 nullptr,
199 };
200 mfs[2] = SkMaskFilter::MakeCompose(mfs[1], mfs[0]);
201
202 // Important that we test with and without an imagefilter attached to the layer,
203 // as cpu and gpu backends treat these differently (w/ or w/o a SkSpecialImage)
204 const sk_sp<SkImageFilter> imfs[] = {nullptr, SkBlurImageFilter::Make(3.5f, 3.5f, nullptr)};
205
206 for (auto& mf : mfs) {
207 SkPaint layerPaint;
208 layerPaint.setMaskFilter(mf);
209 canvas->save();
210 for (auto& imf : imfs) {
211 layerPaint.setImageFilter(imf);
212
213 canvas->saveLayer(&r, &layerPaint);
214 canvas->drawImage(layerImage, 0, 0, nullptr);
215 canvas->restore();
216
217 // now draw the (approximage) expected bounds of the mask
218 canvas->drawRect(r.makeOutset(1, 1), overlayPaint);
219
220 canvas->translate(r.width() + 10, 0);
221 }
222 canvas->restore();
223 canvas->translate(0, r.height() + 10);
224 }
225 }
226
draw_mask(SkCanvas * canvas)227 static void draw_mask(SkCanvas* canvas) {
228 SkPaint p;
229 p.setAntiAlias(true);
230 canvas->drawOval(SkRect::Make(canvas->imageInfo().bounds()), p);
231 }
232
233 DEF_SIMPLE_GM(shadermaskfilter_localmatrix, canvas, 1500, 1000) {
234 static constexpr SkScalar kSize = 100;
235
236 using ShaderMakerT = sk_sp<SkShader>(*)(SkCanvas*, const SkMatrix& lm);
237 static const ShaderMakerT gShaderMakers[] = {
__anonece5b1a30302() 238 [](SkCanvas* canvas, const SkMatrix& lm) -> sk_sp<SkShader> {
239 auto surface = sk_tool_utils::makeSurface(canvas,
240 SkImageInfo::MakeN32Premul(kSize, kSize));
241 draw_mask(surface->getCanvas());
242 return surface->makeImageSnapshot()->makeShader(SkShader::kClamp_TileMode,
243 SkShader::kClamp_TileMode, &lm);
244 },
__anonece5b1a30402() 245 [](SkCanvas*, const SkMatrix& lm) -> sk_sp<SkShader> {
246 SkPictureRecorder recorder;
247 draw_mask(recorder.beginRecording(kSize, kSize));
248 return SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
249 SkShader::kClamp_TileMode,
250 SkShader::kClamp_TileMode,
251 &lm, nullptr);
252 },
253 };
254
255 struct Config {
256 SkMatrix fCanvasMatrix,
257 fMaskMatrix,
258 fShaderMatrix;
259 } gConfigs[] = {
260 { SkMatrix::I(), SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10) },
261 { SkMatrix::MakeScale(2, 2), SkMatrix::I(), SkMatrix::MakeTrans(10, 10) },
262 { SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10), SkMatrix::I() },
263 { SkMatrix::Concat(SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10)),
264 SkMatrix::I(), SkMatrix::I() },
265 { SkMatrix::I(),
266 SkMatrix::Concat(SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10)),
267 SkMatrix::I() },
268 { SkMatrix::I(), SkMatrix::I(),
269 SkMatrix::Concat(SkMatrix::MakeScale(2, 2), SkMatrix::MakeTrans(10, 10)) },
270 };
271
272 using DrawerT = void(*)(SkCanvas*, const SkRect&, const SkPaint&);
273 static const DrawerT gDrawers[] = {
__anonece5b1a30502() 274 [](SkCanvas* canvas, const SkRect& dest, const SkPaint& mask) {
275 canvas->drawRect(dest, mask);
276 },
__anonece5b1a30602() 277 [](SkCanvas* canvas, const SkRect& dest, const SkPaint& mask) {
278 canvas->saveLayer(&dest, &mask);
279 SkPaint p = mask;
280 p.setMaskFilter(nullptr);
281 canvas->drawPaint(p);
282 canvas->restore();
283 },
284 };
285
286 SkPaint paint, rectPaint;
287 paint.setColor(0xff00ff00);
288 rectPaint.setStyle(SkPaint::kStroke_Style);
289 rectPaint.setColor(0xffff0000);
290
291 for (const auto& sm : gShaderMakers) {
292 for (const auto& drawer : gDrawers) {
293 {
294 SkAutoCanvasRestore acr(canvas, true);
295 for (const auto& cfg : gConfigs) {
296 paint.setMaskFilter(SkShaderMaskFilter::Make(sm(canvas, cfg.fShaderMatrix))
297 ->makeWithMatrix(cfg.fMaskMatrix));
298 auto dest = SkRect::MakeWH(kSize, kSize);
299 SkMatrix::Concat(cfg.fMaskMatrix, cfg.fShaderMatrix).mapRect(&dest);
300
301 {
302 SkAutoCanvasRestore acr(canvas, true);
303 canvas->concat(cfg.fCanvasMatrix);
304 drawer(canvas, dest, paint);
305 canvas->drawRect(dest, rectPaint);
306 }
307
308 canvas->translate(kSize * 2.5f, 0);
309 }
310 }
311 canvas->translate(0, kSize * 2.5f);
312 }
313
314 }
315 }
316