blob: 3451fa1dac912f8bd7cb4d74239e1c5fff31fae3 [file] [log] [blame]
robertphillips05a4cf52016-09-08 09:02:43 -07001/*
2 * Copyright 2016 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 "SkAnimTimer.h"
10#include "SkBlurMaskFilter.h"
11#include "SkGaussianEdgeShader.h"
robertphillips2af83ac2016-09-12 12:02:16 -070012#include "SkRRectsGaussianEdgeShader.h"
robertphillips05a4cf52016-09-08 09:02:43 -070013#include "SkPath.h"
14#include "SkPathOps.h"
15#include "SkRRect.h"
16#include "SkStroke.h"
17
18constexpr int kNumCols = 2;
19constexpr int kNumRows = 5;
20constexpr int kCellSize = 128;
21constexpr SkScalar kPad = 8.0f;
robertphillipsec895342016-09-15 13:18:15 -070022constexpr SkScalar kBlurRadius = 8.0f;
robertphillips05a4cf52016-09-08 09:02:43 -070023constexpr SkScalar kPeriod = 8.0f;
24constexpr int kClipOffset = 32;
25
26///////////////////////////////////////////////////////////////////////////////////////////////////
robertphillips05a4cf52016-09-08 09:02:43 -070027
robertphillips2af83ac2016-09-12 12:02:16 -070028class Object {
29public:
30 virtual ~Object() {}
31 virtual bool asRRect(SkRRect* rr) const = 0;
robertphillipsf619a7a2016-09-12 17:25:50 -070032 virtual SkPath asPath(SkScalar inset) const = 0;
robertphillips2af83ac2016-09-12 12:02:16 -070033 virtual void draw(SkCanvas* canvas, const SkPaint& paint) const = 0;
34 virtual void clip(SkCanvas* canvas) const = 0;
35 virtual bool contains(const SkRect& r) const = 0;
36 virtual const SkRect& bounds() const = 0;
37};
robertphillips05a4cf52016-09-08 09:02:43 -070038
robertphillips2af83ac2016-09-12 12:02:16 -070039typedef Object* (*PFMakeMthd)(const SkRect& r);
40
41class RRect : public Object {
42public:
43 RRect(const SkRect& r) {
44 fRRect = SkRRect::MakeRectXY(r, 4*kPad, 4*kPad);
robertphillips05a4cf52016-09-08 09:02:43 -070045 }
robertphillips05a4cf52016-09-08 09:02:43 -070046
robertphillips2af83ac2016-09-12 12:02:16 -070047 bool asRRect(SkRRect* rr) const override {
48 *rr = fRRect;
49 return true;
50 }
robertphillips05a4cf52016-09-08 09:02:43 -070051
robertphillipsf619a7a2016-09-12 17:25:50 -070052 SkPath asPath(SkScalar inset) const override {
53 SkRRect tmp = fRRect;
54 tmp.inset(inset, inset);
robertphillips2af83ac2016-09-12 12:02:16 -070055 SkPath p;
robertphillipsf619a7a2016-09-12 17:25:50 -070056 p.addRRect(tmp);
robertphillips2af83ac2016-09-12 12:02:16 -070057 return p;
58 }
robertphillips05a4cf52016-09-08 09:02:43 -070059
robertphillips2af83ac2016-09-12 12:02:16 -070060 void draw(SkCanvas* canvas, const SkPaint& paint) const override {
61 canvas->drawRRect(fRRect, paint);
62 }
robertphillips05a4cf52016-09-08 09:02:43 -070063
robertphillips2af83ac2016-09-12 12:02:16 -070064 void clip(SkCanvas* canvas) const override {
65 canvas->clipRRect(fRRect);
66 }
67
68 bool contains(const SkRect& r) const override {
69 return fRRect.contains(r);
70 }
71
72 const SkRect& bounds() const override {
73 return fRRect.getBounds();
74 }
75
76 static Object* Make(const SkRect& r) {
77 return new RRect(r);
78 }
79
80private:
81 SkRRect fRRect;
82};
83
84class StrokedRRect : public Object {
85public:
86 StrokedRRect(const SkRect& r) {
87 fRRect = SkRRect::MakeRectXY(r, 2*kPad, 2*kPad);
88 fStrokedBounds = r.makeOutset(kPad, kPad);
89 }
90
91 bool asRRect(SkRRect* rr) const override {
92 return false;
93 }
94
robertphillipsf619a7a2016-09-12 17:25:50 -070095 SkPath asPath(SkScalar inset) const override {
96 SkRRect tmp = fRRect;
97 tmp.inset(inset, inset);
98
robertphillips2af83ac2016-09-12 12:02:16 -070099 // In this case we want the outline of the stroked rrect
100 SkPaint paint;
101 paint.setAntiAlias(true);
102 paint.setStyle(SkPaint::kStroke_Style);
103 paint.setStrokeWidth(kPad);
104
robertphillips05a4cf52016-09-08 09:02:43 -0700105 SkPath p, stroked;
robertphillipsf619a7a2016-09-12 17:25:50 -0700106 p.addRRect(tmp);
robertphillips05a4cf52016-09-08 09:02:43 -0700107 SkStroke stroke(paint);
108 stroke.strokePath(p, &stroked);
robertphillips2af83ac2016-09-12 12:02:16 -0700109 return stroked;
robertphillips05a4cf52016-09-08 09:02:43 -0700110 }
111
robertphillips2af83ac2016-09-12 12:02:16 -0700112 void draw(SkCanvas* canvas, const SkPaint& paint) const override {
113 SkPaint stroke(paint);
114 stroke.setStyle(SkPaint::kStroke_Style);
115 stroke.setStrokeWidth(kPad);
robertphillips05a4cf52016-09-08 09:02:43 -0700116
robertphillips2af83ac2016-09-12 12:02:16 -0700117 canvas->drawRRect(fRRect, stroke);
robertphillips05a4cf52016-09-08 09:02:43 -0700118 }
119
robertphillips2af83ac2016-09-12 12:02:16 -0700120 void clip(SkCanvas* canvas) const override {
robertphillipsf619a7a2016-09-12 17:25:50 -0700121 canvas->clipPath(this->asPath(0.0f));
robertphillips05a4cf52016-09-08 09:02:43 -0700122 }
123
robertphillips2af83ac2016-09-12 12:02:16 -0700124 bool contains(const SkRect& r) const override {
125 return false;
robertphillips05a4cf52016-09-08 09:02:43 -0700126 }
127
robertphillips2af83ac2016-09-12 12:02:16 -0700128 const SkRect& bounds() const override {
129 return fStrokedBounds;
130 }
robertphillips05a4cf52016-09-08 09:02:43 -0700131
robertphillips2af83ac2016-09-12 12:02:16 -0700132 static Object* Make(const SkRect& r) {
133 return new StrokedRRect(r);
134 }
robertphillips05a4cf52016-09-08 09:02:43 -0700135
robertphillips2af83ac2016-09-12 12:02:16 -0700136private:
137 SkRRect fRRect;
138 SkRect fStrokedBounds;
139};
robertphillips05a4cf52016-09-08 09:02:43 -0700140
robertphillips2af83ac2016-09-12 12:02:16 -0700141class Oval : public Object {
142public:
143 Oval(const SkRect& r) {
144 fRRect = SkRRect::MakeOval(r);
145 }
146
147 bool asRRect(SkRRect* rr) const override {
148 *rr = fRRect;
149 return true;
150 }
151
robertphillipsf619a7a2016-09-12 17:25:50 -0700152 SkPath asPath(SkScalar inset) const override {
153 SkRRect tmp = fRRect;
154 tmp.inset(inset, inset);
155
robertphillips2af83ac2016-09-12 12:02:16 -0700156 SkPath p;
robertphillipsf619a7a2016-09-12 17:25:50 -0700157 p.addRRect(tmp);
robertphillips2af83ac2016-09-12 12:02:16 -0700158 return p;
159 }
160
161 void draw(SkCanvas* canvas, const SkPaint& paint) const override {
162 canvas->drawRRect(fRRect, paint);
163 }
164
165 void clip(SkCanvas* canvas) const override {
166 canvas->clipRRect(fRRect);
167 }
168
169 bool contains(const SkRect& r) const override {
170 return fRRect.contains(r);
171 }
172
173 const SkRect& bounds() const override {
174 return fRRect.getBounds();
175 }
176
177 static Object* Make(const SkRect& r) {
178 return new Oval(r);
179 }
180
181private:
182 SkRRect fRRect;
183};
184
185class Rect : public Object {
186public:
187 Rect(const SkRect& r) : fRect(r) { }
188
189 bool asRRect(SkRRect* rr) const override {
190 *rr = SkRRect::MakeRect(fRect);
191 return true;
192 }
193
robertphillipsf619a7a2016-09-12 17:25:50 -0700194 SkPath asPath(SkScalar inset) const override {
195 SkRect tmp = fRect;
196 tmp.inset(inset, inset);
197
robertphillips2af83ac2016-09-12 12:02:16 -0700198 SkPath p;
robertphillipsf619a7a2016-09-12 17:25:50 -0700199 p.addRect(tmp);
robertphillips2af83ac2016-09-12 12:02:16 -0700200 return p;
201 }
202
203 void draw(SkCanvas* canvas, const SkPaint& paint) const override {
204 canvas->drawRect(fRect, paint);
205 }
206
207 void clip(SkCanvas* canvas) const override {
208 canvas->clipRect(fRect);
209 }
210
211 bool contains(const SkRect& r) const override {
212 return fRect.contains(r);
213 }
214
215 const SkRect& bounds() const override {
216 return fRect;
217 }
218
219 static Object* Make(const SkRect& r) {
220 return new Rect(r);
221 }
222
223private:
224 SkRect fRect;
225};
226
227class Pentagon : public Object {
228public:
229 Pentagon(const SkRect& r) {
230 SkPoint points[5] = {
231 { 0.000000f, -1.000000f },
232 { -0.951056f, -0.309017f },
233 { -0.587785f, 0.809017f },
234 { 0.587785f, 0.809017f },
235 { 0.951057f, -0.309017f },
236 };
237
238 SkScalar height = r.height()/2.0f;
239 SkScalar width = r.width()/2.0f;
240
241 fPath.moveTo(r.centerX() + points[0].fX * width, r.centerY() + points[0].fY * height);
242 fPath.lineTo(r.centerX() + points[1].fX * width, r.centerY() + points[1].fY * height);
243 fPath.lineTo(r.centerX() + points[2].fX * width, r.centerY() + points[2].fY * height);
244 fPath.lineTo(r.centerX() + points[3].fX * width, r.centerY() + points[3].fY * height);
245 fPath.lineTo(r.centerX() + points[4].fX * width, r.centerY() + points[4].fY * height);
246 fPath.close();
247 }
248
249 bool asRRect(SkRRect* rr) const override {
250 return false;
251 }
252
robertphillipsf619a7a2016-09-12 17:25:50 -0700253 SkPath asPath(SkScalar inset) const override { return fPath; }
robertphillips2af83ac2016-09-12 12:02:16 -0700254
255 void draw(SkCanvas* canvas, const SkPaint& paint) const override {
256 canvas->drawPath(fPath, paint);
257 }
258
259 void clip(SkCanvas* canvas) const override {
robertphillipsf619a7a2016-09-12 17:25:50 -0700260 canvas->clipPath(this->asPath(0.0f));
robertphillips2af83ac2016-09-12 12:02:16 -0700261 }
262
263 bool contains(const SkRect& r) const override {
264 return false;
265 }
266
267 const SkRect& bounds() const override {
268 return fPath.getBounds();
269 }
270
271 static Object* Make(const SkRect& r) {
272 return new Pentagon(r);
273 }
274
275private:
276 SkPath fPath;
277};
robertphillips05a4cf52016-09-08 09:02:43 -0700278
279///////////////////////////////////////////////////////////////////////////////////////////////////
280namespace skiagm {
281
282// This GM attempts to mimic Android's reveal animation
283class RevealGM : public GM {
284public:
robertphillips2af83ac2016-09-12 12:02:16 -0700285 enum Mode {
286 kGaussianEdge_Mode,
287 kBlurMask_Mode,
288 kRRectsGaussianEdge_Mode,
289
290 kLast_Mode = kRRectsGaussianEdge_Mode
291 };
292
293 static const int kModeCount = kLast_Mode + 1;
294
robertphillipsf619a7a2016-09-12 17:25:50 -0700295 RevealGM() : fFraction(0.5f), fMode(kRRectsGaussianEdge_Mode), fPause(false) {
robertphillips05a4cf52016-09-08 09:02:43 -0700296 this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
297 }
298
299protected:
300
301 SkString onShortName() override {
302 return SkString("reveal");
303 }
304
305 SkISize onISize() override {
306 return SkISize::Make(kNumCols * kCellSize, kNumRows * kCellSize);
307 }
308
309 void onDraw(SkCanvas* canvas) override {
robertphillips2af83ac2016-09-12 12:02:16 -0700310 PFMakeMthd clipMakes[kNumCols] = { Oval::Make, Rect::Make };
311 PFMakeMthd drawMakes[kNumRows] = {
312 RRect::Make, StrokedRRect::Make, Oval::Make, Rect::Make, Pentagon::Make
robertphillips05a4cf52016-09-08 09:02:43 -0700313 };
314
315 SkPaint strokePaint;
316 strokePaint.setColor(SK_ColorGREEN);
317 strokePaint.setStyle(SkPaint::kStroke_Style);
318 strokePaint.setStrokeWidth(0.0f);
319
320 for (int y = 0; y < kNumRows; ++y) {
321 for (int x = 0; x < kNumCols; ++x) {
322 SkRect cell = SkRect::MakeXYWH(SkIntToScalar(x*kCellSize),
323 SkIntToScalar(y*kCellSize),
324 SkIntToScalar(kCellSize),
325 SkIntToScalar(kCellSize));
326
robertphillips2af83ac2016-09-12 12:02:16 -0700327 canvas->save();
328 canvas->clipRect(cell);
329
robertphillips05a4cf52016-09-08 09:02:43 -0700330 cell.inset(kPad, kPad);
331 SkPoint clipCenter = SkPoint::Make(cell.centerX() - kClipOffset,
332 cell.centerY() + kClipOffset);
robertphillips05a4cf52016-09-08 09:02:43 -0700333 SkScalar curSize = kCellSize * fFraction;
robertphillips2af83ac2016-09-12 12:02:16 -0700334 const SkRect clipRect = SkRect::MakeLTRB(clipCenter.fX - curSize,
335 clipCenter.fY - curSize,
336 clipCenter.fX + curSize,
337 clipCenter.fY + curSize);
338
339 SkAutoTDelete<Object> clipObj((*clipMakes[x])(clipRect));
340 SkAutoTDelete<Object> drawObj((*drawMakes[y])(cell));
robertphillips05a4cf52016-09-08 09:02:43 -0700341
342 // The goal is to replace this clipped draw (which clips the
343 // shadow) with a draw using the geometric clip
robertphillips2af83ac2016-09-12 12:02:16 -0700344 if (kGaussianEdge_Mode == fMode) {
robertphillips05a4cf52016-09-08 09:02:43 -0700345 canvas->save();
robertphillips2af83ac2016-09-12 12:02:16 -0700346 clipObj->clip(canvas);
347
348 // Draw with GaussianEdgeShader
349 SkPaint paint;
350 paint.setAntiAlias(true);
351 // G channel is an F6.2 radius
robertphillipsec895342016-09-15 13:18:15 -0700352 paint.setColor(SkColorSetARGB(255, 0, (unsigned char)(4*kBlurRadius), 0));
robertphillips2af83ac2016-09-12 12:02:16 -0700353 paint.setShader(SkGaussianEdgeShader::Make());
354 drawObj->draw(canvas, paint);
robertphillips05a4cf52016-09-08 09:02:43 -0700355 canvas->restore();
robertphillips2af83ac2016-09-12 12:02:16 -0700356 } else if (kBlurMask_Mode == fMode) {
357 SkPath clippedPath;
robertphillips05a4cf52016-09-08 09:02:43 -0700358
robertphillipsec895342016-09-15 13:18:15 -0700359 SkScalar sigma = kBlurRadius / 4.0f;
robertphillipsf619a7a2016-09-12 17:25:50 -0700360
robertphillips2af83ac2016-09-12 12:02:16 -0700361 if (clipObj->contains(drawObj->bounds())) {
robertphillipsf619a7a2016-09-12 17:25:50 -0700362 clippedPath = drawObj->asPath(2.0f*sigma);
robertphillips2af83ac2016-09-12 12:02:16 -0700363 } else {
robertphillipsf619a7a2016-09-12 17:25:50 -0700364 SkPath drawnPath = drawObj->asPath(2.0f*sigma);
365 SkPath clipPath = clipObj->asPath(2.0f*sigma);
robertphillips05a4cf52016-09-08 09:02:43 -0700366
robertphillips2af83ac2016-09-12 12:02:16 -0700367 SkAssertResult(Op(clipPath, drawnPath, kIntersect_SkPathOp, &clippedPath));
368 }
robertphillips05a4cf52016-09-08 09:02:43 -0700369
370 SkPaint blurPaint;
371 blurPaint.setAntiAlias(true);
robertphillipsf619a7a2016-09-12 17:25:50 -0700372 blurPaint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma));
robertphillips05a4cf52016-09-08 09:02:43 -0700373 canvas->drawPath(clippedPath, blurPaint);
robertphillips2af83ac2016-09-12 12:02:16 -0700374 } else {
375 SkASSERT(kRRectsGaussianEdge_Mode == fMode);
376
377 SkRect cover = drawObj->bounds();
378 SkAssertResult(cover.intersect(clipObj->bounds()));
379
380 SkPaint paint;
381
382 SkRRect clipRR, drawnRR;
383
384 if (clipObj->asRRect(&clipRR) && drawObj->asRRect(&drawnRR)) {
385 paint.setShader(SkRRectsGaussianEdgeShader::Make(clipRR, drawnRR,
robertphillipsec895342016-09-15 13:18:15 -0700386 kBlurRadius));
robertphillips2af83ac2016-09-12 12:02:16 -0700387 }
388
389 canvas->drawRect(cover, paint);
robertphillips05a4cf52016-09-08 09:02:43 -0700390 }
robertphillips2af83ac2016-09-12 12:02:16 -0700391
392 // Draw the clip and draw objects for reference
393 SkPaint strokePaint;
394 strokePaint.setStyle(SkPaint::kStroke_Style);
395 strokePaint.setStrokeWidth(0);
396 strokePaint.setColor(SK_ColorRED);
robertphillipsf619a7a2016-09-12 17:25:50 -0700397 canvas->drawPath(drawObj->asPath(0.0f), strokePaint);
robertphillips2af83ac2016-09-12 12:02:16 -0700398 strokePaint.setColor(SK_ColorGREEN);
robertphillipsf619a7a2016-09-12 17:25:50 -0700399 canvas->drawPath(clipObj->asPath(0.0f), strokePaint);
robertphillips2af83ac2016-09-12 12:02:16 -0700400
401 canvas->restore();
robertphillips05a4cf52016-09-08 09:02:43 -0700402 }
403 }
404 }
405
406 bool onHandleKey(SkUnichar uni) override {
407 switch (uni) {
408 case 'C':
robertphillips2af83ac2016-09-12 12:02:16 -0700409 fMode = (Mode)((fMode + 1) % kModeCount);
robertphillips05a4cf52016-09-08 09:02:43 -0700410 return true;
robertphillipsf619a7a2016-09-12 17:25:50 -0700411 case 'p':
412 fPause = !fPause;
413 return true;
robertphillips05a4cf52016-09-08 09:02:43 -0700414 }
415
416 return false;
417 }
418
419 bool onAnimate(const SkAnimTimer& timer) override {
robertphillipsf619a7a2016-09-12 17:25:50 -0700420 if (!fPause) {
421 fFraction = timer.pingPong(kPeriod, 0.0f, 0.0f, 1.0f);
422 }
robertphillips05a4cf52016-09-08 09:02:43 -0700423 return true;
424 }
425
426private:
427 SkScalar fFraction;
robertphillips2af83ac2016-09-12 12:02:16 -0700428 Mode fMode;
robertphillipsf619a7a2016-09-12 17:25:50 -0700429 bool fPause;
robertphillips05a4cf52016-09-08 09:02:43 -0700430
431 typedef GM INHERITED;
432};
433
434//////////////////////////////////////////////////////////////////////////////
435
436DEF_GM(return new RevealGM;)
437}