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