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