blob: a4e54c6b219d0be4e81e429ead8148cd1c9b6d25 [file] [log] [blame]
Mike Reed6e87eee2018-01-18 16:06:54 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBlendMode.h"
10#include "include/core/SkBlurTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040012#include "include/core/SkColor.h"
13#include "include/core/SkCoverageMode.h"
14#include "include/core/SkFont.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/core/SkImage.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040016#include "include/core/SkImageFilter.h"
17#include "include/core/SkImageInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "include/core/SkMaskFilter.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040019#include "include/core/SkMatrix.h"
20#include "include/core/SkPaint.h"
21#include "include/core/SkPath.h"
22#include "include/core/SkPicture.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "include/core/SkPictureRecorder.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040024#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"
Ben Wagner7fde8e12019-05-01 17:28:53 -040033#include "include/effects/SkGradientShader.h"
Michael Ludwig898bbfa2019-08-02 15:21:23 -040034#include "include/effects/SkImageFilters.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050035#include "include/effects/SkShaderMaskFilter.h"
36#include "include/utils/SkTextUtils.h"
37#include "src/core/SkBlendModePriv.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040038#include "tools/Resources.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050039#include "tools/ToolUtils.h"
Mike Reed6e87eee2018-01-18 16:06:54 -050040
Ben Wagner7fde8e12019-05-01 17:28:53 -040041#include <initializer_list>
42
Mike Reed6e87eee2018-01-18 16:06:54 -050043static void draw_masked_image(SkCanvas* canvas, const SkImage* image, SkScalar x, SkScalar y,
Mike Reed547c8592018-02-05 15:59:23 -050044 const SkImage* mask, sk_sp<SkMaskFilter> outer, SkBlendMode mode) {
Mike Reed6e87eee2018-01-18 16:06:54 -050045 SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(image->width()) / mask->width(),
46 SkIntToScalar(image->height() / mask->height()));
Michael Ludwigb7d64b92019-02-11 11:09:15 -050047 // 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);
Mike Reed6e87eee2018-01-18 16:06:54 -050050 SkPaint paint;
Mike Reed20dc6722018-01-24 16:34:02 -050051 auto mf = SkShaderMaskFilter::Make(mask->makeShader(&matrix));
52 if (outer) {
Michael Ludwigb7d64b92019-02-11 11:09:15 -050053 mf = SkMaskFilter::MakeCompose(outer->makeWithMatrix(matrix), mf);
Mike Reed20dc6722018-01-24 16:34:02 -050054 }
55 paint.setMaskFilter(mf);
Mike Reed28eaed22018-02-01 11:24:53 -050056 paint.setAntiAlias(true);
Mike Reed547c8592018-02-05 15:59:23 -050057 paint.setBlendMode(mode);
Mike Reed6e87eee2018-01-18 16:06:54 -050058 canvas->drawImage(image, x, y, &paint);
59}
60
Mike Reed6e87eee2018-01-18 16:06:54 -050061static 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 };
Mike Reedfae8fce2019-04-03 10:27:45 -040066 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kRepeat);
Mike Reed6e87eee2018-01-18 16:06:54 -050067}
68
69DEF_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);
Mike Reed28eaed22018-02-01 11:24:53 -050080 paint.setAntiAlias(true);
Mike Reed6e87eee2018-01-18 16:06:54 -050081 canvas->drawOval(r, paint);
82}
83
Chris Dalton50e24d72019-02-07 16:20:09 -070084DEF_SIMPLE_GM_CAN_FAIL(shadermaskfilter_image, canvas, errorMsg, 560, 370) {
Mike Reed6e87eee2018-01-18 16:06:54 -050085 canvas->scale(1.25f, 1.25f);
86
87 auto image = GetResourceAsImage("images/mandrill_128.png");
88 auto mask = GetResourceAsImage("images/color_wheel.png");
Hal Canarybaa2a282018-11-26 15:34:12 -050089 if (!image || !mask) {
Chris Dalton50e24d72019-02-07 16:20:09 -070090 *errorMsg = "Could not load images. Did you forget to set the resourcePath?";
91 return skiagm::DrawResult::kFail;
Hal Canarybaa2a282018-11-26 15:34:12 -050092 }
Mike Reed1be1f8d2018-03-14 13:01:17 -040093 auto blurmf = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5);
Mike Reed547c8592018-02-05 15:59:23 -050094 auto gradmf = SkShaderMaskFilter::Make(make_shader(SkRect::MakeIWH(mask->width(),
95 mask->height())));
Mike Reed6e87eee2018-01-18 16:06:54 -050096
Mike Reed547c8592018-02-05 15:59:23 -050097 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 }
Chris Dalton50e24d72019-02-07 16:20:09 -0700107 return skiagm::DrawResult::kOk;
Mike Reed20dc6722018-01-24 16:34:02 -0500108}
109
110///////////////////////////////////////////////////////////////////////////////////////////////////
111
Mike Reed20dc6722018-01-24 16:34:02 -0500112static 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);
Mike Reedfae8fce2019-04-03 10:27:45 -0400119 auto shader = recorder.finishRecordingAsPicture()->makeShader(SkTileMode::kClamp,
120 SkTileMode::kClamp);
Mike Reed20dc6722018-01-24 16:34:02 -0500121 return SkShaderMaskFilter::Make(shader);
122}
123
Mike Reed1bd556a2018-01-26 11:42:38 -0500124typedef void (*MakePathsProc)(const SkRect&, SkPath*, SkPath*);
125
126const char* gCoverageName[] = {
127 "union", "sect", "diff", "rev-diff", "xor"
128};
129
Mike Reed547c8592018-02-05 15:59:23 -0500130DEF_SIMPLE_GM(combinemaskfilter, canvas, 560, 510) {
Mike Reed20dc6722018-01-24 16:34:02 -0500131 const SkRect r = { 0, 0, 100, 100 };
132
Mike Reed1bd556a2018-01-26 11:42:38 -0500133 SkPaint paint;
134 paint.setColor(SK_ColorRED);
135
Mike Reeddc5863c2018-12-23 23:19:14 -0500136 SkFont font;
137 font.setSize(20);
Mike Reed1bd556a2018-01-26 11:42:38 -0500138
Mike Reed20dc6722018-01-24 16:34:02 -0500139 const SkRect r2 = r.makeOutset(1.5f, 1.5f);
Mike Reed547c8592018-02-05 15:59:23 -0500140 SkPaint strokePaint;
141 strokePaint.setStyle(SkPaint::kStroke_Style);
Mike Reed20dc6722018-01-24 16:34:02 -0500142
Mike Reed1bd556a2018-01-26 11:42:38 -0500143 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 };
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 };
Mike Reed20dc6722018-01-24 16:34:02 -0500156
Mike Reed1bd556a2018-01-26 11:42:38 -0500157 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 }
Mike Reed20dc6722018-01-24 16:34:02 -0500164
Mike Reed1bd556a2018-01-26 11:42:38 -0500165 canvas->translate(10, 10 + 20);
Mike Reed20dc6722018-01-24 16:34:02 -0500166 canvas->save();
Mike Reed1bd556a2018-01-26 11:42:38 -0500167 for (int i = 0; i < 5; ++i) {
Mike Reeddc5863c2018-12-23 23:19:14 -0500168 SkTextUtils::DrawString(canvas, gCoverageName[i], r.width()*0.5f, -10, font, SkPaint(),
Mike Reed3a42ec02018-10-30 12:53:21 -0400169 SkTextUtils::kCenter_Align);
Mike Reed1bd556a2018-01-26 11:42:38 -0500170
Mike Reed547c8592018-02-05 15:59:23 -0500171 SkCoverageMode cmode = static_cast<SkCoverageMode>(i);
Mike Reed1bd556a2018-01-26 11:42:38 -0500172 canvas->save();
Mike Reed547c8592018-02-05 15:59:23 -0500173 // 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);
Mike Reed20dc6722018-01-24 16:34:02 -0500188 }
Mike Reed1bd556a2018-01-26 11:42:38 -0500189 canvas->restore();
190 canvas->translate(r.width() + 10, 0);
Mike Reed20dc6722018-01-24 16:34:02 -0500191 }
192 canvas->restore();
Mike Reed6e87eee2018-01-18 16:06:54 -0500193}
Mike Reedbe33bab2018-02-06 16:07:51 -0500194
Mike Reedbe33bab2018-02-06 16:07:51 -0500195static sk_sp<SkImage> make_circle_image(SkCanvas* canvas, SkScalar radius, int margin) {
196 const int n = SkScalarCeilToInt(radius) * 2 + margin * 2;
Mike Kleinea3f0142019-03-20 11:12:10 -0500197 auto surf = ToolUtils::makeSurface(canvas, SkImageInfo::MakeN32Premul(n, n));
Mike Reedbe33bab2018-02-06 16:07:51 -0500198 SkPaint paint;
199 paint.setAntiAlias(true);
200 surf->getCanvas()->drawCircle(n * 0.5f, n * 0.5f, radius, paint);
201 return surf->makeImageSnapshot();
202}
203
Mike Reed2179b782018-02-07 11:59:57 -0500204DEF_SIMPLE_GM(savelayer_maskfilter, canvas, 450, 675) {
205 auto layerImage = GetResourceAsImage("images/mandrill_128.png");
206 auto maskImage = make_circle_image(canvas, 50, 1);
Mike Reedbe33bab2018-02-06 16:07:51 -0500207 SkRect r = SkRect::MakeWH(102, 102);
208
Mike Reed2179b782018-02-07 11:59:57 -0500209 SkPaint overlayPaint;
210 overlayPaint.setStyle(SkPaint::kStroke_Style);
Mike Reedbe33bab2018-02-06 16:07:51 -0500211
212 // test that the maskfilter sees these changes to the ctm
213 canvas->translate(10, 10);
214 canvas->scale(2, 2);
215
Mike Reed2179b782018-02-07 11:59:57 -0500216 sk_sp<SkMaskFilter> mfs[] = {
217 SkShaderMaskFilter::Make(maskImage->makeShader()),
Mike Reed1be1f8d2018-03-14 13:01:17 -0400218 SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 3.5f),
Mike Reed2179b782018-02-07 11:59:57 -0500219 nullptr,
220 };
221 mfs[2] = SkMaskFilter::MakeCompose(mfs[1], mfs[0]);
Mike Reedbe33bab2018-02-06 16:07:51 -0500222
Mike Reed2179b782018-02-07 11:59:57 -0500223 // 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)
Michael Ludwig898bbfa2019-08-02 15:21:23 -0400225 const sk_sp<SkImageFilter> imfs[] = {nullptr, SkImageFilters::Blur(3.5f, 3.5f, nullptr)};
Mike Reed2179b782018-02-07 11:59:57 -0500226
Mike Reedb9fc24e2018-02-08 14:00:56 -0500227 for (auto& mf : mfs) {
Mike Reed2179b782018-02-07 11:59:57 -0500228 SkPaint layerPaint;
229 layerPaint.setMaskFilter(mf);
230 canvas->save();
Mike Reedb9fc24e2018-02-08 14:00:56 -0500231 for (auto& imf : imfs) {
Mike Reed2179b782018-02-07 11:59:57 -0500232 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 }
Mike Reedbe33bab2018-02-06 16:07:51 -0500246}
247
Florin Malitac6c5ead2018-04-11 15:33:40 -0400248static void draw_mask(SkCanvas* canvas) {
249 SkPaint p;
250 p.setAntiAlias(true);
251 canvas->drawOval(SkRect::Make(canvas->imageInfo().bounds()), p);
252}
253
254DEF_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[] = {
Mike Kleinea3f0142019-03-20 11:12:10 -0500259 [](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(
Mike Reedfae8fce2019-04-03 10:27:45 -0400264 SkTileMode::kClamp, SkTileMode::kClamp, &lm);
Mike Kleinea3f0142019-03-20 11:12:10 -0500265 },
266 [](SkCanvas*, const SkMatrix& lm) -> sk_sp<SkShader> {
267 SkPictureRecorder recorder;
268 draw_mask(recorder.beginRecording(kSize, kSize));
Mike Reedfae8fce2019-04-03 10:27:45 -0400269 return recorder.finishRecordingAsPicture()->makeShader(
270 SkTileMode::kClamp,
271 SkTileMode::kClamp,
Mike Kleinea3f0142019-03-20 11:12:10 -0500272 &lm,
273 nullptr);
274 },
Florin Malitac6c5ead2018-04-11 15:33:40 -0400275 };
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[] = {
296 [](SkCanvas* canvas, const SkRect& dest, const SkPaint& mask) {
297 canvas->drawRect(dest, mask);
298 },
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}