1/*
2 * Copyright 2015 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 "SkCanvas.h"
11#include "SkGradientShader.h"
12#include "SkPatchUtils.h"
13#include "SkPerlinNoiseShader.h"
14
15static void draw_control_points(SkCanvas* canvas, const SkPoint cubics[12]) {
16    //draw control points
17    SkPaint paint;
18    SkPoint bottom[SkPatchUtils::kNumPtsCubic];
19    SkPatchUtils::GetBottomCubic(cubics, bottom);
20    SkPoint top[SkPatchUtils::kNumPtsCubic];
21    SkPatchUtils::GetTopCubic(cubics, top);
22    SkPoint left[SkPatchUtils::kNumPtsCubic];
23    SkPatchUtils::GetLeftCubic(cubics, left);
24    SkPoint right[SkPatchUtils::kNumPtsCubic];
25    SkPatchUtils::GetRightCubic(cubics, right);
26
27    paint.setColor(SK_ColorBLACK);
28    paint.setStrokeWidth(0.5f);
29    SkPoint corners[4] = { bottom[0], bottom[3], top[0], top[3] };
30    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, bottom, paint);
31    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, bottom + 1, paint);
32    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, top, paint);
33    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, left, paint);
34    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, right, paint);
35
36    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, top + 1, paint);
37    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, left + 1, paint);
38    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, right + 1, paint);
39
40    paint.setStrokeWidth(2);
41
42    paint.setColor(SK_ColorRED);
43    canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, corners, paint);
44
45    paint.setColor(SK_ColorBLUE);
46    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, bottom + 1, paint);
47
48    paint.setColor(SK_ColorCYAN);
49    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, top + 1, paint);
50
51    paint.setColor(SK_ColorYELLOW);
52    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, left + 1, paint);
53
54    paint.setColor(SK_ColorGREEN);
55    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, right + 1, paint);
56}
57
58// These are actually half the total width and hieghts
59const SkScalar TexWidth = 100.0f;
60const SkScalar TexHeight = 100.0f;
61
62class PerlinPatchView : public SampleView {
63    sk_sp<SkShader> fShader0;
64    sk_sp<SkShader> fShader1;
65    sk_sp<SkShader> fShaderCompose;
66    SkScalar fXFreq;
67    SkScalar fYFreq;
68    SkScalar fSeed;
69    SkPoint  fPts[SkPatchUtils::kNumCtrlPts];
70    SkScalar fTexX;
71    SkScalar fTexY;
72    SkScalar fTexScale;
73    SkMatrix fInvMatrix;
74    bool     fShowGrid = false;
75
76public:
77    PerlinPatchView() : fXFreq(0.025f), fYFreq(0.025f), fSeed(0.0f),
78                        fTexX(100.0), fTexY(50.0), fTexScale(1.0f) {
79        const SkScalar s = 2;
80        // The order of the colors and points is clockwise starting at upper-left corner.
81        //top points
82        fPts[0].set(100 * s, 100 * s);
83        fPts[1].set(150 * s, 50 * s);
84        fPts[2].set(250 * s, 150 * s);
85        fPts[3].set(300 * s, 100 * s);
86        //right points
87        fPts[4].set(275 * s, 150 * s);
88        fPts[5].set(350 * s, 250 * s);
89        //bottom points
90        fPts[6].set(300 * s, 300 * s);
91        fPts[7].set(250 * s, 250 * s);
92        //left points
93        fPts[8].set(150 * s, 350 * s);
94        fPts[9].set(100 * s, 300 * s);
95        fPts[10].set(50 * s, 250 * s);
96        fPts[11].set(150 * s, 150 * s);
97
98        const SkColor colors[SkPatchUtils::kNumCorners] = {
99            0xFF5555FF, 0xFF8888FF, 0xFFCCCCFF
100        };
101        const SkPoint points[2] = { SkPoint::Make(0.0f, 0.0f),
102                                    SkPoint::Make(100.0f, 100.0f) };
103        fShader0 = SkGradientShader::MakeLinear(points,
104                                                  colors,
105                                                  nullptr,
106                                                  3,
107                                                  SkShader::kMirror_TileMode,
108                                                  0,
109                                                  nullptr);
110    }
111
112protected:
113    // overrides from SkEventSink
114    bool onQuery(SkEvent* evt)  override {
115        if (SampleCode::TitleQ(*evt)) {
116            SampleCode::TitleR(evt, "PerlinPatch");
117            return true;
118        }
119        SkUnichar uni;
120        if (SampleCode::CharQ(*evt, &uni)) {
121            switch (uni) {
122                case 'g': fShowGrid = !fShowGrid; return true;
123                default: break;
124            }
125        }
126        return this->INHERITED::onQuery(evt);
127    }
128
129    bool onAnimate(const SkAnimTimer& timer) override {
130        fSeed += 0.005f;
131        return true;
132    }
133
134
135    void onDrawContent(SkCanvas* canvas) override {
136        if (!canvas->getTotalMatrix().invert(&fInvMatrix)) {
137            return;
138        }
139
140        SkPaint paint;
141
142        SkScalar texWidth = fTexScale * TexWidth;
143        SkScalar texHeight = fTexScale * TexHeight;
144        const SkPoint texCoords[SkPatchUtils::kNumCorners] = {
145            { fTexX - texWidth, fTexY - texHeight},
146            { fTexX + texWidth, fTexY - texHeight},
147            { fTexX + texWidth, fTexY + texHeight},
148            { fTexX - texWidth, fTexY + texHeight}}
149        ;
150
151        SkScalar scaleFreq = 2.0;
152        fShader1 = SkPerlinNoiseShader::MakeImprovedNoise(fXFreq/scaleFreq, fYFreq/scaleFreq, 4,
153                                                             fSeed);
154        fShaderCompose = SkShader::MakeComposeShader(fShader0, fShader1, SkBlendMode::kSrcOver);
155
156        paint.setShader(fShaderCompose);
157
158        const SkPoint* tex = texCoords;
159        if (fShowGrid) {
160            tex = nullptr;
161        }
162        canvas->drawPatch(fPts, nullptr, tex, SkBlendMode::kSrc, paint);
163
164        draw_control_points(canvas, fPts);
165    }
166
167    class PtClick : public Click {
168    public:
169        int fIndex;
170        PtClick(SkView* view, int index) : Click(view), fIndex(index) {}
171    };
172
173    static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
174        return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
175    }
176
177    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
178        // holding down shift
179        if (1 == modi) {
180            return new PtClick(this, -1);
181        }
182        // holding down ctrl
183        if (2 == modi) {
184            return new PtClick(this, -2);
185        }
186        SkPoint clickPoint = {x, y};
187        fInvMatrix.mapPoints(&clickPoint, 1);
188        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
189            if (hittest(fPts[i], clickPoint.fX, clickPoint.fY)) {
190                return new PtClick(this, (int)i);
191            }
192        }
193        return this->INHERITED::onFindClickHandler(x, y, modi);
194    }
195
196    bool onClick(Click* click) override {
197        PtClick* ptClick = (PtClick*)click;
198        if (ptClick->fIndex >= 0) {
199            fPts[ptClick->fIndex].set(click->fCurr.fX , click->fCurr.fY );
200        } else if (-1 == ptClick->fIndex) {
201            SkScalar xDiff = click->fPrev.fX - click->fCurr.fX;
202            SkScalar yDiff = click->fPrev.fY - click->fCurr.fY;
203            fTexX += xDiff * fTexScale;
204            fTexY += yDiff * fTexScale;
205        } else if (-2 == ptClick->fIndex) {
206            SkScalar yDiff = click->fCurr.fY - click->fPrev.fY;
207            fTexScale += yDiff / 10.0f;
208            fTexScale = SkTMax(0.1f, SkTMin(20.f, fTexScale));
209        }
210        return true;
211    }
212
213private:
214    typedef SampleView INHERITED;
215};
216
217DEF_SAMPLE( return new PerlinPatchView(); )
218