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 
24 class PathClipView : public SampleView {
25 public:
26     SkRect fOval;
27     SkPoint fCenter;
28 
PathClipView()29     PathClipView() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {}
30 
31 protected:
onQuery(SkEvent * evt)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 
onDrawContent(SkCanvas * canvas)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 
onFindClickHandler(SkScalar x,SkScalar y,unsigned)61     SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
62         return new Click(this);
63     }
64 
onClick(Click * click)65     bool onClick(Click* click) override {
66         fCenter.set(click->fCurr.fX, click->fCurr.fY);
67         this->inval(nullptr);
68         return false;
69     }
70 
71 private:
72     typedef SampleView INHERITED;
73 };
DEF_SAMPLE(return new PathClipView;)74 DEF_SAMPLE( return new PathClipView; )
75 
76 //////////////////////////////////////////////////////////////////////////////
77 
78 static 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 
draw_clipped_line(SkCanvas * canvas,const SkRect & bounds,SkPoint p0,SkPoint p1,const SkPaint & paint)141 static 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 //
153 class EdgeClipView : public SampleView {
154     enum {
155         N = 3
156     };
157 public:
158     SkPoint fPoly[N];
159     SkRect  fClip;
160     SkColor fEdgeColor[N];
161 
EdgeClipView()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 
172 protected:
onQuery(SkEvent * evt)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 
snap(SkScalar x)181     static SkScalar snap(SkScalar x) {
182         return SkScalarRoundToScalar(x * 0.5f) * 2;
183     }
snap(const SkPoint & pt)184     static SkPoint snap(const SkPoint& pt) {
185         return SkPoint::Make(snap(pt.x()), snap(pt.y()));
186     }
snap(SkPoint dst[],const SkPoint src[],int count)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 
onDrawContent(SkCanvas * canvas)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:
MyClick(SkView * view)241         MyClick(SkView* view) : Click(view) {}
242         virtual void handleMove() = 0;
243     };
244 
245     class VertClick : public MyClick {
246         SkPoint* fPt;
247     public:
VertClick(SkView * view,SkPoint * pt)248         VertClick(SkView* view, SkPoint* pt) : MyClick(view), fPt(pt) {}
handleMove()249         void handleMove() override { *fPt = snap(fCurr); }
250     };
251 
252     class DragRectClick : public MyClick {
253         SkRect* fRect;
254     public:
DragRectClick(SkView * view,SkRect * rect)255         DragRectClick(SkView* view, SkRect* rect) : MyClick(view), fRect(rect) {}
handleMove()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:
DragPolyClick(SkView * view,SkPoint poly[],int count)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         }
handleMove()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:
DoNothingClick(SkView * view)281         DoNothingClick(SkView* view) : MyClick(view) {}
handleMove()282         void handleMove() override {}
283     };
284 
hit_test(const SkPoint & pt,SkScalar x,SkScalar y)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 
onFindClickHandler(SkScalar x,SkScalar y,unsigned)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 
onClick(Click * click)311     bool onClick(Click* click) override {
312         ((MyClick*)click)->handleMove();
313         this->inval(nullptr);
314         return false;
315     }
316 
317 private:
318     typedef SampleView INHERITED;
319 };
320 DEF_SAMPLE( return new EdgeClipView; )
321 
322