blob: 02a613a72fcf0e9cccd2653790eefde12b07a2f1 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@android.comc07d23a2009-02-06 13:30:58 +00008#include "SampleCode.h"
9#include "SkView.h"
10#include "SkCanvas.h"
11#include "SkGradientShader.h"
12#include "SkGraphics.h"
reed@android.comc07d23a2009-02-06 13:30:58 +000013#include "SkPath.h"
reed@android.comc07d23a2009-02-06 13:30:58 +000014#include "SkRegion.h"
15#include "SkShader.h"
16#include "SkUtils.h"
17#include "SkXfermode.h"
18#include "SkColorPriv.h"
19#include "SkColorFilter.h"
20#include "SkTime.h"
21#include "SkTypeface.h"
22
reed@google.com81e3d7f2011-06-01 12:42:36 +000023class PathClipView : public SampleView {
reed@android.comc07d23a2009-02-06 13:30:58 +000024public:
25 SkRect fOval;
26 SkPoint fCenter;
27
reed67c65132015-09-28 06:16:07 -070028 PathClipView() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {}
rmistry@google.comae933ce2012-08-23 18:19:56 +000029
reed@android.comc07d23a2009-02-06 13:30:58 +000030protected:
mtkleinf0599002015-07-13 06:18:39 -070031 bool onQuery(SkEvent* evt) override {
reed@android.comc07d23a2009-02-06 13:30:58 +000032 if (SampleCode::TitleQ(*evt)) {
33 SampleCode::TitleR(evt, "PathClip");
34 return true;
35 }
36 return this->INHERITED::onQuery(evt);
37 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000038
mtkleinf0599002015-07-13 06:18:39 -070039 void onDrawContent(SkCanvas* canvas) override {
reed67c65132015-09-28 06:16:07 -070040 const SkRect oval = fOval.makeOffset(fCenter.fX - fOval.centerX(),
41 fCenter.fY - fOval.centerY());
rmistry@google.comae933ce2012-08-23 18:19:56 +000042
reed@android.comc07d23a2009-02-06 13:30:58 +000043 SkPaint p;
44 p.setAntiAlias(true);
rmistry@google.comae933ce2012-08-23 18:19:56 +000045
reed@android.comc07d23a2009-02-06 13:30:58 +000046 p.setStyle(SkPaint::kStroke_Style);
47 canvas->drawOval(oval, p);
48
reed67c65132015-09-28 06:16:07 -070049 const SkRect r = SkRect::MakeLTRB(200, 200, 300, 300);
reed@android.comc07d23a2009-02-06 13:30:58 +000050 canvas->clipRect(r);
rmistry@google.comae933ce2012-08-23 18:19:56 +000051
reed@android.comc07d23a2009-02-06 13:30:58 +000052 p.setStyle(SkPaint::kFill_Style);
53 p.setColor(SK_ColorRED);
54 canvas->drawRect(r, p);
rmistry@google.comae933ce2012-08-23 18:19:56 +000055
reed@android.comc07d23a2009-02-06 13:30:58 +000056 p.setColor(0x800000FF);
reed@android.comc07d23a2009-02-06 13:30:58 +000057 canvas->drawOval(oval, p);
58 }
reed@android.come72fee52009-11-16 14:52:01 +000059
mtklein36352bf2015-03-25 18:17:31 -070060 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
reed@android.come72fee52009-11-16 14:52:01 +000061 return new Click(this);
62 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000063
mtkleinf0599002015-07-13 06:18:39 -070064 bool onClick(Click* click) override {
reed@android.come72fee52009-11-16 14:52:01 +000065 fCenter.set(click->fCurr.fX, click->fCurr.fY);
halcanary96fcdcc2015-08-27 07:41:13 -070066 this->inval(nullptr);
caryclark@google.com02939ce2012-06-06 12:09:51 +000067 return false;
reed@android.comc07d23a2009-02-06 13:30:58 +000068 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000069
reed@android.comc07d23a2009-02-06 13:30:58 +000070private:
reed@google.com81e3d7f2011-06-01 12:42:36 +000071 typedef SampleView INHERITED;
reed@android.comc07d23a2009-02-06 13:30:58 +000072};
reed67c65132015-09-28 06:16:07 -070073DEF_SAMPLE( return new PathClipView; )
reed@android.comc07d23a2009-02-06 13:30:58 +000074
75//////////////////////////////////////////////////////////////////////////////
76
reed67c65132015-09-28 06:16:07 -070077static 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 SkTSwap(p0, p1);
86 }
87 // now we're monotonic in Y: p0 <= p1
88 if (p1.fY <= bounds.top() || p0.fY >= bounds.bottom()) {
89 return 0;
90 }
91
92 double dxdy = (double)(p1.fX - p0.fX) / (p1.fY - p0.fY);
93 if (p0.fY < bounds.top()) {
94 p0.fX = SkDoubleToScalar(p0.fX + dxdy * (bounds.top() - p0.fY));
95 p0.fY = bounds.top();
96 }
97 if (p1.fY > bounds.bottom()) {
98 p1.fX = SkDoubleToScalar(p1.fX + dxdy * (bounds.bottom() - p1.fY));
99 p1.fY = bounds.bottom();
100 }
101
102 // Now p0...p1 is strictly inside bounds vertically, so we just need to clip horizontally
103
104 if (p0.fX > p1.fX) {
105 SkTSwap(p0, p1);
106 }
107 // now we're left-to-right: p0 .. p1
108
109 if (p1.fX <= bounds.left()) { // entirely to the left
110 p0.fX = p1.fX = bounds.left();
111 *edges++ = p0;
112 *edges++ = p1;
113 return 2;
114 }
115 if (p0.fX >= bounds.right()) { // entirely to the right
116 p0.fX = p1.fX = bounds.right();
117 *edges++ = p0;
118 *edges++ = p1;
119 return 2;
120 }
121
122 if (p0.fX < bounds.left()) {
123 float y = SkDoubleToScalar(p0.fY + (bounds.left() - p0.fX) / dxdy);
124 *edges++ = SkPoint::Make(bounds.left(), p0.fY);
125 *edges++ = SkPoint::Make(bounds.left(), y);
126 p0.set(bounds.left(), y);
127 }
128 if (p1.fX > bounds.right()) {
129 float y = SkDoubleToScalar(p0.fY + (bounds.right() - p0.fX) / dxdy);
130 *edges++ = p0;
131 *edges++ = SkPoint::Make(bounds.right(), y);
132 *edges++ = SkPoint::Make(bounds.right(), p1.fY);
133 } else {
134 *edges++ = p0;
135 *edges++ = p1;
136 }
137 return SkToInt(edges - edgesStart);
138}
139
140static void draw_clipped_line(SkCanvas* canvas, const SkRect& bounds,
141 SkPoint p0, SkPoint p1, const SkPaint& paint) {
142 SkPoint verts[6];
143 int count = clip_line(bounds, p0, p1, verts);
144
145 SkPath path;
146 path.addPoly(verts, count, false);
147 canvas->drawPath(path, paint);
148}
149
150// Demonstrate edge-clipping that is used in the scan converter
151//
152class EdgeClipView : public SampleView {
153 enum {
154 N = 3
155 };
156public:
157 SkPoint fPoly[N];
158 SkRect fClip;
159 SkColor fEdgeColor[N];
160
161 EdgeClipView() : fClip(SkRect::MakeLTRB(150, 150, 550, 450)) {
162 fPoly[0].set(300, 40);
163 fPoly[1].set(550, 250);
164 fPoly[2].set(40, 450);
165
166 fEdgeColor[0] = 0xFFFF0000;
167 fEdgeColor[1] = 0xFF00FF00;
168 fEdgeColor[2] = 0xFF0000FF;
169 }
170
171protected:
172 bool onQuery(SkEvent* evt) override {
173 if (SampleCode::TitleQ(*evt)) {
174 SampleCode::TitleR(evt, "EdgeClip");
175 return true;
176 }
177 return this->INHERITED::onQuery(evt);
178 }
179
180 static SkScalar snap(SkScalar x) {
181 return SkScalarRoundToScalar(x * 0.5f) * 2;
182 }
183 static SkPoint snap(const SkPoint& pt) {
184 return SkPoint::Make(snap(pt.x()), snap(pt.y()));
185 }
186 static void snap(SkPoint dst[], const SkPoint src[], int count) {
187 for (int i = 0; i < count; ++i) {
188 dst[i] = snap(src[i]);
189 }
190 }
191
192 void onDrawContent(SkCanvas* canvas) override {
193 SkPath path;
194 path.addPoly(fPoly, N, true);
195
196 // Draw the full triangle, stroked and filled
197 SkPaint p;
198 p.setAntiAlias(true);
199 p.setColor(0xFFE0E0E0);
200 canvas->drawPath(path, p);
201 p.setStyle(SkPaint::kStroke_Style);
202 p.setStrokeWidth(2);
203 for (int i = 0; i < N; ++i) {
204 const int j = (i + 1) % N;
205 p.setColor(fEdgeColor[i]);
206 p.setAlpha(0x88);
207 canvas->drawLine(fPoly[i].x(), fPoly[i].y(), fPoly[j].x(), fPoly[j].y(), p);
208 }
209 p.setStyle(SkPaint::kFill_Style);
210
211 // Draw the clip itself
212 p.setColor(0xFF8888CC);
213 canvas->drawRect(fClip, p);
214
215 // Draw the filled triangle through the clip
216 p.setColor(0xFF88CC88);
217 canvas->save();
218 canvas->clipRect(fClip);
219 canvas->drawPath(path, p);
220 canvas->restore();
221
222 p.setStyle(SkPaint::kStroke_Style);
223 p.setStrokeWidth(6);
224
225 // Draw each of the "Edges" that survived the clipping
226 // We use a layer, so we can PLUS the different edge-colors, showing where two edges
227 // canceled each other out.
228 canvas->saveLayer(nullptr, nullptr);
229 p.setXfermodeMode(SkXfermode::kPlus_Mode);
230 for (int i = 0; i < N; ++i) {
231 const int j = (i + 1) % N;
232 p.setColor(fEdgeColor[i]);
233 draw_clipped_line(canvas, fClip, fPoly[i], fPoly[j], p);
234 }
235 canvas->restore();
236 }
237
238 class MyClick : public Click {
239 public:
240 MyClick(SkView* view) : Click(view) {}
241 virtual void handleMove() = 0;
242 };
243
244 class VertClick : public MyClick {
245 SkPoint* fPt;
246 public:
247 VertClick(SkView* view, SkPoint* pt) : MyClick(view), fPt(pt) {}
248 void handleMove() override { *fPt = snap(fCurr); }
249 };
250
251 class DragRectClick : public MyClick {
252 SkRect* fRect;
253 public:
254 DragRectClick(SkView* view, SkRect* rect) : MyClick(view), fRect(rect) {}
255 void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
256 };
257
258 class DragPolyClick : public MyClick {
259 SkPoint fSrc[100];
260 SkPoint* fPoly;
261 int fCount;
262 public:
263 DragPolyClick(SkView* view, SkPoint poly[], int count)
264 : MyClick(view), fPoly(poly), fCount(count)
265 {
266 SkASSERT((size_t)count <= SK_ARRAY_COUNT(fSrc));
267 memcpy(fSrc, poly, count * sizeof(SkPoint));
268 }
269 void handleMove() override {
270 const SkScalar dx = fCurr.x() - fOrig.x();
271 const SkScalar dy = fCurr.y() - fOrig.y();
272 for (int i = 0; i < fCount; ++i) {
273 fPoly[i].set(snap(fSrc[i].x() + dx), snap(fSrc[i].y() + dy));
274 }
275 }
276 };
277
278 class DoNothingClick : public MyClick {
279 public:
280 DoNothingClick(SkView* view) : MyClick(view) {}
281 void handleMove() override {}
282 };
283
284 static bool hit_test(const SkPoint& pt, SkScalar x, SkScalar y) {
285 const SkScalar rad = 8;
286 const SkScalar dx = pt.x() - x;
287 const SkScalar dy = pt.y() - y;
288 return dx*dx + dy*dy <= rad*rad;
289 }
290
291 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
292 for (int i = 0; i < N; ++i) {
293 if (hit_test(fPoly[i], x, y)) {
294 return new VertClick(this, &fPoly[i]);
295 }
296 }
297
298 SkPath path;
299 path.addPoly(fPoly, N, true);
300 if (path.contains(x, y)) {
301 return new DragPolyClick(this, fPoly, N);
302 }
303
304 if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
305 return new DragRectClick(this, &fClip);
306 }
307 return new DoNothingClick(this);
308 }
309
310 bool onClick(Click* click) override {
311 ((MyClick*)click)->handleMove();
312 this->inval(nullptr);
313 return false;
314 }
315
316private:
317 typedef SampleView INHERITED;
318};
319DEF_SAMPLE( return new EdgeClipView; )
320