1/*
2 * Copyright 2012 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 "SkView.h"
10#include "SkCanvas.h"
11#include "SkRandom.h"
12#include "SkRRect.h"
13#include "SkColorPriv.h"
14
15static void rotateAbout(SkCanvas* canvas, SkScalar degrees,
16                        SkScalar cx, SkScalar cy) {
17    canvas->translate(cx, cy);
18    canvas->rotate(degrees);
19    canvas->translate(-cx, -cy);
20}
21
22class RotateCirclesView : public SampleView {
23public:
24    RotateCirclesView() {
25        this->setBGColor(SK_ColorLTGRAY);
26
27        fAngle = 0;
28    }
29
30protected:
31    // overrides from SkEventSink
32    virtual bool onQuery(SkEvent* evt) {
33        if (SampleCode::TitleQ(*evt)) {
34            SampleCode::TitleR(evt, "RotateCircles");
35            return true;
36        }
37        return this->INHERITED::onQuery(evt);
38    }
39
40    virtual void onDrawContent(SkCanvas* canvas) {
41        SkRandom rand;
42        SkPaint paint;
43        paint.setAntiAlias(true);
44        paint.setStrokeWidth(20);
45
46        SkScalar cx = 240;
47        SkScalar cy = 240;
48        SkScalar DX = 240 * 2;
49        SkColor color = 0;
50
51        float scale = 1;
52        float sign = 0.3f;
53        for (SkScalar rad = 200; rad >= 20; rad -= 15) {
54            sign = -sign;
55            scale += 0.2f;
56
57            paint.setColor(rand.nextU());
58            paint.setAlpha(0xFF);
59            color = ~color;
60
61            paint.setStyle(SkPaint::kFill_Style);
62
63            canvas->save();
64            rotateAbout(canvas, fAngle * scale * sign, cx, cy);
65            canvas->drawCircle(cx, cy, rad, paint);
66            canvas->restore();
67
68            paint.setStyle(SkPaint::kStroke_Style);
69            paint.setStrokeWidth(rad*2);
70
71            canvas->save();
72            rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy);
73            canvas->drawCircle(cx + DX, cy, 10, paint);
74            canvas->restore();
75
76            canvas->save();
77            rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy + DX);
78            canvas->drawCircle(cx + DX, cy + DX, 10, paint);
79            canvas->restore();
80
81        }
82
83        fAngle = (fAngle + 1) % 360;
84        this->inval(NULL);
85    }
86
87private:
88    int fAngle;
89    typedef SkView INHERITED;
90};
91
92class TestCirclesView : public SampleView {
93public:
94    TestCirclesView() {
95    }
96
97protected:
98    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
99        if (SampleCode::TitleQ(*evt)) {
100            SampleCode::TitleR(evt, "RotateCircles2");
101            return true;
102        }
103        return this->INHERITED::onQuery(evt);
104    }
105
106    void draw_real_circle(SkCanvas* canvas, SkScalar radius) {
107        int w = SkScalarCeilToInt(radius * 2);
108        int h = w;
109
110        SkBitmap bm;
111        bm.allocN32Pixels(w, h);
112        bm.eraseColor(0);
113
114        SkAutoLockPixels alp(bm);
115
116        SkScalar cx = radius;
117        SkScalar cy = radius;
118        for (int y = 0; y < h; y += 1) {
119            for (int x = 0; x < w; x += 1) {
120                float d = sqrtf((x - cx)*(x - cx) + (y - cy)*(y - cy));
121                if (d <= radius) {
122                    *bm.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0);
123                }
124            }
125        }
126
127        canvas->drawBitmap(bm, 0, 0, NULL);
128    }
129
130    virtual void onDrawContent(SkCanvas* canvas) {
131        SkScalar radius = 256;
132        canvas->translate(10, 10);
133
134        draw_real_circle(canvas, radius);
135
136        SkPaint paint;
137        paint.setAntiAlias(true);
138
139        paint.setColor(0x80FF0000);
140        canvas->drawCircle(radius, radius, radius, paint);
141
142        paint.setStyle(SkPaint::kStroke_Style);
143        paint.setStrokeWidth(radius);
144        paint.setColor(0x8000FF00);
145        canvas->drawCircle(radius, radius, radius/2, paint);
146    }
147
148private:
149    typedef SkView INHERITED;
150};
151
152static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
153    const SkScalar TOL = 7;
154    return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
155}
156
157static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
158    SkPath::RawIter iter(path);
159    SkPoint pts[4];
160    SkPath::Verb verb;
161
162    int count = 0;
163    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
164        switch (verb) {
165            case SkPath::kMove_Verb:
166            case SkPath::kLine_Verb:
167            case SkPath::kQuad_Verb:
168            case SkPath::kCubic_Verb:
169                storage[count++] = pts[0];
170                break;
171            default:
172                break;
173        }
174    }
175    return count;
176}
177
178#include "SkPathMeasure.h"
179
180class TestStrokeView : public SampleView {
181    enum {
182        SKELETON_COLOR = 0xFF0000FF,
183        WIREFRAME_COLOR = 0x80FF0000
184    };
185
186    enum {
187        kCount = 9
188    };
189    SkPoint fPts[kCount];
190    SkScalar fWidth, fDWidth;
191public:
192    TestStrokeView() {
193        this->setBGColor(SK_ColorLTGRAY);
194
195        fPts[0].set(50, 200);
196        fPts[1].set(50, 100);
197        fPts[2].set(150, 50);
198        fPts[3].set(300, 50);
199
200        fPts[4].set(350, 200);
201        fPts[5].set(350, 100);
202        fPts[6].set(450, 50);
203
204        fPts[7].set(200, 200);
205        fPts[8].set(400, 400);
206
207        fWidth = 50;
208        fDWidth = 0.25f;
209    }
210
211protected:
212    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
213        if (SampleCode::TitleQ(*evt)) {
214            SampleCode::TitleR(evt, "RotateCircles3");
215            return true;
216        }
217        return this->INHERITED::onQuery(evt);
218    }
219
220    void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
221                     bool show_lines) {
222        SkPaint paint;
223        paint.setColor(color);
224        paint.setAlpha(0x80);
225
226        int n = path.countPoints();
227        SkAutoSTArray<32, SkPoint> pts(n);
228        if (show_lines) {
229            path.getPoints(pts.get(), n);
230            canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts.get(), paint);
231        } else {
232            n = getOnCurvePoints(path, pts.get());
233        }
234        paint.setStrokeWidth(5);
235        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
236    }
237
238    void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
239                   SkColor color) {
240        const SkScalar radius = width / 2;
241
242        SkPathMeasure meas(path, false);
243        SkScalar total = meas.getLength();
244
245        SkScalar delta = 8;
246        SkPaint paint;
247        paint.setColor(color);
248
249        SkPoint pos, tan;
250        for (SkScalar dist = 0; dist <= total; dist += delta) {
251            if (meas.getPosTan(dist, &pos, &tan)) {
252                tan.scale(radius);
253                tan.rotateCCW();
254                canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
255                                 pos.x() - tan.x(), pos.y() - tan.y(), paint);
256            }
257        }
258    }
259
260    void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width) {
261        SkPaint paint;
262        paint.setAntiAlias(true);
263        paint.setStyle(SkPaint::kStroke_Style);
264
265        paint.setColor(SKELETON_COLOR);
266        canvas->drawPath(path, paint);
267        draw_points(canvas, path, SKELETON_COLOR, true);
268
269        draw_ribs(canvas, path, width, 0xFF00FF00);
270
271        SkPath fill;
272
273        SkPaint p;
274        p.setStyle(SkPaint::kStroke_Style);
275        p.setStrokeWidth(width);
276        p.getFillPath(path, &fill);
277
278        paint.setColor(WIREFRAME_COLOR);
279        canvas->drawPath(fill, paint);
280        draw_points(canvas, fill, WIREFRAME_COLOR, false);
281    }
282
283    virtual void onDrawContent(SkCanvas* canvas) {
284        SkPath path;
285        SkScalar width = fWidth;
286
287        path.moveTo(fPts[0]);
288        path.cubicTo(fPts[1], fPts[2], fPts[3]);
289        draw_stroke(canvas, path, width);
290
291        path.reset();
292        path.moveTo(fPts[4]);
293        path.quadTo(fPts[5], fPts[6]);
294        draw_stroke(canvas, path, width);
295
296        SkScalar rad = 32;
297        SkRect r;
298        r.set(&fPts[7], 2);
299        path.reset();
300        SkRRect rr;
301        rr.setRectXY(r, rad, rad);
302        path.addRRect(rr);
303        draw_stroke(canvas, path, width);
304
305        path.reset();
306        SkRRect rr2;
307        rr.inset(width/2, width/2, &rr2);
308        path.addRRect(rr2, SkPath::kCCW_Direction);
309        rr.inset(-width/2, -width/2, &rr2);
310        path.addRRect(rr2, SkPath::kCW_Direction);
311        SkPaint paint;
312        paint.setAntiAlias(true);
313        paint.setColor(0x40FF8844);
314        canvas->drawPath(path, paint);
315
316        fWidth += fDWidth;
317        if (fDWidth > 0 && fWidth > 100) {
318            fDWidth = -fDWidth;
319        } else if (fDWidth < 0 && fWidth < 10) {
320            fDWidth = -fDWidth;
321        }
322        this->inval(NULL);
323    }
324
325    class MyClick : public Click {
326    public:
327        int fIndex;
328        MyClick(SkView* target, int index) : Click(target), fIndex(index) {}
329    };
330
331    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
332                                              unsigned modi) SK_OVERRIDE {
333        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
334            if (hittest(fPts[i], x, y)) {
335                return new MyClick(this, (int)i);
336            }
337        }
338        return this->INHERITED::onFindClickHandler(x, y, modi);
339    }
340
341    virtual bool onClick(Click* click) {
342        int index = ((MyClick*)click)->fIndex;
343        fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
344                           SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
345        this->inval(NULL);
346        return true;
347    }
348
349private:
350    typedef SkView INHERITED;
351};
352
353///////////////////////////////////////////////////////////////////////////////
354
355static SkView* F0() { return new RotateCirclesView; }
356static SkViewRegister gR0(F0);
357static SkView* F1() { return new TestCirclesView; }
358static SkViewRegister gR1(F1);
359static SkView* F2() { return new TestStrokeView; }
360static SkViewRegister gR2(F2);
361