/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.h"
#include "SkAnimTimer.h"
#include "SkBlurImageFilter.h"
#include "SkRandom.h"
#include "SkRRect.h"
static const SkScalar kBlurMax = 7.0f;
static const int kNumNodes = 30;
static const int kWidth = 512;
static const int kHeight = 512;
static const SkScalar kBlurAnimationDuration = 4.0f; // in secs
// This GM draws a lot of layers with animating BlurImageFilters
class AnimatedImageBlurs : public skiagm::GM {
public:
AnimatedImageBlurs() : fLastTime(0.0f) {
this->setBGColor(0xFFCCCCCC);
}
protected:
bool runAsBench() const override { return true; }
SkString onShortName() override { return SkString("animated-image-blurs"); }
SkISize onISize() override { return SkISize::Make(kWidth, kHeight); }
void onOnceBeforeDraw() override {
for (int i = 0; i < kNumNodes; ++i) {
fNodes[i].init(&fRand);
}
}
void onDraw(SkCanvas* canvas) override {
SkPaint paint;
paint.setAntiAlias(true);
for (int i = 0; i < kNumNodes; ++i) {
SkPaint layerPaint;
layerPaint.setImageFilter(SkBlurImageFilter::Make(fNodes[i].sigma(),
fNodes[i].sigma(),
nullptr));
canvas->saveLayer(nullptr, &layerPaint);
// The rect is outset to block the circle case
SkRect rect = SkRect::MakeLTRB(fNodes[i].pos().fX - fNodes[i].size()-0.5f,
fNodes[i].pos().fY - fNodes[i].size()-0.5f,
fNodes[i].pos().fX + fNodes[i].size()+0.5f,
fNodes[i].pos().fY + fNodes[i].size()+0.5f);
SkRRect rrect = SkRRect::MakeRectXY(rect, fNodes[i].size(), fNodes[i].size());
canvas->drawRRect(rrect, paint);
canvas->restore();
}
}
bool onAnimate(const SkAnimTimer& timer) override {
if (0.0f != fLastTime) {
for (int i = 0; i < kNumNodes; ++i) {
fNodes[i].update(timer, fLastTime);
}
}
fLastTime = timer.secs();
return true;
}
private:
class Node {
public:
Node()
: fSize(0.0f)
, fPos { 0.0f, 0.0f }
, fDir { 1.0f, 0.0f }
, fBlurOffset(0.0f)
, fBlur(fBlurOffset)
, fSpeed(0.0f) {
}
void init(SkRandom* rand) {
fSize = rand->nextRangeF(10.0f, 60.f);
fPos.fX = rand->nextRangeF(fSize, kWidth - fSize);
fPos.fY = rand->nextRangeF(fSize, kHeight - fSize);
fDir.fX = rand->nextRangeF(-1.0f, 1.0f);
fDir.fY = SkScalarSqrt(1.0f - fDir.fX * fDir.fX);
if (rand->nextBool()) {
fDir.fY = -fDir.fY;
}
fBlurOffset = rand->nextRangeF(0.0f, kBlurMax);
fBlur = fBlurOffset;
fSpeed = rand->nextRangeF(20.0f, 60.0f);
}
void update(const SkAnimTimer& timer, SkScalar lastTime) {
SkScalar deltaTime = timer.secs() - lastTime;
fPos.fX += deltaTime * fSpeed * fDir.fX;
fPos.fY += deltaTime * fSpeed * fDir.fY;
if (fPos.fX >= kWidth || fPos.fX < 0.0f) {
fPos.fX = SkTPin<SkScalar>(fPos.fX, 0.0f, kWidth);
fDir.fX = -fDir.fX;
}
if (fPos.fY >= kHeight || fPos.fY < 0.0f) {
fPos.fY = SkTPin<SkScalar>(fPos.fY, 0.0f, kHeight);
fDir.fY = -fDir.fY;
}
fBlur = timer.pingPong(kBlurAnimationDuration, fBlurOffset, 0.0f, kBlurMax);
}
SkScalar sigma() const { return fBlur; }
const SkPoint& pos() const { return fPos; }
SkScalar size() const { return fSize; }
private:
SkScalar fSize;
SkPoint fPos;
SkVector fDir;
SkScalar fBlurOffset;
SkScalar fBlur;
SkScalar fSpeed;
};
Node fNodes[kNumNodes];
SkRandom fRand;
SkScalar fLastTime;
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new AnimatedImageBlurs;)