blob: 70910deb411b4084df597af6007a2df899b8247a [file] [log] [blame]
robertphillips@google.com41570852013-06-03 17:13:25 +00001/*
2 * Copyright 2013 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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkColorFilter.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040014#include "include/core/SkDrawLooper.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/core/SkMaskFilter.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040016#include "include/core/SkPaint.h"
17#include "include/core/SkPoint.h"
18#include "include/core/SkRect.h"
19#include "include/core/SkRefCnt.h"
20#include "include/core/SkScalar.h"
21#include "include/core/SkSize.h"
22#include "include/core/SkString.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "include/effects/SkLayerDrawLooper.h"
24#include "src/core/SkBlurMask.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040025#include "src/core/SkClipOpPriv.h"
robertphillips@google.com41570852013-06-03 17:13:25 +000026
27// This GM tests 3 different ways of drawing four shadows around a square:
28// just using 4 blurred rects
29// using 4 1-level draw loopers
30// using 1 4-level draw looper
31// They all produce exactly the same pixels
32class MegaLooperGM : public skiagm::GM {
33public:
34 // The types define "<# of loopers> x <# of stages per looper>"
35 enum Type {
36 k0x0_Type, // draw without loopers at all
37 k4x1_Type, // a looper for each shadow
38 k1x4_Type, // all four shadows in one looper
39 };
40
41 MegaLooperGM(Type type) : fType(type) {}
42
43protected:
44 virtual SkString onShortName() {
45 switch (fType) {
46 case k0x0_Type:
47 return SkString("megalooper_0x0");
48 break;
49 case k4x1_Type:
50 return SkString("megalooper_4x1");
51 break;
52 case k1x4_Type: // fall through
53 default:
54 return SkString("megalooper_1x4");
55 break;
56 }
57 }
58
59 virtual SkISize onISize() {
60 return SkISize::Make(kWidth, kHeight);
61 }
62
63 virtual void onDraw(SkCanvas* canvas) {
64 for (int y = 100; y < kHeight; y += 200) {
65 for (int x = 100; x < kWidth; x += 200) {
66 switch (fType) {
67 case k0x0_Type:
68 draw0x0(canvas, SkIntToScalar(x), SkIntToScalar(y));
69 break;
70 case k4x1_Type:
71 draw4x1(canvas, SkIntToScalar(x), SkIntToScalar(y));
72 break;
73 case k1x4_Type: // fall through
74 default:
75 draw1x4(canvas, SkIntToScalar(x), SkIntToScalar(y));
76 break;
77 }
78 }
79 }
80 }
81
82private:
mtkleindbfd7ab2016-09-01 11:24:54 -070083 static constexpr int kWidth = 800;
84 static constexpr int kHeight = 800;
85 static constexpr int kHalfOuterClipSize = 100;
86 static constexpr int kHalfSquareSize = 50;
87 static constexpr int kOffsetToOutsideClip = kHalfSquareSize + kHalfOuterClipSize + 1;
robertphillips@google.com41570852013-06-03 17:13:25 +000088
89 static const SkPoint gBlurOffsets[4];
90 static const SkColor gColors[4];
91 Type fType;
92
93 // Just draw a blurred rect at each of the four corners of a square (centered at x,y).
94 // Use two clips to define a rectori where we want pixels to appear.
95 void draw0x0(SkCanvas* canvas, SkScalar x, SkScalar y) {
96 SkRect innerClip = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
97 innerClip.offset(x, y);
98
skia.committer@gmail.com11f2b442013-06-04 07:00:53 +000099 SkRect outerClip = {
100 -kHalfOuterClipSize-kHalfSquareSize, -kHalfOuterClipSize-kHalfSquareSize,
robertphillips@google.com41570852013-06-03 17:13:25 +0000101 kHalfOuterClipSize+kHalfSquareSize, kHalfOuterClipSize+kHalfSquareSize
102 };
103 outerClip.offset(x, y);
104
105 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500106 canvas->clipRect(outerClip, kIntersect_SkClipOp);
107 canvas->clipRect(innerClip, kDifference_SkClipOp);
robertphillips@google.com41570852013-06-03 17:13:25 +0000108
109 SkPaint paint;
110 paint.setAntiAlias(true);
reedefdfd512016-04-04 10:02:58 -0700111 paint.setMaskFilter(MakeBlur());
robertphillips@google.com41570852013-06-03 17:13:25 +0000112
113 for (int i = 0; i < 4; ++i) {
114 paint.setColor(gColors[i]);
115
116 SkRect rect = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
117 rect.offset(gBlurOffsets[i]);
118 rect.offset(x, y);
119 canvas->drawRect(rect, paint);
120 }
121
122 canvas->restore();
123 }
124
reedefdfd512016-04-04 10:02:58 -0700125 static sk_sp<SkMaskFilter> MakeBlur() {
mtkleindbfd7ab2016-09-01 11:24:54 -0700126 const SkScalar kBlurSigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(25));
robertphillips@google.com41570852013-06-03 17:13:25 +0000127
Mike Reed1be1f8d2018-03-14 13:01:17 -0400128 return SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, kBlurSigma);
robertphillips@google.com41570852013-06-03 17:13:25 +0000129 }
130
131 // This draws 4 blurred shadows around a single square (centered at x, y).
132 // Each blur is offset +/- half the square's side in x & y from the original
133 // (so each blurred rect is centered at one of the corners of the original).
134 // For each blur a large outer clip is centered around the blurred rect
135 // while a difference clip stays at the location of the original rect.
136 // Each blurred rect is drawn with a draw looper where the original (non-
137 // blurred rect) is offset to reside outside of the large outer clip (so
skia.committer@gmail.com11f2b442013-06-04 07:00:53 +0000138 // it never appears) but the offset in the draw looper is used to translate
robertphillips@google.com41570852013-06-03 17:13:25 +0000139 // the blurred version back into the clip.
140 void draw4x1(SkCanvas* canvas, SkScalar x, SkScalar y) {
141
142 for (int i = 0; i < 4; ++i) {
143 SkPaint loopPaint;
144
reed7b380d02016-03-21 13:25:16 -0700145 loopPaint.setLooper(create1Looper(-kOffsetToOutsideClip, 0, gColors[i]));
robertphillips@google.com41570852013-06-03 17:13:25 +0000146 loopPaint.setAntiAlias(true);
147
skia.committer@gmail.com11f2b442013-06-04 07:00:53 +0000148 SkRect outerClip = {
149 -kHalfOuterClipSize, -kHalfOuterClipSize,
150 kHalfOuterClipSize, kHalfOuterClipSize
robertphillips@google.com41570852013-06-03 17:13:25 +0000151 };
152 outerClip.offset(x, y);
153 // center it on the blurred rect
154 outerClip.offset(gBlurOffsets[i]);
155
156 SkRect rect = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
157 rect.offset(x, y);
158
159 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500160 canvas->clipRect(outerClip, kIntersect_SkClipOp);
161 canvas->clipRect(rect, kDifference_SkClipOp);
robertphillips@google.com41570852013-06-03 17:13:25 +0000162
163 // move the rect to where we want the blur to appear
164 rect.offset(gBlurOffsets[i]);
165 // then move it outside the clip (the blur stage of the draw
166 // looper will undo this translation)
167 rect.offset(SkIntToScalar(kOffsetToOutsideClip), 0);
168
169 canvas->drawRect(rect, loopPaint);
170 canvas->restore();
171 }
172 }
173
skia.committer@gmail.com11f2b442013-06-04 07:00:53 +0000174 // Create a 1-tier drawlooper
reed7b380d02016-03-21 13:25:16 -0700175 sk_sp<SkDrawLooper> create1Looper(SkScalar xOff, SkScalar yOff, SkColor color) {
commit-bot@chromium.org73cb1532014-04-15 15:48:36 +0000176 SkLayerDrawLooper::Builder looperBuilder;
robertphillips@google.com41570852013-06-03 17:13:25 +0000177 SkLayerDrawLooper::LayerInfo info;
178
robertphillips@google.com41570852013-06-03 17:13:25 +0000179 info.fPaintBits = SkLayerDrawLooper::kColorFilter_Bit |
180 SkLayerDrawLooper::kMaskFilter_Bit;
Mike Reedfaba3712016-11-03 14:45:31 -0400181 info.fColorMode = SkBlendMode::kSrc;
robertphillips@google.com41570852013-06-03 17:13:25 +0000182 info.fOffset.set(xOff, yOff);
183 info.fPostTranslate = false;
184
commit-bot@chromium.org73cb1532014-04-15 15:48:36 +0000185 SkPaint* paint = looperBuilder.addLayer(info);
robertphillips@google.com41570852013-06-03 17:13:25 +0000186
reedefdfd512016-04-04 10:02:58 -0700187 paint->setMaskFilter(MakeBlur());
robertphillips@google.com41570852013-06-03 17:13:25 +0000188
Mike Reedb286bc22019-04-08 16:23:20 -0400189 paint->setColorFilter(SkColorFilters::Blend(color, SkBlendMode::kSrcIn));
robertphillips@google.com41570852013-06-03 17:13:25 +0000190
reed7b380d02016-03-21 13:25:16 -0700191 return looperBuilder.detach();
robertphillips@google.com41570852013-06-03 17:13:25 +0000192 }
193
194 void draw1x4(SkCanvas* canvas, SkScalar x, SkScalar y) {
195 SkRect rect = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
196 rect.offset(x, y);
197
skia.committer@gmail.com11f2b442013-06-04 07:00:53 +0000198 SkRect outerClip = {
199 -kHalfOuterClipSize-kHalfSquareSize, -kHalfOuterClipSize-kHalfSquareSize,
robertphillips@google.com41570852013-06-03 17:13:25 +0000200 kHalfOuterClipSize+kHalfSquareSize, kHalfOuterClipSize+kHalfSquareSize
201 };
202 outerClip.offset(x, y);
203
204 SkPaint paint;
205 paint.setAntiAlias(true);
reed7b380d02016-03-21 13:25:16 -0700206 paint.setLooper(create4Looper(-kOffsetToOutsideClip-kHalfSquareSize, 0));
robertphillips@google.com41570852013-06-03 17:13:25 +0000207
208 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500209 canvas->clipRect(outerClip, kIntersect_SkClipOp);
210 canvas->clipRect(rect, kDifference_SkClipOp);
robertphillips@google.com41570852013-06-03 17:13:25 +0000211
212 rect.offset(SkIntToScalar(kOffsetToOutsideClip+kHalfSquareSize), 0);
213 canvas->drawRect(rect, paint);
214 canvas->restore();
215 }
216
217 // Create a 4-tier draw looper
reed7b380d02016-03-21 13:25:16 -0700218 sk_sp<SkDrawLooper> create4Looper(SkScalar xOff, SkScalar yOff) {
commit-bot@chromium.org73cb1532014-04-15 15:48:36 +0000219 SkLayerDrawLooper::Builder looperBuilder;
robertphillips@google.com41570852013-06-03 17:13:25 +0000220 SkLayerDrawLooper::LayerInfo info;
221
robertphillips@google.com41570852013-06-03 17:13:25 +0000222 info.fPaintBits = SkLayerDrawLooper::kColorFilter_Bit |
223 SkLayerDrawLooper::kMaskFilter_Bit;
Mike Reedfaba3712016-11-03 14:45:31 -0400224 info.fColorMode = SkBlendMode::kSrc;
robertphillips@google.com41570852013-06-03 17:13:25 +0000225 info.fPostTranslate = false;
226
227 SkPaint* paint;
228
229 for (int i = 3; i >= 0; --i) {
230 info.fOffset.set(xOff+gBlurOffsets[i].fX, yOff+gBlurOffsets[i].fY);
commit-bot@chromium.org73cb1532014-04-15 15:48:36 +0000231 paint = looperBuilder.addLayer(info);
robertphillips@google.com41570852013-06-03 17:13:25 +0000232
reedefdfd512016-04-04 10:02:58 -0700233 paint->setMaskFilter(MakeBlur());
robertphillips@google.com41570852013-06-03 17:13:25 +0000234
Mike Reedb286bc22019-04-08 16:23:20 -0400235 paint->setColorFilter(SkColorFilters::Blend(gColors[i], SkBlendMode::kSrcIn));
robertphillips@google.com41570852013-06-03 17:13:25 +0000236 }
237
reed7b380d02016-03-21 13:25:16 -0700238 return looperBuilder.detach();
robertphillips@google.com41570852013-06-03 17:13:25 +0000239 }
240
241 typedef GM INHERITED;
242};
243
244const SkPoint MegaLooperGM::gBlurOffsets[4] = {
skia.committer@gmail.com11f2b442013-06-04 07:00:53 +0000245 { kHalfSquareSize, kHalfSquareSize },
246 { -kHalfSquareSize, kHalfSquareSize },
247 { kHalfSquareSize, -kHalfSquareSize },
robertphillips@google.com41570852013-06-03 17:13:25 +0000248 { -kHalfSquareSize, -kHalfSquareSize }
249};
250
251const SkColor MegaLooperGM::gColors[4] = {
252 SK_ColorGREEN, SK_ColorYELLOW, SK_ColorBLUE, SK_ColorRED
253};
254
255DEF_GM( return new MegaLooperGM(MegaLooperGM::k0x0_Type); )
256DEF_GM( return new MegaLooperGM(MegaLooperGM::k4x1_Type); )
257DEF_GM( return new MegaLooperGM(MegaLooperGM::k1x4_Type); )