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