1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SampleCode.h"
9#include "SkView.h"
10#include "SkCanvas.h"
11#include "SkGradientShader.h"
12#include "SkGraphics.h"
13#include "SkImageDecoder.h"
14#include "SkPath.h"
15#include "SkRegion.h"
16#include "SkShader.h"
17#include "SkUtils.h"
18#include "SkXfermode.h"
19#include "SkColorPriv.h"
20#include "SkColorFilter.h"
21#include "SkTime.h"
22#include "SkTypeface.h"
23
24class PathClipView : public SampleView {
25public:
26    SkRect fOval;
27    SkPoint fCenter;
28
29    PathClipView() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {}
30
31protected:
32    bool onQuery(SkEvent* evt) override {
33        if (SampleCode::TitleQ(*evt)) {
34            SampleCode::TitleR(evt, "PathClip");
35            return true;
36        }
37        return this->INHERITED::onQuery(evt);
38    }
39
40    void onDrawContent(SkCanvas* canvas) override {
41        const SkRect oval = fOval.makeOffset(fCenter.fX - fOval.centerX(),
42                                             fCenter.fY - fOval.centerY());
43
44        SkPaint p;
45        p.setAntiAlias(true);
46
47        p.setStyle(SkPaint::kStroke_Style);
48        canvas->drawOval(oval, p);
49
50        const SkRect r = SkRect::MakeLTRB(200, 200, 300, 300);
51        canvas->clipRect(r);
52
53        p.setStyle(SkPaint::kFill_Style);
54        p.setColor(SK_ColorRED);
55        canvas->drawRect(r, p);
56
57        p.setColor(0x800000FF);
58        canvas->drawOval(oval, p);
59    }
60
61    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
62        return new Click(this);
63    }
64
65    bool onClick(Click* click) override {
66        fCenter.set(click->fCurr.fX, click->fCurr.fY);
67        this->inval(nullptr);
68        return false;
69    }
70
71private:
72    typedef SampleView INHERITED;
73};
74DEF_SAMPLE( return new PathClipView; )
75
76//////////////////////////////////////////////////////////////////////////////
77
78static int clip_line(const SkRect& bounds, SkPoint p0, SkPoint p1, SkPoint edges[]) {
79    SkPoint* edgesStart = edges;
80
81    if (p0.fY == p1.fY) {
82        return 0;
83    }
84
85    if (p0.fY > p1.fY) {
86        SkTSwap(p0, p1);
87    }
88    // now we're monotonic in Y: p0 <= p1
89    if (p1.fY <= bounds.top() || p0.fY >= bounds.bottom()) {
90        return 0;
91    }
92
93    double dxdy = (double)(p1.fX - p0.fX) / (p1.fY - p0.fY);
94    if (p0.fY < bounds.top()) {
95        p0.fX = SkDoubleToScalar(p0.fX + dxdy * (bounds.top() - p0.fY));
96        p0.fY = bounds.top();
97    }
98    if (p1.fY > bounds.bottom()) {
99        p1.fX = SkDoubleToScalar(p1.fX + dxdy * (bounds.bottom() - p1.fY));
100        p1.fY = bounds.bottom();
101    }
102
103    // Now p0...p1 is strictly inside bounds vertically, so we just need to clip horizontally
104
105    if (p0.fX > p1.fX) {
106        SkTSwap(p0, p1);
107    }
108    // now we're left-to-right: p0 .. p1
109
110    if (p1.fX <= bounds.left()) {   // entirely to the left
111        p0.fX = p1.fX = bounds.left();
112        *edges++ = p0;
113        *edges++ = p1;
114        return 2;
115    }
116    if (p0.fX >= bounds.right()) {  // entirely to the right
117        p0.fX = p1.fX = bounds.right();
118        *edges++ = p0;
119        *edges++ = p1;
120        return 2;
121    }
122
123    if (p0.fX < bounds.left()) {
124        float y = SkDoubleToScalar(p0.fY + (bounds.left() - p0.fX) / dxdy);
125        *edges++ = SkPoint::Make(bounds.left(), p0.fY);
126        *edges++ = SkPoint::Make(bounds.left(), y);
127        p0.set(bounds.left(), y);
128    }
129    if (p1.fX > bounds.right()) {
130        float y = SkDoubleToScalar(p0.fY + (bounds.right() - p0.fX) / dxdy);
131        *edges++ = p0;
132        *edges++ = SkPoint::Make(bounds.right(), y);
133        *edges++ = SkPoint::Make(bounds.right(), p1.fY);
134    } else {
135        *edges++ = p0;
136        *edges++ = p1;
137    }
138    return SkToInt(edges - edgesStart);
139}
140
141static void draw_clipped_line(SkCanvas* canvas, const SkRect& bounds,
142                              SkPoint p0, SkPoint p1, const SkPaint& paint) {
143    SkPoint verts[6];
144    int count = clip_line(bounds, p0, p1, verts);
145
146    SkPath path;
147    path.addPoly(verts, count, false);
148    canvas->drawPath(path, paint);
149}
150
151// Demonstrate edge-clipping that is used in the scan converter
152//
153class EdgeClipView : public SampleView {
154    enum {
155        N = 3
156    };
157public:
158    SkPoint fPoly[N];
159    SkRect  fClip;
160    SkColor fEdgeColor[N];
161
162    EdgeClipView() : fClip(SkRect::MakeLTRB(150, 150, 550, 450)) {
163        fPoly[0].set(300, 40);
164        fPoly[1].set(550, 250);
165        fPoly[2].set(40, 450);
166
167        fEdgeColor[0] = 0xFFFF0000;
168        fEdgeColor[1] = 0xFF00FF00;
169        fEdgeColor[2] = 0xFF0000FF;
170    }
171
172protected:
173    bool onQuery(SkEvent* evt) override {
174        if (SampleCode::TitleQ(*evt)) {
175            SampleCode::TitleR(evt, "EdgeClip");
176            return true;
177        }
178        return this->INHERITED::onQuery(evt);
179    }
180
181    static SkScalar snap(SkScalar x) {
182        return SkScalarRoundToScalar(x * 0.5f) * 2;
183    }
184    static SkPoint snap(const SkPoint& pt) {
185        return SkPoint::Make(snap(pt.x()), snap(pt.y()));
186    }
187    static void snap(SkPoint dst[], const SkPoint src[], int count) {
188        for (int i = 0; i < count; ++i) {
189            dst[i] = snap(src[i]);
190        }
191    }
192
193    void onDrawContent(SkCanvas* canvas) override {
194        SkPath path;
195        path.addPoly(fPoly, N, true);
196
197        // Draw the full triangle, stroked and filled
198        SkPaint p;
199        p.setAntiAlias(true);
200        p.setColor(0xFFE0E0E0);
201        canvas->drawPath(path, p);
202        p.setStyle(SkPaint::kStroke_Style);
203        p.setStrokeWidth(2);
204        for (int i = 0; i < N; ++i) {
205            const int j = (i + 1) % N;
206            p.setColor(fEdgeColor[i]);
207            p.setAlpha(0x88);
208            canvas->drawLine(fPoly[i].x(), fPoly[i].y(), fPoly[j].x(), fPoly[j].y(), p);
209        }
210        p.setStyle(SkPaint::kFill_Style);
211
212        // Draw the clip itself
213        p.setColor(0xFF8888CC);
214        canvas->drawRect(fClip, p);
215
216        // Draw the filled triangle through the clip
217        p.setColor(0xFF88CC88);
218        canvas->save();
219        canvas->clipRect(fClip);
220        canvas->drawPath(path, p);
221        canvas->restore();
222
223        p.setStyle(SkPaint::kStroke_Style);
224        p.setStrokeWidth(6);
225
226        // Draw each of the "Edges" that survived the clipping
227        // We use a layer, so we can PLUS the different edge-colors, showing where two edges
228        // canceled each other out.
229        canvas->saveLayer(nullptr, nullptr);
230        p.setXfermodeMode(SkXfermode::kPlus_Mode);
231        for (int i = 0; i < N; ++i) {
232            const int j = (i + 1) % N;
233            p.setColor(fEdgeColor[i]);
234            draw_clipped_line(canvas, fClip, fPoly[i], fPoly[j], p);
235        }
236        canvas->restore();
237    }
238
239    class MyClick : public Click {
240    public:
241        MyClick(SkView* view) : Click(view) {}
242        virtual void handleMove() = 0;
243    };
244
245    class VertClick : public MyClick {
246        SkPoint* fPt;
247    public:
248        VertClick(SkView* view, SkPoint* pt) : MyClick(view), fPt(pt) {}
249        void handleMove() override { *fPt = snap(fCurr); }
250    };
251
252    class DragRectClick : public MyClick {
253        SkRect* fRect;
254    public:
255        DragRectClick(SkView* view, SkRect* rect) : MyClick(view), fRect(rect) {}
256        void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
257    };
258
259    class DragPolyClick : public MyClick {
260        SkPoint fSrc[100];
261        SkPoint* fPoly;
262        int fCount;
263    public:
264        DragPolyClick(SkView* view, SkPoint poly[], int count)
265            : MyClick(view), fPoly(poly), fCount(count)
266        {
267            SkASSERT((size_t)count <= SK_ARRAY_COUNT(fSrc));
268            memcpy(fSrc, poly, count * sizeof(SkPoint));
269        }
270        void handleMove() override {
271            const SkScalar dx = fCurr.x() - fOrig.x();
272            const SkScalar dy = fCurr.y() - fOrig.y();
273            for (int i = 0; i < fCount; ++i) {
274                fPoly[i].set(snap(fSrc[i].x() + dx), snap(fSrc[i].y() + dy));
275            }
276        }
277    };
278
279    class DoNothingClick : public MyClick {
280    public:
281        DoNothingClick(SkView* view) : MyClick(view) {}
282        void handleMove() override {}
283    };
284
285    static bool hit_test(const SkPoint& pt, SkScalar x, SkScalar y) {
286        const SkScalar rad = 8;
287        const SkScalar dx = pt.x() - x;
288        const SkScalar dy = pt.y() - y;
289        return dx*dx + dy*dy <= rad*rad;
290    }
291
292    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
293        for (int i = 0; i < N; ++i) {
294            if (hit_test(fPoly[i], x, y)) {
295                return new VertClick(this, &fPoly[i]);
296            }
297        }
298
299        SkPath path;
300        path.addPoly(fPoly, N, true);
301        if (path.contains(x, y)) {
302            return new DragPolyClick(this, fPoly, N);
303        }
304
305        if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
306            return new DragRectClick(this, &fClip);
307        }
308        return new DoNothingClick(this);
309    }
310
311    bool onClick(Click* click) override {
312        ((MyClick*)click)->handleMove();
313        this->inval(nullptr);
314        return false;
315    }
316
317private:
318    typedef SampleView INHERITED;
319};
320DEF_SAMPLE( return new EdgeClipView; )
321
322