1c67bb575d0393bef8517810c8f7c578804403c61bsalomon/*
2c67bb575d0393bef8517810c8f7c578804403c61bsalomon* Copyright 2016 Google Inc.
3c67bb575d0393bef8517810c8f7c578804403c61bsalomon*
4c67bb575d0393bef8517810c8f7c578804403c61bsalomon* Use of this source code is governed by a BSD-style license that can be
5c67bb575d0393bef8517810c8f7c578804403c61bsalomon* found in the LICENSE file.
6c67bb575d0393bef8517810c8f7c578804403c61bsalomon*/
7c67bb575d0393bef8517810c8f7c578804403c61bsalomon
8c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "gm.h"
9c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkAnimTimer.h"
10c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkBlurMask.h"
11c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkBlurMaskFilter.h"
12c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkCanvas.h"
13c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkPaint.h"
14c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkPath.h"
15c67bb575d0393bef8517810c8f7c578804403c61bsalomon#include "SkString.h"
1633d2055e594177b27360f84e0631b26d74a55a9bMike Klein#include "SkRandom.h"
17c67bb575d0393bef8517810c8f7c578804403c61bsalomon
18c67bb575d0393bef8517810c8f7c578804403c61bsalomon/**
19c67bb575d0393bef8517810c8f7c578804403c61bsalomon * In GM mode this draws an array of circles with different radii and different blur radii. Below
20c67bb575d0393bef8517810c8f7c578804403c61bsalomon * each circle an almost-circle path is drawn with the same blur filter for comparison.
21c67bb575d0393bef8517810c8f7c578804403c61bsalomon *
22c67bb575d0393bef8517810c8f7c578804403c61bsalomon * In Sample mode this draws a single circle and almost-circle with animating radius and blur
23c67bb575d0393bef8517810c8f7c578804403c61bsalomon * radius.
242996553ba23212e345ba611772fcc6ee1574cb7ebsalomon *
252996553ba23212e345ba611772fcc6ee1574cb7ebsalomon * Bench mode draws the same as GM mode but without the comparison almost-circle paths. It also
262996553ba23212e345ba611772fcc6ee1574cb7ebsalomon * slightly perturbs the blur and circle radii to stress caching of blurred profiles in GPU mode.
27c67bb575d0393bef8517810c8f7c578804403c61bsalomon */
28c67bb575d0393bef8517810c8f7c578804403c61bsalomonclass BlurCircles2GM : public skiagm::GM {
29c67bb575d0393bef8517810c8f7c578804403c61bsalomonpublic:
30c67bb575d0393bef8517810c8f7c578804403c61bsalomon    BlurCircles2GM() {
31c67bb575d0393bef8517810c8f7c578804403c61bsalomon        fAnimRadius = SkAnimTimer::PingPong(0, kRadiusPingPoingPeriod, kRadiusPingPoingShift,
32c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                            kMinRadius, kMaxRadius);
33c67bb575d0393bef8517810c8f7c578804403c61bsalomon        fAnimBlurRadius = SkAnimTimer::PingPong(0, kBlurRadiusPingPoingPeriod,
34c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                                kBlurRadiusPingPoingShift, kMinBlurRadius,
35c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                                kMaxBlurRadius);
36c67bb575d0393bef8517810c8f7c578804403c61bsalomon    }
37c67bb575d0393bef8517810c8f7c578804403c61bsalomon
38c67bb575d0393bef8517810c8f7c578804403c61bsalomonprotected:
392996553ba23212e345ba611772fcc6ee1574cb7ebsalomon    bool runAsBench() const override { return true; }
402996553ba23212e345ba611772fcc6ee1574cb7ebsalomon
41c67bb575d0393bef8517810c8f7c578804403c61bsalomon    SkString onShortName() override { return SkString("blurcircles2"); }
42c67bb575d0393bef8517810c8f7c578804403c61bsalomon
43c67bb575d0393bef8517810c8f7c578804403c61bsalomon    SkISize onISize() override {
44c67bb575d0393bef8517810c8f7c578804403c61bsalomon        return SkISize::Make(730, 1350);
45c67bb575d0393bef8517810c8f7c578804403c61bsalomon    }
46c67bb575d0393bef8517810c8f7c578804403c61bsalomon
47c67bb575d0393bef8517810c8f7c578804403c61bsalomon    void onDraw(SkCanvas* canvas) override {
48dbfd7ab10883f173f5c1b653a233e18dc6142002mtklein        constexpr SkScalar kMaxR = kMaxRadius + kMaxBlurRadius;
49c67bb575d0393bef8517810c8f7c578804403c61bsalomon
50c67bb575d0393bef8517810c8f7c578804403c61bsalomon        auto almostCircleMaker = [] (SkScalar radius, SkPath* dst) {
51c67bb575d0393bef8517810c8f7c578804403c61bsalomon            dst->reset();
52c67bb575d0393bef8517810c8f7c578804403c61bsalomon            dst->addArc(SkRect::MakeXYWH(-radius, -radius, 2 * radius, 2 * radius), 0, 355);
53c67bb575d0393bef8517810c8f7c578804403c61bsalomon            dst->setIsVolatile(true);
54c67bb575d0393bef8517810c8f7c578804403c61bsalomon            dst->close();
55c67bb575d0393bef8517810c8f7c578804403c61bsalomon        };
56c67bb575d0393bef8517810c8f7c578804403c61bsalomon
57c67bb575d0393bef8517810c8f7c578804403c61bsalomon        auto blurMaker = [] (SkScalar radius) ->sk_sp<SkMaskFilter> {
58c67bb575d0393bef8517810c8f7c578804403c61bsalomon            return SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
59c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                          SkBlurMask::ConvertRadiusToSigma(radius),
60c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                          SkBlurMaskFilter::kHighQuality_BlurFlag);
61c67bb575d0393bef8517810c8f7c578804403c61bsalomon        };
62c67bb575d0393bef8517810c8f7c578804403c61bsalomon
63c67bb575d0393bef8517810c8f7c578804403c61bsalomon        SkPaint paint;
64c67bb575d0393bef8517810c8f7c578804403c61bsalomon        paint.setColor(SK_ColorBLACK);
65c67bb575d0393bef8517810c8f7c578804403c61bsalomon
66c67bb575d0393bef8517810c8f7c578804403c61bsalomon        if (this->getMode() == kSample_Mode) {
67c67bb575d0393bef8517810c8f7c578804403c61bsalomon            paint.setMaskFilter(blurMaker(fAnimBlurRadius));
68c67bb575d0393bef8517810c8f7c578804403c61bsalomon            SkISize size = canvas->getBaseLayerSize();
69c67bb575d0393bef8517810c8f7c578804403c61bsalomon            SkPath almostCircle;
70c67bb575d0393bef8517810c8f7c578804403c61bsalomon            almostCircleMaker(fAnimRadius, &almostCircle);
71c67bb575d0393bef8517810c8f7c578804403c61bsalomon            canvas->save();
72c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->translate(size.fWidth / 2.f, size.fHeight / 4.f);
73c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->drawCircle(0, 0, fAnimRadius, paint);
74c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->translate(0, 2 * kMaxR);
75c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->drawPath(almostCircle, paint);
76c67bb575d0393bef8517810c8f7c578804403c61bsalomon            canvas->restore();
77c67bb575d0393bef8517810c8f7c578804403c61bsalomon        } else {
782996553ba23212e345ba611772fcc6ee1574cb7ebsalomon            bool benchMode = this->getMode() == kBench_Mode;
79c67bb575d0393bef8517810c8f7c578804403c61bsalomon            canvas->save();
80dbfd7ab10883f173f5c1b653a233e18dc6142002mtklein            constexpr SkScalar kPad = 5;
81dbfd7ab10883f173f5c1b653a233e18dc6142002mtklein            constexpr SkScalar kRadiusSteps = 5;
82dbfd7ab10883f173f5c1b653a233e18dc6142002mtklein            constexpr SkScalar kBlurRadiusSteps = 5;
83c67bb575d0393bef8517810c8f7c578804403c61bsalomon            canvas->translate(kPad + kMinRadius + kMaxBlurRadius,
84c67bb575d0393bef8517810c8f7c578804403c61bsalomon                              kPad + kMinRadius + kMaxBlurRadius);
85dbfd7ab10883f173f5c1b653a233e18dc6142002mtklein            constexpr SkScalar kDeltaRadius = (kMaxRadius - kMinRadius) / kRadiusSteps;
86dbfd7ab10883f173f5c1b653a233e18dc6142002mtklein            constexpr SkScalar kDeltaBlurRadius = (kMaxBlurRadius - kMinBlurRadius) /
872996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                                                         kBlurRadiusSteps;
88c67bb575d0393bef8517810c8f7c578804403c61bsalomon            SkScalar lineWidth = 0;
892996553ba23212e345ba611772fcc6ee1574cb7ebsalomon            if (!benchMode) {
902996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                for (int r = 0; r < kRadiusSteps - 1; ++r) {
912996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    const SkScalar radius = r * kDeltaRadius + kMinRadius;
922996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    lineWidth += 2 * (radius + kMaxBlurRadius) + kPad;
932996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                }
94c67bb575d0393bef8517810c8f7c578804403c61bsalomon            }
95c67bb575d0393bef8517810c8f7c578804403c61bsalomon            for (int br = 0; br < kBlurRadiusSteps; ++br) {
962996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                SkScalar blurRadius = br * kDeltaBlurRadius + kMinBlurRadius;
972996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                if (benchMode) {
982996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    blurRadius += fRandom.nextSScalar1() * kDeltaBlurRadius;
992996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                }
100c67bb575d0393bef8517810c8f7c578804403c61bsalomon                const SkScalar maxRowR = blurRadius + kMaxRadius;
101c67bb575d0393bef8517810c8f7c578804403c61bsalomon                paint.setMaskFilter(blurMaker(blurRadius));
102c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->save();
103c67bb575d0393bef8517810c8f7c578804403c61bsalomon                for (int r = 0; r < kRadiusSteps; ++r) {
1042996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    SkScalar radius = r * kDeltaRadius + kMinRadius;
1052996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    if (benchMode) {
1062996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                        radius += fRandom.nextSScalar1() * kDeltaRadius;
1072996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    }
108c67bb575d0393bef8517810c8f7c578804403c61bsalomon                    SkPath almostCircle;
1092996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    if (!benchMode) {
1102996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                        almostCircleMaker(radius, &almostCircle);
1112996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    }
112c67bb575d0393bef8517810c8f7c578804403c61bsalomon                    canvas->save();
113c67bb575d0393bef8517810c8f7c578804403c61bsalomon                        canvas->drawCircle(0, 0, radius, paint);
114c67bb575d0393bef8517810c8f7c578804403c61bsalomon                        canvas->translate(0, 2 * maxRowR + kPad);
1152996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    if (!benchMode) {
116c67bb575d0393bef8517810c8f7c578804403c61bsalomon                        canvas->drawPath(almostCircle, paint);
1172996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    }
118c67bb575d0393bef8517810c8f7c578804403c61bsalomon                    canvas->restore();
119c67bb575d0393bef8517810c8f7c578804403c61bsalomon                    const SkScalar maxColR = radius + kMaxBlurRadius;
120c67bb575d0393bef8517810c8f7c578804403c61bsalomon                    canvas->translate(maxColR * 2 + kPad, 0);
121c67bb575d0393bef8517810c8f7c578804403c61bsalomon                }
122c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->restore();
1232996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                if (!benchMode) {
1242996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    SkPaint blackPaint;
1252996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    blackPaint.setColor(SK_ColorBLACK);
1262996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    const SkScalar lineY = 3 * maxRowR + 1.5f * kPad;
1272996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    if (br != kBlurRadiusSteps - 1) {
1282996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                        canvas->drawLine(0, lineY, lineWidth, lineY, blackPaint);
1292996553ba23212e345ba611772fcc6ee1574cb7ebsalomon                    }
130c67bb575d0393bef8517810c8f7c578804403c61bsalomon                }
131c67bb575d0393bef8517810c8f7c578804403c61bsalomon                canvas->translate(0, maxRowR * 4 + 2 * kPad);
132c67bb575d0393bef8517810c8f7c578804403c61bsalomon            }
133c67bb575d0393bef8517810c8f7c578804403c61bsalomon            canvas->restore();
134c67bb575d0393bef8517810c8f7c578804403c61bsalomon        }
135c67bb575d0393bef8517810c8f7c578804403c61bsalomon    }
136c67bb575d0393bef8517810c8f7c578804403c61bsalomon
137c67bb575d0393bef8517810c8f7c578804403c61bsalomon    bool onAnimate(const SkAnimTimer& timer) override {
138c67bb575d0393bef8517810c8f7c578804403c61bsalomon        fAnimRadius = timer.pingPong(kRadiusPingPoingPeriod, kRadiusPingPoingShift, kMinRadius,
139c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                     kMaxRadius);
140c67bb575d0393bef8517810c8f7c578804403c61bsalomon        fAnimBlurRadius = timer.pingPong(kBlurRadiusPingPoingPeriod, kBlurRadiusPingPoingShift,
141c67bb575d0393bef8517810c8f7c578804403c61bsalomon                                         kMinBlurRadius, kMaxBlurRadius);
142c67bb575d0393bef8517810c8f7c578804403c61bsalomon        return true;
143c67bb575d0393bef8517810c8f7c578804403c61bsalomon    }
144c67bb575d0393bef8517810c8f7c578804403c61bsalomon
145c67bb575d0393bef8517810c8f7c578804403c61bsalomonprivate:
146c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kMinRadius = 15;
147c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kMaxRadius = 45;
148c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kRadiusPingPoingPeriod = 8;
149c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kRadiusPingPoingShift = 3;
150c67bb575d0393bef8517810c8f7c578804403c61bsalomon
151c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kMinBlurRadius = 5;
152c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kMaxBlurRadius = 45;
153c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kBlurRadiusPingPoingPeriod = 3;
154c67bb575d0393bef8517810c8f7c578804403c61bsalomon    static constexpr SkScalar kBlurRadiusPingPoingShift = 1.5;
155c67bb575d0393bef8517810c8f7c578804403c61bsalomon
156c67bb575d0393bef8517810c8f7c578804403c61bsalomon    SkScalar    fAnimRadius;
157c67bb575d0393bef8517810c8f7c578804403c61bsalomon    SkScalar    fAnimBlurRadius;
158c67bb575d0393bef8517810c8f7c578804403c61bsalomon
1592996553ba23212e345ba611772fcc6ee1574cb7ebsalomon    SkRandom    fRandom;
1602996553ba23212e345ba611772fcc6ee1574cb7ebsalomon
161c67bb575d0393bef8517810c8f7c578804403c61bsalomon    typedef skiagm::GM INHERITED;
162c67bb575d0393bef8517810c8f7c578804403c61bsalomon};
163c67bb575d0393bef8517810c8f7c578804403c61bsalomon
164c67bb575d0393bef8517810c8f7c578804403c61bsalomonDEF_GM(return new BlurCircles2GM();)
165