| /* | |
| * 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/gm.h" | |
| #include "include/core/SkBlurTypes.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkMaskFilter.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "src/core/SkBlurMask.h" | |
| #include "src/core/SkRandom.h" | |
| #include "tools/timer/TimeUtils.h" | |
| /** | |
| * In GM mode this draws an array of circles with different radii and different blur radii. Below | |
| * each circle an almost-circle path is drawn with the same blur filter for comparison. | |
| * | |
| * In Sample mode this draws a single circle and almost-circle with animating radius and blur | |
| * radius. | |
| * | |
| * Bench mode draws the same as GM mode but without the comparison almost-circle paths. It also | |
| * slightly perturbs the blur and circle radii to stress caching of blurred profiles in GPU mode. | |
| */ | |
| class BlurCircles2GM : public skiagm::GM { | |
| public: | |
| BlurCircles2GM() { | |
| fAnimRadius = TimeUtils::PingPong( | |
| 0, kRadiusPingPoingPeriod, kRadiusPingPoingShift, kMinRadius, kMaxRadius); | |
| fAnimBlurRadius = TimeUtils::PingPong(0, | |
| kBlurRadiusPingPoingPeriod, | |
| kBlurRadiusPingPoingShift, | |
| kMinBlurRadius, | |
| kMaxBlurRadius); | |
| } | |
| protected: | |
| bool runAsBench() const override { return true; } | |
| SkString getName() const override { return SkString("blurcircles2"); } | |
| SkISize getISize() override { return SkISize::Make(730, 1350); } | |
| void onDraw(SkCanvas* canvas) override { | |
| constexpr SkScalar kMaxR = kMaxRadius + kMaxBlurRadius; | |
| auto almostCircleMaker = [] (SkScalar radius) { | |
| return SkPathBuilder().addArc(SkRect::MakeXYWH(-radius, -radius, 2 * radius, 2 * radius), 0, 355) | |
| .setIsVolatile(true) | |
| .close() | |
| .detach(); | |
| }; | |
| auto blurMaker = [] (SkScalar radius) ->sk_sp<SkMaskFilter> { | |
| return SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, | |
| SkBlurMask::ConvertRadiusToSigma(radius)); | |
| }; | |
| SkPaint paint; | |
| paint.setColor(SK_ColorBLACK); | |
| if (this->getMode() == kSample_Mode) { | |
| paint.setMaskFilter(blurMaker(fAnimBlurRadius)); | |
| SkISize size = canvas->getBaseLayerSize(); | |
| SkPath almostCircle = almostCircleMaker(fAnimRadius); | |
| canvas->save(); | |
| canvas->translate(size.fWidth / 2.f, size.fHeight / 4.f); | |
| canvas->drawCircle(0, 0, fAnimRadius, paint); | |
| canvas->translate(0, 2 * kMaxR); | |
| canvas->drawPath(almostCircle, paint); | |
| canvas->restore(); | |
| } else { | |
| bool benchMode = this->getMode() == kBench_Mode; | |
| canvas->save(); | |
| constexpr SkScalar kPad = 5; | |
| constexpr SkScalar kRadiusSteps = 5; | |
| constexpr SkScalar kBlurRadiusSteps = 5; | |
| canvas->translate(kPad + kMinRadius + kMaxBlurRadius, | |
| kPad + kMinRadius + kMaxBlurRadius); | |
| constexpr SkScalar kDeltaRadius = (kMaxRadius - kMinRadius) / kRadiusSteps; | |
| constexpr SkScalar kDeltaBlurRadius = (kMaxBlurRadius - kMinBlurRadius) / | |
| kBlurRadiusSteps; | |
| SkScalar lineWidth = 0; | |
| if (!benchMode) { | |
| for (int r = 0; r < kRadiusSteps - 1; ++r) { | |
| const SkScalar radius = r * kDeltaRadius + kMinRadius; | |
| lineWidth += 2 * (radius + kMaxBlurRadius) + kPad; | |
| } | |
| } | |
| for (int br = 0; br < kBlurRadiusSteps; ++br) { | |
| SkScalar blurRadius = br * kDeltaBlurRadius + kMinBlurRadius; | |
| if (benchMode) { | |
| blurRadius += fRandom.nextSScalar1() * kDeltaBlurRadius; | |
| } | |
| const SkScalar maxRowR = blurRadius + kMaxRadius; | |
| paint.setMaskFilter(blurMaker(blurRadius)); | |
| canvas->save(); | |
| for (int r = 0; r < kRadiusSteps; ++r) { | |
| SkScalar radius = r * kDeltaRadius + kMinRadius; | |
| if (benchMode) { | |
| radius += fRandom.nextSScalar1() * kDeltaRadius; | |
| } | |
| SkPath almostCircle; | |
| if (!benchMode) { | |
| almostCircle = almostCircleMaker(radius); | |
| } | |
| canvas->save(); | |
| canvas->drawCircle(0, 0, radius, paint); | |
| canvas->translate(0, 2 * maxRowR + kPad); | |
| if (!benchMode) { | |
| canvas->drawPath(almostCircle, paint); | |
| } | |
| canvas->restore(); | |
| const SkScalar maxColR = radius + kMaxBlurRadius; | |
| canvas->translate(maxColR * 2 + kPad, 0); | |
| } | |
| canvas->restore(); | |
| if (!benchMode) { | |
| SkPaint blackPaint; | |
| blackPaint.setColor(SK_ColorBLACK); | |
| const SkScalar lineY = 3 * maxRowR + 1.5f * kPad; | |
| if (br != kBlurRadiusSteps - 1) { | |
| canvas->drawLine(0, lineY, lineWidth, lineY, blackPaint); | |
| } | |
| } | |
| canvas->translate(0, maxRowR * 4 + 2 * kPad); | |
| } | |
| canvas->restore(); | |
| } | |
| } | |
| bool onAnimate(double nanos) override { | |
| fAnimRadius = TimeUtils::PingPong(1e-9 * nanos, kRadiusPingPoingPeriod, kRadiusPingPoingShift, kMinRadius, | |
| kMaxRadius); | |
| fAnimBlurRadius = TimeUtils::PingPong(1e-9 * nanos, kBlurRadiusPingPoingPeriod, kBlurRadiusPingPoingShift, | |
| kMinBlurRadius, kMaxBlurRadius); | |
| return true; | |
| } | |
| private: | |
| inline static constexpr SkScalar kMinRadius = 15; | |
| inline static constexpr SkScalar kMaxRadius = 45; | |
| inline static constexpr SkScalar kRadiusPingPoingPeriod = 8; | |
| inline static constexpr SkScalar kRadiusPingPoingShift = 3; | |
| inline static constexpr SkScalar kMinBlurRadius = 5; | |
| inline static constexpr SkScalar kMaxBlurRadius = 45; | |
| inline static constexpr SkScalar kBlurRadiusPingPoingPeriod = 3; | |
| inline static constexpr SkScalar kBlurRadiusPingPoingShift = 1.5; | |
| SkScalar fAnimRadius; | |
| SkScalar fAnimBlurRadius; | |
| SkRandom fRandom; | |
| using INHERITED = skiagm::GM; | |
| }; | |
| DEF_GM(return new BlurCircles2GM();) |