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 "SkBlurMaskFilter.h"
12 #include "SkCanvas.h"
13 #include "SkImage.h"
14 #include "SkShaderMaskFilter.h"
15
draw_masked_image(SkCanvas * canvas,const SkImage * image,SkScalar x,SkScalar y,const SkImage * mask,sk_sp<SkMaskFilter> outer,SkBlendMode mode)16 static void draw_masked_image(SkCanvas* canvas, const SkImage* image, SkScalar x, SkScalar y,
17 const SkImage* mask, sk_sp<SkMaskFilter> outer, SkBlendMode mode) {
18 SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(image->width()) / mask->width(),
19 SkIntToScalar(image->height() / mask->height()));
20 SkPaint paint;
21 auto mf = SkShaderMaskFilter::Make(mask->makeShader(&matrix));
22 if (outer) {
23 mf = SkMaskFilter::MakeCompose(outer, mf);
24 }
25 paint.setMaskFilter(mf);
26 paint.setAntiAlias(true);
27 paint.setBlendMode(mode);
28 canvas->drawImage(image, x, y, &paint);
29 }
30
31 #include "SkGradientShader.h"
make_shader(const SkRect & r)32 static sk_sp<SkShader> make_shader(const SkRect& r) {
33 const SkPoint pts[] = {
34 { r.fLeft, r.fTop }, { r.fRight, r.fBottom },
35 };
36 const SkColor colors[] = { 0, SK_ColorWHITE };
37 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kRepeat_TileMode);
38 }
39
40 DEF_SIMPLE_GM(shadermaskfilter_gradient, canvas, 512, 512) {
41 SkRect r = { 0, 0, 100, 150 };
42 auto shader = make_shader(r);
43 auto mf = SkShaderMaskFilter::Make(shader);
44
45 canvas->translate(20, 20);
46 canvas->scale(2, 2);
47
48 SkPaint paint;
49 paint.setMaskFilter(mf);
50 paint.setColor(SK_ColorRED);
51 paint.setAntiAlias(true);
52 canvas->drawOval(r, paint);
53 }
54
55 #include "Resources.h"
56 DEF_SIMPLE_GM(shadermaskfilter_image, canvas, 560, 370) {
57 canvas->scale(1.25f, 1.25f);
58
59 auto image = GetResourceAsImage("images/mandrill_128.png");
60 auto mask = GetResourceAsImage("images/color_wheel.png");
61 auto blurmf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 5);
62 auto gradmf = SkShaderMaskFilter::Make(make_shader(SkRect::MakeIWH(mask->width(),
63 mask->height())));
64
65 const sk_sp<SkMaskFilter> array[] = { nullptr , blurmf, gradmf };
66 for (SkBlendMode mode : {SkBlendMode::kSrcOver, SkBlendMode::kSrcIn}) {
67 canvas->save();
68 for (sk_sp<SkMaskFilter> mf : array) {
69 draw_masked_image(canvas, image.get(), 10, 10, mask.get(), mf, mode);
70 canvas->translate(image->width() + 20.f, 0);
71 }
72 canvas->restore();
73 canvas->translate(0, image->height() + 20.f);
74 }
75 }
76
77 ///////////////////////////////////////////////////////////////////////////////////////////////////
78
79 #include "SkPictureRecorder.h"
80 #include "SkPath.h"
81
make_path_mf(const SkPath & path,unsigned alpha)82 static sk_sp<SkMaskFilter> make_path_mf(const SkPath& path, unsigned alpha) {
83 SkPaint paint;
84 paint.setAntiAlias(true);
85 paint.setAlpha(alpha);
86
87 SkPictureRecorder recorder;
88 recorder.beginRecording(1000, 1000)->drawPath(path, paint);
89 auto shader = SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
90 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
91 nullptr, nullptr);
92 return SkShaderMaskFilter::Make(shader);
93 }
94
95 typedef void (*MakePathsProc)(const SkRect&, SkPath*, SkPath*);
96
97 const char* gCoverageName[] = {
98 "union", "sect", "diff", "rev-diff", "xor"
99 };
100
101 DEF_SIMPLE_GM(combinemaskfilter, canvas, 560, 510) {
102 const SkRect r = { 0, 0, 100, 100 };
103
104 SkPaint paint;
105 paint.setColor(SK_ColorRED);
106
107 SkPaint labelP;
108 labelP.setAntiAlias(true);
109 labelP.setTextSize(20);
110 labelP.setTextAlign(SkPaint::kCenter_Align);
111
112 const SkRect r2 = r.makeOutset(1.5f, 1.5f);
113 SkPaint strokePaint;
114 strokePaint.setStyle(SkPaint::kStroke_Style);
115
__anon133ef8220102(const SkRect& r, SkPath* pathA, SkPath* pathB) 116 auto proc0 = [](const SkRect& r, SkPath* pathA, SkPath* pathB) {
117 pathA->moveTo(r.fLeft, r.fBottom);
118 pathA->lineTo(r.fRight, r.fTop);
119 pathA->lineTo(r.fRight, r.fBottom);
120 pathB->moveTo(r.fLeft, r.fTop);
121 pathB->lineTo(r.fRight, r.fBottom);
122 pathB->lineTo(r.fLeft, r.fBottom);
123 };
__anon133ef8220202(const SkRect& r, SkPath* pathA, SkPath* pathB) 124 auto proc1 = [](const SkRect& r, SkPath* pathA, SkPath* pathB) {
125 pathA->addCircle(r.width()*0.25f, r.height()*0.25f, r.width()*0.5f);
126 pathB->addCircle(r.width()*0.75f, r.height()*0.75f, r.width()*0.5f);
127 };
128 MakePathsProc procs[] = { proc0, proc1 };
129
130 sk_sp<SkMaskFilter> mfA[2], mfB[2];
131 for (int i = 0; i < 2; ++i) {
132 SkPath a, b;
133 procs[i](r, &a, &b);
134 mfA[i] = make_path_mf(a, 1 * 0xFF / 3);
135 mfB[i] = make_path_mf(b, 2 * 0xFF / 3);
136 }
137
138 canvas->translate(10, 10 + 20);
139 canvas->save();
140 for (int i = 0; i < 5; ++i) {
141 canvas->drawText(gCoverageName[i], strlen(gCoverageName[i]), r.width()*0.5f, -10, labelP);
142
143 SkCoverageMode cmode = static_cast<SkCoverageMode>(i);
144 canvas->save();
145 // esp. on gpu side, its valuable to exercise modes that do and do-not convolve coverage
146 // with alpha. SrcOver and SrcIn have these properties, but also happen to "look" the same
147 // for this test.
148 const SkBlendMode bmodes[] = { SkBlendMode::kSrcOver, SkBlendMode::kSrcIn };
149 SkASSERT( SkBlendMode_SupportsCoverageAsAlpha(bmodes[0])); // test as-alpha
150 SkASSERT(!SkBlendMode_SupportsCoverageAsAlpha(bmodes[1])); // test not-as-alpha
151 for (auto bmode : bmodes) {
152 paint.setBlendMode(bmode);
153 for (int j = 0; j < 2; ++j) {
154 paint.setMaskFilter(SkMaskFilter::MakeCombine(mfA[j], mfB[j], cmode));
155 canvas->drawRect(r2, strokePaint);
156 canvas->drawRect(r, paint);
157 canvas->translate(0, r.height() + 10);
158 }
159 canvas->translate(0, 40);
160 }
161 canvas->restore();
162 canvas->translate(r.width() + 10, 0);
163 }
164 canvas->restore();
165 }
166
167 #include "SkSurface.h"
168 #include "SkBlurImageFilter.h"
169 #include "SkBlurMaskFilter.h"
make_circle_image(SkCanvas * canvas,SkScalar radius,int margin)170 static sk_sp<SkImage> make_circle_image(SkCanvas* canvas, SkScalar radius, int margin) {
171 const int n = SkScalarCeilToInt(radius) * 2 + margin * 2;
172 auto surf = sk_tool_utils::makeSurface(canvas, SkImageInfo::MakeN32Premul(n, n));
173 SkPaint paint;
174 paint.setAntiAlias(true);
175 surf->getCanvas()->drawCircle(n * 0.5f, n * 0.5f, radius, paint);
176 return surf->makeImageSnapshot();
177 }
178
179 DEF_SIMPLE_GM(savelayer_maskfilter, canvas, 450, 675) {
180 auto layerImage = GetResourceAsImage("images/mandrill_128.png");
181 auto maskImage = make_circle_image(canvas, 50, 1);
182 SkRect r = SkRect::MakeWH(102, 102);
183
184 SkPaint overlayPaint;
185 overlayPaint.setStyle(SkPaint::kStroke_Style);
186
187 // test that the maskfilter sees these changes to the ctm
188 canvas->translate(10, 10);
189 canvas->scale(2, 2);
190
191 sk_sp<SkMaskFilter> mfs[] = {
192 SkShaderMaskFilter::Make(maskImage->makeShader()),
193 SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 3.5f),
194 nullptr,
195 };
196 mfs[2] = SkMaskFilter::MakeCompose(mfs[1], mfs[0]);
197
198 // Important that we test with and without an imagefilter attached to the layer,
199 // as cpu and gpu backends treat these differently (w/ or w/o a SkSpecialImage)
200 const sk_sp<SkImageFilter> imfs[] = {nullptr, SkBlurImageFilter::Make(3.5f, 3.5f, nullptr)};
201
202 for (auto& mf : mfs) {
203 SkPaint layerPaint;
204 layerPaint.setMaskFilter(mf);
205 canvas->save();
206 for (auto& imf : imfs) {
207 layerPaint.setImageFilter(imf);
208
209 canvas->saveLayer(&r, &layerPaint);
210 canvas->drawImage(layerImage, 0, 0, nullptr);
211 canvas->restore();
212
213 // now draw the (approximage) expected bounds of the mask
214 canvas->drawRect(r.makeOutset(1, 1), overlayPaint);
215
216 canvas->translate(r.width() + 10, 0);
217 }
218 canvas->restore();
219 canvas->translate(0, r.height() + 10);
220 }
221 }
222
223