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