• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Sample.h"
9 #include "SkCanvas.h"
10 #include "SkColorFilter.h"
11 #include "SkColorPriv.h"
12 #include "SkGradientShader.h"
13 #include "SkGraphics.h"
14 #include "SkPath.h"
15 #include "SkRegion.h"
16 #include "SkShader.h"
17 #include "SkTime.h"
18 #include "SkTo.h"
19 #include "SkTypeface.h"
20 #include "SkUTF.h"
21 
22 #include <utility>
23 
24 class PathClipView : public Sample {
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(Sample::Event * evt)32     bool onQuery(Sample::Event* evt) override {
33         if (Sample::TitleQ(*evt)) {
34             Sample::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     Sample::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         return false;
68     }
69 
70 private:
71     typedef Sample INHERITED;
72 };
DEF_SAMPLE(return new PathClipView;)73 DEF_SAMPLE( return new PathClipView; )
74 
75 //////////////////////////////////////////////////////////////////////////////
76 
77 static int clip_line(const SkRect& bounds, SkPoint p0, SkPoint p1, SkPoint edges[]) {
78     SkPoint* edgesStart = edges;
79 
80     if (p0.fY == p1.fY) {
81         return 0;
82     }
83 
84     if (p0.fY > p1.fY) {
85         using std::swap;
86         swap(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         using std::swap;
107         swap(p0, p1);
108     }
109     // now we're left-to-right: p0 .. p1
110 
111     if (p1.fX <= bounds.left()) {   // entirely to the left
112         p0.fX = p1.fX = bounds.left();
113         *edges++ = p0;
114         *edges++ = p1;
115         return 2;
116     }
117     if (p0.fX >= bounds.right()) {  // entirely to the right
118         p0.fX = p1.fX = bounds.right();
119         *edges++ = p0;
120         *edges++ = p1;
121         return 2;
122     }
123 
124     if (p0.fX < bounds.left()) {
125         float y = SkDoubleToScalar(p0.fY + (bounds.left() - p0.fX) / dxdy);
126         *edges++ = SkPoint::Make(bounds.left(), p0.fY);
127         *edges++ = SkPoint::Make(bounds.left(), y);
128         p0.set(bounds.left(), y);
129     }
130     if (p1.fX > bounds.right()) {
131         float y = SkDoubleToScalar(p0.fY + (bounds.right() - p0.fX) / dxdy);
132         *edges++ = p0;
133         *edges++ = SkPoint::Make(bounds.right(), y);
134         *edges++ = SkPoint::Make(bounds.right(), p1.fY);
135     } else {
136         *edges++ = p0;
137         *edges++ = p1;
138     }
139     return SkToInt(edges - edgesStart);
140 }
141 
draw_clipped_line(SkCanvas * canvas,const SkRect & bounds,SkPoint p0,SkPoint p1,const SkPaint & paint)142 static void draw_clipped_line(SkCanvas* canvas, const SkRect& bounds,
143                               SkPoint p0, SkPoint p1, const SkPaint& paint) {
144     SkPoint verts[6];
145     int count = clip_line(bounds, p0, p1, verts);
146 
147     SkPath path;
148     path.addPoly(verts, count, false);
149     canvas->drawPath(path, paint);
150 }
151 
152 // Demonstrate edge-clipping that is used in the scan converter
153 //
154 class EdgeClipView : public Sample {
155     enum {
156         N = 3
157     };
158 public:
159     SkPoint fPoly[N];
160     SkRect  fClip;
161     SkColor fEdgeColor[N];
162 
EdgeClipView()163     EdgeClipView() : fClip(SkRect::MakeLTRB(150, 150, 550, 450)) {
164         fPoly[0].set(300, 40);
165         fPoly[1].set(550, 250);
166         fPoly[2].set(40, 450);
167 
168         fEdgeColor[0] = 0xFFFF0000;
169         fEdgeColor[1] = 0xFF00FF00;
170         fEdgeColor[2] = 0xFF0000FF;
171     }
172 
173 protected:
onQuery(Sample::Event * evt)174     bool onQuery(Sample::Event* evt) override {
175         if (Sample::TitleQ(*evt)) {
176             Sample::TitleR(evt, "EdgeClip");
177             return true;
178         }
179         return this->INHERITED::onQuery(evt);
180     }
181 
snap(SkScalar x)182     static SkScalar snap(SkScalar x) {
183         return SkScalarRoundToScalar(x * 0.5f) * 2;
184     }
snap(const SkPoint & pt)185     static SkPoint snap(const SkPoint& pt) {
186         return SkPoint::Make(snap(pt.x()), snap(pt.y()));
187     }
snap(SkPoint dst[],const SkPoint src[],int count)188     static void snap(SkPoint dst[], const SkPoint src[], int count) {
189         for (int i = 0; i < count; ++i) {
190             dst[i] = snap(src[i]);
191         }
192     }
193 
onDrawContent(SkCanvas * canvas)194     void onDrawContent(SkCanvas* canvas) override {
195         SkPath path;
196         path.addPoly(fPoly, N, true);
197 
198         // Draw the full triangle, stroked and filled
199         SkPaint p;
200         p.setAntiAlias(true);
201         p.setColor(0xFFE0E0E0);
202         canvas->drawPath(path, p);
203         p.setStyle(SkPaint::kStroke_Style);
204         p.setStrokeWidth(2);
205         for (int i = 0; i < N; ++i) {
206             const int j = (i + 1) % N;
207             p.setColor(fEdgeColor[i]);
208             p.setAlpha(0x88);
209             canvas->drawLine(fPoly[i], fPoly[j], p);
210         }
211         p.setStyle(SkPaint::kFill_Style);
212 
213         // Draw the clip itself
214         p.setColor(0xFF8888CC);
215         canvas->drawRect(fClip, p);
216 
217         // Draw the filled triangle through the clip
218         p.setColor(0xFF88CC88);
219         canvas->save();
220         canvas->clipRect(fClip);
221         canvas->drawPath(path, p);
222         canvas->restore();
223 
224         p.setStyle(SkPaint::kStroke_Style);
225         p.setStrokeWidth(6);
226 
227         // Draw each of the "Edges" that survived the clipping
228         // We use a layer, so we can PLUS the different edge-colors, showing where two edges
229         // canceled each other out.
230         canvas->saveLayer(nullptr, nullptr);
231         p.setBlendMode(SkBlendMode::kPlus);
232         for (int i = 0; i < N; ++i) {
233             const int j = (i + 1) % N;
234             p.setColor(fEdgeColor[i]);
235             draw_clipped_line(canvas, fClip, fPoly[i], fPoly[j], p);
236         }
237         canvas->restore();
238     }
239 
240     class MyClick : public Click {
241     public:
MyClick(Sample * view)242         MyClick(Sample* view) : Click(view) {}
243         virtual void handleMove() = 0;
244     };
245 
246     class VertClick : public MyClick {
247         SkPoint* fPt;
248     public:
VertClick(Sample * view,SkPoint * pt)249         VertClick(Sample* view, SkPoint* pt) : MyClick(view), fPt(pt) {}
handleMove()250         void handleMove() override { *fPt = snap(fCurr); }
251     };
252 
253     class DragRectClick : public MyClick {
254         SkRect* fRect;
255     public:
DragRectClick(Sample * view,SkRect * rect)256         DragRectClick(Sample* view, SkRect* rect) : MyClick(view), fRect(rect) {}
handleMove()257         void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
258     };
259 
260     class DragPolyClick : public MyClick {
261         SkPoint fSrc[100];
262         SkPoint* fPoly;
263         int fCount;
264     public:
DragPolyClick(Sample * view,SkPoint poly[],int count)265         DragPolyClick(Sample* view, SkPoint poly[], int count)
266             : MyClick(view), fPoly(poly), fCount(count)
267         {
268             SkASSERT((size_t)count <= SK_ARRAY_COUNT(fSrc));
269             memcpy(fSrc, poly, count * sizeof(SkPoint));
270         }
handleMove()271         void handleMove() override {
272             const SkScalar dx = fCurr.x() - fOrig.x();
273             const SkScalar dy = fCurr.y() - fOrig.y();
274             for (int i = 0; i < fCount; ++i) {
275                 fPoly[i].set(snap(fSrc[i].x() + dx), snap(fSrc[i].y() + dy));
276             }
277         }
278     };
279 
280     class DoNothingClick : public MyClick {
281     public:
DoNothingClick(Sample * view)282         DoNothingClick(Sample* view) : MyClick(view) {}
handleMove()283         void handleMove() override {}
284     };
285 
hit_test(const SkPoint & pt,SkScalar x,SkScalar y)286     static bool hit_test(const SkPoint& pt, SkScalar x, SkScalar y) {
287         const SkScalar rad = 8;
288         const SkScalar dx = pt.x() - x;
289         const SkScalar dy = pt.y() - y;
290         return dx*dx + dy*dy <= rad*rad;
291     }
292 
onFindClickHandler(SkScalar x,SkScalar y,unsigned)293     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
294         for (int i = 0; i < N; ++i) {
295             if (hit_test(fPoly[i], x, y)) {
296                 return new VertClick(this, &fPoly[i]);
297             }
298         }
299 
300         SkPath path;
301         path.addPoly(fPoly, N, true);
302         if (path.contains(x, y)) {
303             return new DragPolyClick(this, fPoly, N);
304         }
305 
306         if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
307             return new DragRectClick(this, &fClip);
308         }
309         return new DoNothingClick(this);
310     }
311 
onClick(Click * click)312     bool onClick(Click* click) override {
313         ((MyClick*)click)->handleMove();
314         return false;
315     }
316 
317 private:
318     typedef Sample INHERITED;
319 };
320 
321 DEF_SAMPLE( return new EdgeClipView; )
322