blob: d561967cd868f5ca5df0e353f6750ee09e59467a [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
8#include "gm.h"
9#include "sk_tool_utils.h"
Mike Reed547c8592018-02-05 15:59:23 -050010#include "SkBlendModePriv.h"
Mike Reed20dc6722018-01-24 16:34:02 -050011#include "SkBlurMaskFilter.h"
Mike Reed6e87eee2018-01-18 16:06:54 -050012#include "SkCanvas.h"
13#include "SkImage.h"
14#include "SkShaderMaskFilter.h"
15
16static void draw_masked_image(SkCanvas* canvas, const SkImage* image, SkScalar x, SkScalar y,
Mike Reed547c8592018-02-05 15:59:23 -050017 const SkImage* mask, sk_sp<SkMaskFilter> outer, SkBlendMode mode) {
Mike Reed6e87eee2018-01-18 16:06:54 -050018 SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(image->width()) / mask->width(),
19 SkIntToScalar(image->height() / mask->height()));
20 SkPaint paint;
Mike Reed20dc6722018-01-24 16:34:02 -050021 auto mf = SkShaderMaskFilter::Make(mask->makeShader(&matrix));
22 if (outer) {
23 mf = SkMaskFilter::MakeCompose(outer, mf);
24 }
25 paint.setMaskFilter(mf);
Mike Reed28eaed22018-02-01 11:24:53 -050026 paint.setAntiAlias(true);
Mike Reed547c8592018-02-05 15:59:23 -050027 paint.setBlendMode(mode);
Mike Reed6e87eee2018-01-18 16:06:54 -050028 canvas->drawImage(image, x, y, &paint);
29}
30
31#include "SkGradientShader.h"
32static 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
40DEF_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);
Mike Reed28eaed22018-02-01 11:24:53 -050051 paint.setAntiAlias(true);
Mike Reed6e87eee2018-01-18 16:06:54 -050052 canvas->drawOval(r, paint);
53}
54
55#include "Resources.h"
Mike Reed547c8592018-02-05 15:59:23 -050056DEF_SIMPLE_GM(shadermaskfilter_image, canvas, 560, 370) {
Mike Reed6e87eee2018-01-18 16:06:54 -050057 canvas->scale(1.25f, 1.25f);
58
59 auto image = GetResourceAsImage("images/mandrill_128.png");
60 auto mask = GetResourceAsImage("images/color_wheel.png");
Mike Reed20dc6722018-01-24 16:34:02 -050061 auto blurmf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 5);
Mike Reed547c8592018-02-05 15:59:23 -050062 auto gradmf = SkShaderMaskFilter::Make(make_shader(SkRect::MakeIWH(mask->width(),
63 mask->height())));
Mike Reed6e87eee2018-01-18 16:06:54 -050064
Mike Reed547c8592018-02-05 15:59:23 -050065 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 }
Mike Reed20dc6722018-01-24 16:34:02 -050075}
76
77///////////////////////////////////////////////////////////////////////////////////////////////////
78
Mike Reed20dc6722018-01-24 16:34:02 -050079#include "SkPictureRecorder.h"
80#include "SkPath.h"
Mike Reed1bd556a2018-01-26 11:42:38 -050081
Mike Reed20dc6722018-01-24 16:34:02 -050082static 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
Mike Reed1bd556a2018-01-26 11:42:38 -050095typedef void (*MakePathsProc)(const SkRect&, SkPath*, SkPath*);
96
97const char* gCoverageName[] = {
98 "union", "sect", "diff", "rev-diff", "xor"
99};
100
Mike Reed547c8592018-02-05 15:59:23 -0500101DEF_SIMPLE_GM(combinemaskfilter, canvas, 560, 510) {
Mike Reed20dc6722018-01-24 16:34:02 -0500102 const SkRect r = { 0, 0, 100, 100 };
103
Mike Reed1bd556a2018-01-26 11:42:38 -0500104 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
Mike Reed20dc6722018-01-24 16:34:02 -0500112 const SkRect r2 = r.makeOutset(1.5f, 1.5f);
Mike Reed547c8592018-02-05 15:59:23 -0500113 SkPaint strokePaint;
114 strokePaint.setStyle(SkPaint::kStroke_Style);
Mike Reed20dc6722018-01-24 16:34:02 -0500115
Mike Reed1bd556a2018-01-26 11:42:38 -0500116 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 };
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 };
Mike Reed20dc6722018-01-24 16:34:02 -0500129
Mike Reed1bd556a2018-01-26 11:42:38 -0500130 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 }
Mike Reed20dc6722018-01-24 16:34:02 -0500137
Mike Reed1bd556a2018-01-26 11:42:38 -0500138 canvas->translate(10, 10 + 20);
Mike Reed20dc6722018-01-24 16:34:02 -0500139 canvas->save();
Mike Reed1bd556a2018-01-26 11:42:38 -0500140 for (int i = 0; i < 5; ++i) {
141 canvas->drawText(gCoverageName[i], strlen(gCoverageName[i]), r.width()*0.5f, -10, labelP);
142
Mike Reed547c8592018-02-05 15:59:23 -0500143 SkCoverageMode cmode = static_cast<SkCoverageMode>(i);
Mike Reed1bd556a2018-01-26 11:42:38 -0500144 canvas->save();
Mike Reed547c8592018-02-05 15:59:23 -0500145 // 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);
Mike Reed20dc6722018-01-24 16:34:02 -0500160 }
Mike Reed1bd556a2018-01-26 11:42:38 -0500161 canvas->restore();
162 canvas->translate(r.width() + 10, 0);
Mike Reed20dc6722018-01-24 16:34:02 -0500163 }
164 canvas->restore();
Mike Reed6e87eee2018-01-18 16:06:54 -0500165}
Mike Reedbe33bab2018-02-06 16:07:51 -0500166
167#include "SkSurface.h"
Mike Reed2179b782018-02-07 11:59:57 -0500168#include "SkBlurImageFilter.h"
169#include "SkBlurMaskFilter.h"
Mike Reedbe33bab2018-02-06 16:07:51 -0500170static 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
Mike Reed2179b782018-02-07 11:59:57 -0500179DEF_SIMPLE_GM(savelayer_maskfilter, canvas, 450, 675) {
180 auto layerImage = GetResourceAsImage("images/mandrill_128.png");
181 auto maskImage = make_circle_image(canvas, 50, 1);
Mike Reedbe33bab2018-02-06 16:07:51 -0500182 SkRect r = SkRect::MakeWH(102, 102);
183
Mike Reed2179b782018-02-07 11:59:57 -0500184 SkPaint overlayPaint;
185 overlayPaint.setStyle(SkPaint::kStroke_Style);
Mike Reedbe33bab2018-02-06 16:07:51 -0500186
187 // test that the maskfilter sees these changes to the ctm
188 canvas->translate(10, 10);
189 canvas->scale(2, 2);
190
Mike Reed2179b782018-02-07 11:59:57 -0500191 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]);
Mike Reedbe33bab2018-02-06 16:07:51 -0500197
Mike Reed2179b782018-02-07 11:59:57 -0500198 // 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
Mike Reedb9fc24e2018-02-08 14:00:56 -0500202 for (auto& mf : mfs) {
Mike Reed2179b782018-02-07 11:59:57 -0500203 SkPaint layerPaint;
204 layerPaint.setMaskFilter(mf);
205 canvas->save();
Mike Reedb9fc24e2018-02-08 14:00:56 -0500206 for (auto& imf : imfs) {
Mike Reed2179b782018-02-07 11:59:57 -0500207 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 }
Mike Reedbe33bab2018-02-06 16:07:51 -0500221}
222