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