1/*
2 * Copyright 2014 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 "SampleCode.h"
9#include "SkAnimTimer.h"
10#include "SkView.h"
11#include "SkCanvas.h"
12#include "SkDrawable.h"
13#include "SkInterpolator.h"
14#include "SkPictureRecorder.h"
15#include "SkRandom.h"
16
17const SkRect gUnitSquare = { -1, -1, 1, 1 };
18
19static void color_to_floats(SkColor c, SkScalar f[4]) {
20    f[0] = SkIntToScalar(SkColorGetA(c));
21    f[1] = SkIntToScalar(SkColorGetR(c));
22    f[2] = SkIntToScalar(SkColorGetG(c));
23    f[3] = SkIntToScalar(SkColorGetB(c));
24}
25
26static SkColor floats_to_color(const SkScalar f[4]) {
27    return SkColorSetARGB(SkScalarRoundToInt(f[0]),
28                          SkScalarRoundToInt(f[1]),
29                          SkScalarRoundToInt(f[2]),
30                          SkScalarRoundToInt(f[3]));
31}
32
33static bool oval_contains(const SkRect& r, SkScalar x, SkScalar y) {
34    SkMatrix m;
35    m.setRectToRect(r, gUnitSquare, SkMatrix::kFill_ScaleToFit);
36    SkPoint pt;
37    m.mapXY(x, y, &pt);
38    return pt.lengthSqd() <= 1;
39}
40
41static SkColor rand_opaque_color(uint32_t seed) {
42    SkRandom rand(seed);
43    return rand.nextU() | (0xFF << 24);
44}
45
46class HTDrawable : public SkDrawable {
47    SkRect          fR;
48    SkColor         fColor;
49    SkInterpolator* fInterp;
50    SkMSec          fTime;
51
52public:
53    HTDrawable(SkRandom& rand) {
54        fR = SkRect::MakeXYWH(rand.nextRangeF(0, 640), rand.nextRangeF(0, 480),
55                              rand.nextRangeF(20, 200), rand.nextRangeF(20, 200));
56        fColor = rand_opaque_color(rand.nextU());
57        fInterp = nullptr;
58        fTime = 0;
59    }
60
61    void spawnAnimation(SkMSec now) {
62        this->setTime(now);
63
64        delete fInterp;
65        fInterp = new SkInterpolator(5, 3);
66        SkScalar values[5];
67        color_to_floats(fColor, values); values[4] = 0;
68        fInterp->setKeyFrame(0, now, values);
69        values[0] = 0; values[4] = 180;
70        fInterp->setKeyFrame(1, now + 1000, values);
71        color_to_floats(rand_opaque_color(fColor), values); values[4] = 360;
72        fInterp->setKeyFrame(2, now + 2000, values);
73
74        fInterp->setMirror(true);
75        fInterp->setRepeatCount(3);
76
77        this->notifyDrawingChanged();
78    }
79
80    bool hitTest(SkScalar x, SkScalar y) {
81        return oval_contains(fR, x, y);
82    }
83
84    void setTime(SkMSec time) { fTime = time; }
85
86    void onDraw(SkCanvas* canvas) override {
87        SkAutoCanvasRestore acr(canvas, false);
88
89        SkPaint paint;
90        paint.setAntiAlias(true);
91
92        if (fInterp) {
93            SkScalar values[5];
94            SkInterpolator::Result res = fInterp->timeToValues(fTime, values);
95            fColor = floats_to_color(values);
96
97            canvas->save();
98            canvas->rotate(values[4], fR.centerX(), fR.centerY());
99
100            switch (res) {
101                case SkInterpolator::kFreezeEnd_Result:
102                    delete fInterp;
103                    fInterp = nullptr;
104                    break;
105                default:
106                    break;
107            }
108        }
109        paint.setColor(fColor);
110        canvas->drawRect(fR, paint);
111    }
112
113    SkRect onGetBounds() override { return fR; }
114};
115
116class HTView : public SampleView {
117public:
118    enum {
119        N = 50,
120        W = 640,
121        H = 480,
122    };
123
124    struct Rec {
125        HTDrawable* fDrawable;
126    };
127    Rec fArray[N];
128    sk_sp<SkDrawable> fRoot;
129    SkMSec fTime;
130
131    HTView() {
132        SkRandom rand;
133
134        SkPictureRecorder recorder;
135        SkCanvas* canvas = recorder.beginRecording(SkRect::MakeWH(W, H));
136        for (int i = 0; i < N; ++i) {
137            fArray[i].fDrawable = new HTDrawable(rand);
138            canvas->drawDrawable(fArray[i].fDrawable);
139            fArray[i].fDrawable->unref();
140        }
141        fRoot = recorder.finishRecordingAsDrawable();
142    }
143
144protected:
145    bool onQuery(SkEvent* evt) override {
146        if (SampleCode::TitleQ(*evt)) {
147            SampleCode::TitleR(evt, "HT");
148            return true;
149        }
150        return this->INHERITED::onQuery(evt);
151    }
152
153    void onDrawContent(SkCanvas* canvas) override {
154        canvas->drawDrawable(fRoot.get());
155    }
156
157    bool onAnimate(const SkAnimTimer& timer) override {
158        fTime = timer.msec();
159        for (int i = 0; i < N; ++i) {
160            fArray[i].fDrawable->setTime(fTime);
161        }
162        return true;
163    }
164
165    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
166        // search backwards to find the top-most
167        for (int i = N - 1; i >= 0; --i) {
168            if (fArray[i].fDrawable->hitTest(x, y)) {
169                fArray[i].fDrawable->spawnAnimation(fTime);
170                break;
171            }
172        }
173        this->inval(nullptr);
174        return nullptr;
175    }
176
177private:
178    typedef SampleView INHERITED;
179};
180
181//////////////////////////////////////////////////////////////////////////////
182
183static SkView* MyFactory() { return new HTView; }
184static SkViewRegister reg(MyFactory);
185