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