1 /*
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/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkImageFilter.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkRRect.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkSize.h"
17 #include "include/core/SkString.h"
18 #include "include/core/SkTypes.h"
19 #include "include/effects/SkImageFilters.h"
20 #include "include/private/SkTPin.h"
21 #include "include/utils/SkRandom.h"
22 #include "tools/timer/TimeUtils.h"
23 
24 static const SkScalar kBlurMax = 7.0f;
25 static const int kNumNodes = 30;
26 static const int kWidth = 512;
27 static const int kHeight = 512;
28 static const SkScalar kBlurAnimationDuration = 4.0f; // in secs
29 
30 // This GM draws a lot of layers with animating BlurImageFilters
31 class AnimatedImageBlurs : public skiagm::GM {
32 public:
AnimatedImageBlurs()33     AnimatedImageBlurs() : fLastTime(0.0f) {
34         this->setBGColor(0xFFCCCCCC);
35     }
36 
37 protected:
runAsBench() const38     bool runAsBench() const override { return true; }
39 
onShortName()40     SkString onShortName() override { return SkString("animated-image-blurs"); }
41 
onISize()42     SkISize onISize() override { return SkISize::Make(kWidth, kHeight); }
43 
onOnceBeforeDraw()44     void onOnceBeforeDraw() override {
45         for (int i = 0; i < kNumNodes; ++i) {
46             fNodes[i].init(&fRand);
47         }
48     }
49 
onDraw(SkCanvas * canvas)50     void onDraw(SkCanvas* canvas) override {
51         SkPaint paint;
52         paint.setAntiAlias(true);
53 
54         for (int i = 0; i < kNumNodes; ++i) {
55             SkPaint layerPaint;
56             layerPaint.setImageFilter(SkImageFilters::Blur(fNodes[i].sigma(), fNodes[i].sigma(),
57                                                            nullptr));
58 
59             canvas->saveLayer(nullptr, &layerPaint);
60                 // The rect is outset to block the circle case
61                 SkRect rect = SkRect::MakeLTRB(fNodes[i].pos().fX - fNodes[i].size()-0.5f,
62                                                fNodes[i].pos().fY - fNodes[i].size()-0.5f,
63                                                fNodes[i].pos().fX + fNodes[i].size()+0.5f,
64                                                fNodes[i].pos().fY + fNodes[i].size()+0.5f);
65                 SkRRect rrect = SkRRect::MakeRectXY(rect, fNodes[i].size(), fNodes[i].size());
66                 canvas->drawRRect(rrect, paint);
67             canvas->restore();
68         }
69     }
70 
onAnimate(double nanos)71     bool onAnimate(double nanos) override {
72         if (0.0f != fLastTime) {
73             for (int i = 0; i < kNumNodes; ++i) {
74                 fNodes[i].update(nanos, fLastTime);
75             }
76         }
77 
78         fLastTime = 1e-9 * nanos;
79         return true;
80     }
81 
82 private:
83     class Node {
84     public:
Node()85         Node()
86             : fSize(0.0f)
87             , fPos { 0.0f, 0.0f }
88             , fDir { 1.0f, 0.0f }
89             , fBlurOffset(0.0f)
90             , fBlur(fBlurOffset)
91             , fSpeed(0.0f) {
92         }
93 
init(SkRandom * rand)94         void init(SkRandom* rand) {
95             fSize = rand->nextRangeF(10.0f, 60.f);
96             fPos.fX = rand->nextRangeF(fSize, kWidth - fSize);
97             fPos.fY = rand->nextRangeF(fSize, kHeight - fSize);
98             fDir.fX = rand->nextRangeF(-1.0f, 1.0f);
99             fDir.fY = SkScalarSqrt(1.0f - fDir.fX * fDir.fX);
100             if (rand->nextBool()) {
101                 fDir.fY = -fDir.fY;
102             }
103             fBlurOffset = rand->nextRangeF(0.0f, kBlurMax);
104             fBlur = fBlurOffset;
105             fSpeed = rand->nextRangeF(20.0f, 60.0f);
106         }
107 
update(double nanos,SkScalar lastTime)108         void update(double nanos, SkScalar lastTime) {
109             SkScalar deltaTime = 1e-9 * nanos - lastTime;
110 
111             fPos.fX += deltaTime * fSpeed * fDir.fX;
112             fPos.fY += deltaTime * fSpeed * fDir.fY;
113             if (fPos.fX >= kWidth || fPos.fX < 0.0f) {
114                 fPos.fX = SkTPin<SkScalar>(fPos.fX, 0.0f, kWidth);
115                 fDir.fX = -fDir.fX;
116             }
117             if (fPos.fY >= kHeight || fPos.fY < 0.0f) {
118                 fPos.fY = SkTPin<SkScalar>(fPos.fY, 0.0f, kHeight);
119                 fDir.fY = -fDir.fY;
120             }
121 
122             fBlur = TimeUtils::PingPong(1e-9 * nanos, kBlurAnimationDuration, fBlurOffset, 0.0f, kBlurMax);
123         }
124 
sigma() const125         SkScalar sigma() const { return fBlur; }
pos() const126         const SkPoint& pos() const { return fPos; }
size() const127         SkScalar size() const { return fSize; }
128 
129     private:
130         SkScalar fSize;
131         SkPoint  fPos;
132         SkVector fDir;
133         SkScalar fBlurOffset;
134         SkScalar fBlur;
135         SkScalar fSpeed;
136     };
137 
138     Node     fNodes[kNumNodes];
139     SkRandom fRand;
140     SkScalar fLastTime;
141 
142     using INHERITED = GM;
143 };
144 
145 //////////////////////////////////////////////////////////////////////////////
146 
147 DEF_GM(return new AnimatedImageBlurs;)
148