1/*
2 * Copyright 2011 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 "SkGradientShader.h"
13#include "SkGraphics.h"
14#include "SkPath.h"
15#include "SkRegion.h"
16#include "SkShader.h"
17#include "SkUtils.h"
18#include "SkColorPriv.h"
19#include "SkColorFilter.h"
20#include "SkParsePath.h"
21#include "SkTime.h"
22#include "SkTypeface.h"
23
24#include "SkGeometry.h"
25
26#include <stdlib.h>
27
28// http://code.google.com/p/skia/issues/detail?id=32
29static void test_cubic() {
30    SkPoint src[4] = {
31        { 556.25000f, 523.03003f },
32        { 556.23999f, 522.96002f },
33        { 556.21997f, 522.89001f },
34        { 556.21997f, 522.82001f }
35    };
36    SkPoint dst[11];
37    dst[10].set(42, -42);   // one past the end, that we don't clobber these
38    SkScalar tval[] = { 0.33333334f, 0.99999994f };
39
40    SkChopCubicAt(src, dst, tval, 2);
41
42#if 0
43    for (int i = 0; i < 11; i++) {
44        SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY);
45    }
46#endif
47}
48
49static void test_cubic2() {
50    const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z";
51    SkPath path;
52    SkParsePath::FromSVGString(str, &path);
53
54    {
55        SkRect r = path.getBounds();
56        SkIRect ir;
57        r.round(&ir);
58        SkDebugf("[%g %g %g %g] [%x %x %x %x]\n",
59                SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop),
60                SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom),
61                ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
62    }
63
64    SkBitmap bitmap;
65    bitmap.allocN32Pixels(300, 200);
66
67    SkCanvas canvas(bitmap);
68    SkPaint paint;
69    paint.setAntiAlias(true);
70    canvas.drawPath(path, paint);
71}
72
73class PathView : public SampleView {
74    SkScalar fPrevSecs;
75public:
76    SkScalar fDStroke, fStroke, fMinStroke, fMaxStroke;
77    SkPath fPath[6];
78    bool fShowHairline;
79    bool fOnce;
80
81    PathView() {
82        fPrevSecs = 0;
83        fOnce = false;
84    }
85
86    void init() {
87        if (fOnce) {
88            return;
89        }
90        fOnce = true;
91
92        test_cubic();
93        test_cubic2();
94
95        fShowHairline = false;
96
97        fDStroke = 1;
98        fStroke = 10;
99        fMinStroke = 10;
100        fMaxStroke = 180;
101
102        const SkScalar V = 85;
103
104        fPath[0].moveTo(40, 70);
105        fPath[0].lineTo(70, 70 + SK_ScalarHalf);
106        fPath[0].lineTo(110, 70);
107
108        fPath[1].moveTo(40, 70);
109        fPath[1].lineTo(70, 70 - SK_ScalarHalf);
110        fPath[1].lineTo(110, 70);
111
112        fPath[2].moveTo(V, V);
113        fPath[2].lineTo(50, V);
114        fPath[2].lineTo(50, 50);
115
116        fPath[3].moveTo(50, 50);
117        fPath[3].lineTo(50, V);
118        fPath[3].lineTo(V, V);
119
120        fPath[4].moveTo(50, 50);
121        fPath[4].lineTo(50, V);
122        fPath[4].lineTo(52, 50);
123
124        fPath[5].moveTo(52, 50);
125        fPath[5].lineTo(50, V);
126        fPath[5].lineTo(50, 50);
127
128        this->setBGColor(0xFFDDDDDD);
129    }
130
131protected:
132    // overrides from SkEventSink
133    bool onQuery(SkEvent* evt) override {
134        if (SampleCode::TitleQ(*evt)) {
135            SampleCode::TitleR(evt, "Paths");
136            return true;
137        }
138        return this->INHERITED::onQuery(evt);
139    }
140
141    void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
142        SkPaint paint;
143
144        paint.setAntiAlias(true);
145        paint.setStyle(SkPaint::kStroke_Style);
146        paint.setStrokeJoin(j);
147        paint.setStrokeWidth(fStroke);
148
149        if (fShowHairline) {
150            SkPath  fill;
151
152            paint.getFillPath(path, &fill);
153            paint.setStrokeWidth(0);
154            canvas->drawPath(fill, paint);
155        } else {
156            canvas->drawPath(path, paint);
157        }
158
159        paint.setColor(SK_ColorRED);
160        paint.setStrokeWidth(0);
161        canvas->drawPath(path, paint);
162    }
163
164    void onDrawContent(SkCanvas* canvas) override {
165        this->init();
166        canvas->translate(50, 50);
167
168        static const SkPaint::Join gJoins[] = {
169            SkPaint::kBevel_Join,
170            SkPaint::kMiter_Join,
171            SkPaint::kRound_Join
172        };
173
174        for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) {
175            canvas->save();
176            for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) {
177                this->drawPath(canvas, fPath[j], gJoins[i]);
178                canvas->translate(200, 0);
179            }
180            canvas->restore();
181
182            canvas->translate(0, 200);
183        }
184    }
185
186    bool onAnimate(const SkAnimTimer& timer) override {
187        SkScalar currSecs = timer.scaled(100);
188        SkScalar delta = currSecs - fPrevSecs;
189        fPrevSecs = currSecs;
190
191        fStroke += fDStroke * delta;
192        if (fStroke > fMaxStroke || fStroke < fMinStroke) {
193            fDStroke = -fDStroke;
194        }
195        return true;
196    }
197
198    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
199        fShowHairline = !fShowHairline;
200        this->inval(nullptr);
201        return this->INHERITED::onFindClickHandler(x, y, modi);
202    }
203
204private:
205    typedef SampleView INHERITED;
206};
207DEF_SAMPLE( return new PathView; )
208
209//////////////////////////////////////////////////////////////////////////////
210
211#include "SkArcToPathEffect.h"
212#include "SkCornerPathEffect.h"
213#include "SkRandom.h"
214
215class ArcToView : public SampleView {
216    bool fDoFrame, fDoArcTo, fDoCorner, fDoConic;
217    SkPaint fPtsPaint, fArcToPaint, fSkeletonPaint, fCornerPaint;
218public:
219    enum {
220        N = 4
221    };
222    SkPoint fPts[N];
223
224    ArcToView()
225        : fDoFrame(false), fDoArcTo(false), fDoCorner(false), fDoConic(false)
226    {
227        SkRandom rand;
228        for (int i = 0; i < N; ++i) {
229            fPts[i].fX = 20 + rand.nextUScalar1() * 640;
230            fPts[i].fY = 20 + rand.nextUScalar1() * 480;
231        }
232
233        const SkScalar rad = 50;
234
235        fPtsPaint.setAntiAlias(true);
236        fPtsPaint.setStrokeWidth(15);
237        fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
238
239        fArcToPaint.setAntiAlias(true);
240        fArcToPaint.setStyle(SkPaint::kStroke_Style);
241        fArcToPaint.setStrokeWidth(9);
242        fArcToPaint.setColor(0x800000FF);
243        fArcToPaint.setPathEffect(SkArcToPathEffect::Make(rad));
244
245        fCornerPaint.setAntiAlias(true);
246        fCornerPaint.setStyle(SkPaint::kStroke_Style);
247        fCornerPaint.setStrokeWidth(13);
248        fCornerPaint.setColor(SK_ColorGREEN);
249        fCornerPaint.setPathEffect(SkCornerPathEffect::Make(rad*2));
250
251        fSkeletonPaint.setAntiAlias(true);
252        fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
253        fSkeletonPaint.setColor(SK_ColorRED);
254    }
255
256    void toggle(bool& value) {
257        value = !value;
258        this->inval(nullptr);
259    }
260
261protected:
262    // overrides from SkEventSink
263    bool onQuery(SkEvent* evt) override {
264        if (SampleCode::TitleQ(*evt)) {
265            SampleCode::TitleR(evt, "ArcTo");
266            return true;
267        }
268        SkUnichar uni;
269        if (SampleCode::CharQ(*evt, &uni)) {
270            switch (uni) {
271                case '1': this->toggle(fDoFrame); return true;
272                case '2': this->toggle(fDoArcTo); return true;
273                case '3': this->toggle(fDoCorner); return true;
274                case '4': this->toggle(fDoConic); return true;
275                default: break;
276            }
277        }
278        return this->INHERITED::onQuery(evt);
279    }
280
281    void makePath(SkPath* path) {
282        path->moveTo(fPts[0]);
283        for (int i = 1; i < N; ++i) {
284            path->lineTo(fPts[i]);
285        }
286        if (!fDoFrame) {
287            path->close();
288        }
289    }
290
291    void onDrawContent(SkCanvas* canvas) override {
292        canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
293
294        SkPath path;
295        this->makePath(&path);
296
297        if (fDoCorner) {
298            canvas->drawPath(path, fCornerPaint);
299        }
300        if (fDoArcTo) {
301            canvas->drawPath(path, fArcToPaint);
302        }
303
304        canvas->drawPath(path, fSkeletonPaint);
305    }
306
307    bool onClick(Click* click) override {
308        int32_t index;
309        if (click->fMeta.findS32("index", &index)) {
310            SkASSERT((unsigned)index < N);
311            fPts[index] = click->fCurr;
312            this->inval(nullptr);
313            return true;
314        }
315        return false;
316    }
317
318    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
319        const SkScalar tol = 4;
320        const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
321        for (int i = 0; i < N; ++i) {
322            if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
323                Click* click = new Click(this);
324                click->fMeta.setS32("index", i);
325                return click;
326            }
327        }
328        return this->INHERITED::onFindClickHandler(x, y, modi);
329    }
330
331private:
332    typedef SampleView INHERITED;
333};
334DEF_SAMPLE( return new ArcToView; )
335
336/////////////
337
338class FatStroke : public SampleView {
339    bool fClosed, fShowStroke, fShowHidden, fShowSkeleton;
340    int  fJoinType, fCapType;
341    float fWidth = 30;
342    SkPaint fPtsPaint, fHiddenPaint, fSkeletonPaint, fStrokePaint;
343public:
344    enum {
345        N = 4
346    };
347    SkPoint fPts[N];
348
349    FatStroke() : fClosed(false), fShowStroke(true), fShowHidden(false), fShowSkeleton(true),
350                  fJoinType(0), fCapType(0)
351    {
352        SkRandom rand;
353        for (int i = 0; i < N; ++i) {
354            fPts[i].fX = 20 + rand.nextUScalar1() * 640;
355            fPts[i].fY = 20 + rand.nextUScalar1() * 480;
356        }
357
358        fPtsPaint.setAntiAlias(true);
359        fPtsPaint.setStrokeWidth(10);
360        fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
361
362        fHiddenPaint.setAntiAlias(true);
363        fHiddenPaint.setStyle(SkPaint::kStroke_Style);
364        fHiddenPaint.setColor(0xFF0000FF);
365
366        fStrokePaint.setAntiAlias(true);
367        fStrokePaint.setStyle(SkPaint::kStroke_Style);
368        fStrokePaint.setStrokeWidth(50);
369        fStrokePaint.setColor(0x8000FF00);
370
371        fSkeletonPaint.setAntiAlias(true);
372        fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
373        fSkeletonPaint.setColor(SK_ColorRED);
374    }
375
376    void toggle(bool& value) {
377        value = !value;
378        this->inval(nullptr);
379    }
380
381    void toggle3(int& value) {
382        value = (value + 1) % 3;
383        this->inval(nullptr);
384    }
385
386protected:
387    // overrides from SkEventSink
388    bool onQuery(SkEvent* evt) override {
389        if (SampleCode::TitleQ(*evt)) {
390            SampleCode::TitleR(evt, "FatStroke");
391            return true;
392        }
393        SkUnichar uni;
394        if (SampleCode::CharQ(*evt, &uni)) {
395            switch (uni) {
396                case '1': this->toggle(fShowSkeleton); return true;
397                case '2': this->toggle(fShowStroke); return true;
398                case '3': this->toggle(fShowHidden); return true;
399                case '4': this->toggle3(fJoinType); return true;
400                case '5': this->toggle3(fCapType); return true;
401                case '6': this->toggle(fClosed); return true;
402                case '-': fWidth -= 5; this->inval(nullptr); return true;
403                case '=': fWidth += 5; this->inval(nullptr); return true;
404                default: break;
405            }
406        }
407        return this->INHERITED::onQuery(evt);
408    }
409
410    void makePath(SkPath* path) {
411        path->moveTo(fPts[0]);
412        for (int i = 1; i < N; ++i) {
413            path->lineTo(fPts[i]);
414        }
415        if (fClosed) {
416            path->close();
417        }
418    }
419
420    void onDrawContent(SkCanvas* canvas) override {
421        canvas->drawColor(0xFFEEEEEE);
422
423        SkPath path;
424        this->makePath(&path);
425
426        fStrokePaint.setStrokeWidth(fWidth);
427        fStrokePaint.setStrokeJoin((SkPaint::Join)fJoinType);
428        fStrokePaint.setStrokeCap((SkPaint::Cap)fCapType);
429
430        if (fShowStroke) {
431            canvas->drawPath(path, fStrokePaint);
432        }
433        if (fShowHidden) {
434            SkPath hidden;
435            fStrokePaint.getFillPath(path, &hidden);
436            canvas->drawPath(hidden, fHiddenPaint);
437        }
438        if (fShowSkeleton) {
439            canvas->drawPath(path, fSkeletonPaint);
440        }
441        canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
442    }
443
444    bool onClick(Click* click) override {
445        int32_t index;
446        if (click->fMeta.findS32("index", &index)) {
447            SkASSERT((unsigned)index < N);
448            fPts[index] = click->fCurr;
449            this->inval(nullptr);
450            return true;
451        }
452        return false;
453    }
454
455    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
456        const SkScalar tol = 4;
457        const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
458        for (int i = 0; i < N; ++i) {
459            if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
460                Click* click = new Click(this);
461                click->fMeta.setS32("index", i);
462                return click;
463            }
464        }
465        return this->INHERITED::onFindClickHandler(x, y, modi);
466    }
467
468private:
469    typedef SampleView INHERITED;
470};
471DEF_SAMPLE( return new FatStroke; )
472