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 "include/core/SkCanvas.h"
9#include "include/core/SkColorPriv.h"
10#include "include/core/SkFont.h"
11#include "include/core/SkPaint.h"
12#include "include/core/SkPathBuilder.h"
13#include "include/utils/SkRandom.h"
14#include "samplecode/Sample.h"
15#include "src/core/SkPathPriv.h"
16#include "tools/Resources.h"
17
18constexpr int W = 150;
19constexpr int H = 200;
20
21static void show_text(SkCanvas* canvas, bool doAA) {
22    SkRandom rand;
23    SkPaint paint;
24    SkFont font(nullptr, 20);
25    font.setEdging(doAA ? SkFont::Edging::kSubpixelAntiAlias : SkFont::Edging::kAlias);
26
27    for (int i = 0; i < 200; ++i) {
28        paint.setColor((SK_A32_MASK << SK_A32_SHIFT) | rand.nextU());
29        canvas->drawString("Hamburgefons", rand.nextSScalar1() * W, rand.nextSScalar1() * H + 20,
30                           font, paint);
31    }
32}
33
34static void show_fill(SkCanvas* canvas, bool doAA) {
35    SkRandom rand;
36    SkPaint paint;
37    paint.setAntiAlias(doAA);
38
39    for (int i = 0; i < 50; ++i) {
40        SkRect r;
41
42        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
43                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
44        paint.setColor(rand.nextU());
45        canvas->drawRect(r, paint);
46
47        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
48                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
49        paint.setColor(rand.nextU());
50        canvas->drawOval(r, paint);
51    }
52}
53
54static SkScalar randRange(SkRandom& rand, SkScalar min, SkScalar max) {
55    SkASSERT(min <= max);
56    return min + rand.nextUScalar1() * (max - min);
57}
58
59static void show_stroke(SkCanvas* canvas, bool doAA, SkScalar strokeWidth, int n) {
60    SkRandom rand;
61    SkPaint paint;
62    paint.setAntiAlias(doAA);
63    paint.setStyle(SkPaint::kStroke_Style);
64    paint.setStrokeWidth(strokeWidth);
65
66    for (int i = 0; i < n; ++i) {
67        SkRect r;
68
69        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
70                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
71        paint.setColor(rand.nextU());
72        canvas->drawRect(r, paint);
73
74        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
75                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
76        paint.setColor(rand.nextU());
77        canvas->drawOval(r, paint);
78
79        const SkScalar minx = -SkIntToScalar(W)/4;
80        const SkScalar maxx = 5*SkIntToScalar(W)/4;
81        const SkScalar miny = -SkIntToScalar(H)/4;
82        const SkScalar maxy = 5*SkIntToScalar(H)/4;
83        paint.setColor(rand.nextU());
84        canvas->drawLine(randRange(rand, minx, maxx), randRange(rand, miny, maxy),
85                         randRange(rand, minx, maxx), randRange(rand, miny, maxy),
86                         paint);
87    }
88}
89
90static void show_hair(SkCanvas* canvas, bool doAA) {
91    show_stroke(canvas, doAA, 0, 150);
92}
93
94static void show_thick(SkCanvas* canvas, bool doAA) {
95    show_stroke(canvas, doAA, SkIntToScalar(5), 50);
96}
97
98typedef void (*CanvasProc)(SkCanvas*, bool);
99
100class ClipView : public Sample {
101    SkString name() override { return SkString("Clip"); }
102
103    void onDrawContent(SkCanvas* canvas) override {
104        canvas->drawColor(SK_ColorWHITE);
105        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
106
107        static const CanvasProc gProc[] = {
108            show_text, show_thick, show_hair, show_fill
109        };
110
111        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
112        r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
113        SkPath clipPath = SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, 20, 20)).detach();
114
115//        clipPath.toggleInverseFillType();
116
117        for (int aa = 0; aa <= 1; ++aa) {
118            canvas->save();
119            for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
120                canvas->save();
121                canvas->clipPath(clipPath, SkClipOp::kIntersect, SkToBool(aa));
122//                canvas->drawColor(SK_ColorWHITE);
123                gProc[i](canvas, SkToBool(aa));
124                canvas->restore();
125                canvas->translate(W * SK_Scalar1 * 8 / 7, 0);
126            }
127            canvas->restore();
128            canvas->translate(0, H * SK_Scalar1 * 8 / 7);
129        }
130    }
131};
132
133DEF_SAMPLE( return new ClipView(); )
134
135///////////////////////////////////////////////////////////////////////////////
136
137struct SkHalfPlane {
138    SkScalar fA, fB, fC;
139
140    SkScalar eval(SkScalar x, SkScalar y) const {
141        return fA * x + fB * y + fC;
142    }
143    SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
144
145    bool twoPts(SkPoint pts[2]) const {
146        // normalize plane to help with the perpendicular step, below
147        SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
148        if (!len) {
149            return false;
150        }
151        SkScalar denom = SkScalarInvert(len);
152        SkScalar a = fA * denom;
153        SkScalar b = fB * denom;
154        SkScalar c = fC * denom;
155
156        // We compute p0 on the half-plane by setting one of the components to 0
157        // We compute p1 by stepping from p0 along a perpendicular to the normal
158        if (b) {
159            pts[0] = { 0, -c / b };
160            pts[1] = { b, pts[0].fY - a};
161        } else if (a) {
162            pts[0] = { -c / a,        0 };
163            pts[1] = { pts[0].fX + b, -a };
164        } else {
165            return false;
166        }
167
168        SkASSERT(SkScalarNearlyZero(this->operator()(pts[0].fX, pts[0].fY)));
169        SkASSERT(SkScalarNearlyZero(this->operator()(pts[1].fX, pts[1].fY)));
170        return true;
171    }
172
173    enum Result {
174        kAllNegative,
175        kAllPositive,
176        kMixed
177    };
178    Result test(const SkRect& bounds) const {
179        SkPoint diagMin, diagMax;
180        if (fA >= 0) {
181            diagMin.fX = bounds.fLeft;
182            diagMax.fX = bounds.fRight;
183        } else {
184            diagMin.fX = bounds.fRight;
185            diagMax.fX = bounds.fLeft;
186        }
187        if (fB >= 0) {
188            diagMin.fY = bounds.fTop;
189            diagMax.fY = bounds.fBottom;
190        } else {
191            diagMin.fY = bounds.fBottom;
192            diagMax.fY = bounds.fTop;
193        }
194        SkScalar test = this->eval(diagMin.fX, diagMin.fY);
195        SkScalar sign = test*this->eval(diagMax.fX, diagMin.fY);
196        if (sign > 0) {
197            // the path is either all on one side of the half-plane or the other
198            if (test < 0) {
199                return kAllNegative;
200            } else {
201                return kAllPositive;
202            }
203        }
204        return kMixed;
205    }
206};
207
208#include "src/core/SkEdgeClipper.h"
209
210static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
211    SkMatrix mx, inv;
212    SkVector v = p1 - p0;
213    mx.setAll(v.fX, -v.fY, p0.fX,
214              v.fY,  v.fX, p0.fY,
215                 0,     0,     1);
216    SkAssertResult(mx.invert(&inv));
217
218    SkPath rotated;
219    path.transform(inv, &rotated);
220
221    SkScalar big = 1e28f;
222    SkRect clip = {-big, 0, big, big };
223
224    struct Rec {
225        SkPathBuilder   fResult;
226        SkPoint         fPrev = {0, 0};
227    } rec;
228
229    SkEdgeClipper::ClipPath(rotated, clip, false,
230                            [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
231        Rec* rec = (Rec*)ctx;
232
233        bool addLineTo = false;
234        SkPoint      pts[4];
235        SkPath::Verb verb;
236        while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
237            if (newCtr) {
238                rec->fResult.moveTo(pts[0]);
239                rec->fPrev = pts[0];
240                newCtr = false;
241            }
242
243            if (addLineTo || pts[0] != rec->fPrev) {
244                rec->fResult.lineTo(pts[0]);
245            }
246
247            switch (verb) {
248                case SkPath::kLine_Verb:
249                    rec->fResult.lineTo(pts[1]);
250                    rec->fPrev = pts[1];
251                    break;
252                case SkPath::kQuad_Verb:
253                    rec->fResult.quadTo(pts[1], pts[2]);
254                    rec->fPrev = pts[2];
255                    break;
256                case SkPath::kCubic_Verb:
257                    rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
258                    rec->fPrev = pts[3];
259                    break;
260                default: break;
261            }
262            addLineTo = true;
263        }
264    }, &rec);
265
266    return rec.fResult.detach().makeTransform(mx);
267}
268
269static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
270    SkVector v = p1 - p0;
271    p0 = p0 - v * 1000;
272    p1 = p1 + v * 1000;
273
274    SkPaint paint;
275    paint.setColor(c);
276    canvas->drawLine(p0, p1, paint);
277}
278
279static SkPath make_path() {
280    SkRandom rand;
281    auto rand_pt = [&rand]() {
282        auto x = rand.nextF();
283        auto y = rand.nextF();
284        return SkPoint{x * 400, y * 400};
285    };
286
287    SkPathBuilder path;
288    for (int i = 0; i < 4; ++i) {
289        SkPoint pts[6];
290        for (auto& p : pts) {
291            p = rand_pt();
292        }
293        path.moveTo(pts[0]).quadTo(pts[1], pts[2]).quadTo(pts[3], pts[4]).lineTo(pts[5]);
294    }
295    return path.detach();
296}
297
298class HalfPlaneView : public Sample {
299    SkPoint fPts[2];
300    SkPath fPath;
301
302    SkString name() override { return SkString("halfplane"); }
303
304    void onOnceBeforeDraw() override {
305        fPts[0] = {0, 0};
306        fPts[1] = {3, 2};
307        fPath = make_path();
308    }
309
310    void onDrawContent(SkCanvas* canvas) override {
311        SkPaint paint;
312
313        paint.setColor({0.5f, 0.5f, 0.5f, 1.0f}, nullptr);
314        canvas->drawPath(fPath, paint);
315
316        paint.setColor({0, 0, 0, 1}, nullptr);
317
318        canvas->drawPath(clip(fPath, fPts[0], fPts[1]), paint);
319
320        draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
321    }
322
323    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
324        return new Click;
325    }
326
327    bool onClick(Click* click) override {
328        fPts[0] = click->fCurr;
329        fPts[1] = fPts[0] + SkPoint{3, 2};
330        return true;
331    }
332};
333DEF_SAMPLE( return new HalfPlaneView(); )
334
335static void draw_halfplane(SkCanvas* canvas, const SkHalfPlane& p, SkColor c) {
336    SkPoint pts[2];
337    p.twoPts(pts);
338    draw_halfplane(canvas, pts[0], pts[1], c);
339}
340
341static void compute_half_planes(const SkMatrix& mx, SkScalar width, SkScalar height,
342                                SkHalfPlane planes[4]) {
343    SkScalar a = mx[0], b = mx[1], c = mx[2],
344             d = mx[3], e = mx[4], f = mx[5],
345             g = mx[6], h = mx[7], i = mx[8];
346
347    planes[0] = { 2*g - 2*a/width,  2*h - 2*b/width,  2*i - 2*c/width };
348    planes[1] = { 2*a/width,        2*b/width,        2*c/width };
349    planes[2] = { 2*g - 2*d/height, 2*h - 2*e/height, 2*i - 2*f/height };
350    planes[3] = { 2*d/height,       2*e/height,       2*f/height };
351}
352
353class HalfPlaneView2 : public Sample {
354    SkPoint fPts[4];
355    SkPath fPath;
356
357    SkString name() override { return SkString("halfplane2"); }
358
359    void onOnceBeforeDraw() override {
360        fPath = make_path();
361        SkRect r = fPath.getBounds();
362        r.toQuad(fPts);
363    }
364
365    void onDrawContent(SkCanvas* canvas) override {
366        SkMatrix mx;
367        {
368            SkRect r = fPath.getBounds();
369            SkPoint src[4];
370            r.toQuad(src);
371            mx.setPolyToPoly(src, fPts, 4);
372        }
373
374        SkPaint paint;
375        canvas->drawPath(fPath, paint);
376
377        canvas->save();
378        canvas->concat(mx);
379        paint.setColor(0x40FF0000);
380        canvas->drawPath(fPath, paint);
381        canvas->restore();
382
383        // draw the frame
384        paint.setStrokeWidth(10);
385        paint.setColor(SK_ColorGREEN);
386        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPts, paint);
387
388        // draw the half-planes
389        SkHalfPlane planes[4];
390        compute_half_planes(mx, 400, 400, planes);
391        for (auto& p : planes) {
392            draw_halfplane(canvas, p, SK_ColorRED);
393        }
394    }
395
396    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
397        SkScalar r = 8;
398        SkRect rect = SkRect::MakeXYWH(x - r, y - r, 2*r, 2*r);
399        for (int i = 0; i < 4; ++i) {
400            if (rect.contains(fPts[i].fX, fPts[i].fY)) {
401                Click* c = new Click;
402                c->fMeta.setS32("index", i);
403                return c;
404            }
405        }
406        return nullptr;
407    }
408
409    bool onClick(Click* click) override {
410        int32_t index;
411        SkAssertResult(click->fMeta.findS32("index", &index));
412        SkASSERT(index >= 0 && index < 4);
413        fPts[index] = click->fCurr;
414        return true;
415    }
416};
417DEF_SAMPLE( return new HalfPlaneView2(); )
418
419static SkM44 inv(const SkM44& m) {
420    SkM44 inverse;
421    SkAssertResult(m.invert(&inverse));
422    return inverse;
423}
424
425static SkHalfPlane half_plane_w0(const SkMatrix& m) {
426    return { m[SkMatrix::kMPersp0], m[SkMatrix::kMPersp1], m[SkMatrix::kMPersp2] - 0.05f };
427}
428
429class SampleCameraView : public Sample {
430    float   fNear = 0.05f;
431    float   fFar = 4;
432    float   fAngle = SK_ScalarPI / 4;
433
434    SkV3    fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
435    SkV3    fCOA { 0, 0, 0 };
436    SkV3    fUp  { 0, 1, 0 };
437
438    SkM44  fRot;
439    SkV3   fTrans;
440
441    void rotate(float x, float y, float z) {
442        SkM44 r;
443        if (x) {
444            r.setRotateUnit({1, 0, 0}, x);
445        } else if (y) {
446            r.setRotateUnit({0, 1, 0}, y);
447        } else {
448            r.setRotateUnit({0, 0, 1}, z);
449        }
450        fRot = r * fRot;
451    }
452
453public:
454    SkM44 get44(const SkRect& r) const {
455        SkScalar w = r.width();
456        SkScalar h = r.height();
457
458        SkM44 camera = SkM44::LookAt(fEye, fCOA, fUp),
459              perspective = SkM44::Perspective(fNear, fFar, fAngle),
460              translate = SkM44::Translate(fTrans.x, fTrans.y, fTrans.z),
461              viewport = SkM44::Translate(r.centerX(), r.centerY(), 0) *
462                         SkM44::Scale(w*0.5f, h*0.5f, 1);
463
464        return viewport * perspective * camera * translate * fRot * inv(viewport);
465    }
466
467    bool onChar(SkUnichar uni) override {
468        float delta = SK_ScalarPI / 30;
469        switch (uni) {
470            case '8': this->rotate( delta, 0, 0); return true;
471            case '2': this->rotate(-delta, 0, 0); return true;
472            case '4': this->rotate(0,  delta, 0); return true;
473            case '6': this->rotate(0, -delta, 0); return true;
474            case '-': this->rotate(0, 0,  delta); return true;
475            case '+': this->rotate(0, 0, -delta); return true;
476
477            case 'i': fTrans.z += 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
478            case 'k': fTrans.z -= 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
479
480            case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
481            case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
482            case 'f': fFar  += 0.1f; SkDebugf("far  %g\n", fFar); return true;
483            case 'F': fFar  -= 0.1f; SkDebugf("far  %g\n", fFar); return true;
484            default: break;
485        }
486        return false;
487    }
488};
489
490class HalfPlaneView3 : public SampleCameraView {
491    SkPath fPath;
492    sk_sp<SkShader> fShader;
493    bool fShowUnclipped = false;
494
495    SkString name() override { return SkString("halfplane3"); }
496
497    void onOnceBeforeDraw() override {
498        fPath = make_path();
499        fShader = GetResourceAsImage("images/mandrill_128.png")
500                        ->makeShader(SkSamplingOptions(), SkMatrix::Scale(3, 3));
501    }
502
503    bool onChar(SkUnichar uni) override {
504        switch (uni) {
505            case 'u': fShowUnclipped = !fShowUnclipped; return true;
506            default: break;
507        }
508        return this->SampleCameraView::onChar(uni);
509    }
510
511    void onDrawContent(SkCanvas* canvas) override {
512        SkM44 mx = this->get44({0, 0, 400, 400});
513
514        SkPaint paint;
515        paint.setColor({0.75, 0.75, 0.75, 1});
516        canvas->drawPath(fPath, paint);
517
518        paint.setShader(fShader);
519
520        if (fShowUnclipped) {
521            canvas->save();
522            canvas->concat(mx);
523            paint.setAlphaf(0.33f);
524            canvas->drawPath(fPath, paint);
525            paint.setAlphaf(1.f);
526            canvas->restore();
527        }
528
529
530        SkColor planeColor = SK_ColorBLUE;
531        SkPath clippedPath, *path = &fPath;
532        if (SkPathPriv::PerspectiveClip(fPath, mx.asM33(), &clippedPath)) {
533            path = &clippedPath;
534            planeColor = SK_ColorRED;
535        }
536        canvas->save();
537        canvas->concat(mx);
538        canvas->drawPath(*path, paint);
539        canvas->restore();
540
541        SkHalfPlane hpw = half_plane_w0(mx.asM33());
542        draw_halfplane(canvas, hpw, planeColor);
543    }
544};
545DEF_SAMPLE( return new HalfPlaneView3(); )
546
547class HalfPlaneCoons : public SampleCameraView {
548    SkPoint fPatch[12];
549    SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
550    SkPoint fTex[4]    = {{0, 0}, {256, 0}, {256, 256}, {0, 256}};
551    sk_sp<SkShader> fShader;
552
553    bool fShowHandles = false;
554    bool fShowSkeleton = false;
555    bool fShowTex = false;
556
557    SkString name() override { return SkString("halfplane-coons"); }
558
559    void onOnceBeforeDraw() override {
560        fPatch[0] = {   0, 0 };
561        fPatch[1] = { 100, 0 };
562        fPatch[2] = { 200, 0 };
563        fPatch[3] = { 300, 0 };
564        fPatch[4] = { 300, 100 };
565        fPatch[5] = { 300, 200 };
566        fPatch[6] = { 300, 300 };
567        fPatch[7] = { 200, 300 };
568        fPatch[8] = { 100, 300 };
569        fPatch[9] = {   0, 300 };
570        fPatch[10] = {  0, 200 };
571        fPatch[11] = {  0, 100 };
572
573        fShader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
574    }
575
576    void onDrawContent(SkCanvas* canvas) override {
577        SkPaint paint;
578
579        canvas->save();
580        canvas->concat(this->get44({0, 0, 300, 300}));
581
582        const SkPoint* tex = nullptr;
583        const SkColor* col = nullptr;
584        if (!fShowSkeleton) {
585            if (fShowTex) {
586                paint.setShader(fShader);
587                tex = fTex;
588            } else {
589                col = fColors;
590            }
591        }
592        canvas->drawPatch(fPatch, col, tex, SkBlendMode::kSrc, paint);
593        paint.setShader(nullptr);
594
595        if (fShowHandles) {
596            paint.setAntiAlias(true);
597            paint.setStrokeCap(SkPaint::kRound_Cap);
598            paint.setStrokeWidth(8);
599            canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
600            paint.setColor(SK_ColorWHITE);
601            paint.setStrokeWidth(6);
602            canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
603        }
604
605        canvas->restore();
606    }
607
608    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
609        auto dist = [](SkPoint a, SkPoint b) { return (b - a).length(); };
610
611        const float tol = 15;
612        for (int i = 0; i < 12; ++i) {
613            if (dist({x,y}, fPatch[i]) <= tol) {
614                return new Click([this, i](Click* c) {
615                    fPatch[i] = c->fCurr;
616                    return true;
617                });
618            }
619        }
620        return nullptr;
621    }
622
623    bool onChar(SkUnichar uni) override {
624        switch (uni) {
625            case 'h': fShowHandles = !fShowHandles; return true;
626            case 'k': fShowSkeleton = !fShowSkeleton; return true;
627            case 't': fShowTex = !fShowTex; return true;
628            default: break;
629        }
630        return this->SampleCameraView::onChar(uni);
631    }
632
633};
634DEF_SAMPLE( return new HalfPlaneCoons(); )
635