blob: ab9fb4bd487a251022e3035adb6d9957d7f1e2b5 [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 */
Hal Canaryc640d0d2018-06-13 09:59:02 -04007
reed@android.comc07d23a2009-02-06 13:30:58 +00008#include "SampleCode.h"
reed@android.comc07d23a2009-02-06 13:30:58 +00009#include "SkCanvas.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040010#include "SkColorFilter.h"
11#include "SkColorPriv.h"
reed@android.comc07d23a2009-02-06 13:30:58 +000012#include "SkGradientShader.h"
13#include "SkGraphics.h"
reed@android.comc07d23a2009-02-06 13:30:58 +000014#include "SkPath.h"
reed@android.comc07d23a2009-02-06 13:30:58 +000015#include "SkRegion.h"
16#include "SkShader.h"
Hal Canaryfdcfb8b2018-06-13 09:42:32 -040017#include "SkTime.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040018#include "SkTo.h"
Hal Canaryfdcfb8b2018-06-13 09:42:32 -040019#include "SkTypeface.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040020#include "SkUtils.h"
21#include "SkView.h"
reed@android.comc07d23a2009-02-06 13:30:58 +000022
Ben Wagnerf08d1d02018-06-18 15:11:00 -040023#include <utility>
24
reed@google.com81e3d7f2011-06-01 12:42:36 +000025class PathClipView : public SampleView {
reed@android.comc07d23a2009-02-06 13:30:58 +000026public:
27 SkRect fOval;
28 SkPoint fCenter;
29
reed67c65132015-09-28 06:16:07 -070030 PathClipView() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {}
rmistry@google.comae933ce2012-08-23 18:19:56 +000031
reed@android.comc07d23a2009-02-06 13:30:58 +000032protected:
mtkleinf0599002015-07-13 06:18:39 -070033 bool onQuery(SkEvent* evt) override {
reed@android.comc07d23a2009-02-06 13:30:58 +000034 if (SampleCode::TitleQ(*evt)) {
35 SampleCode::TitleR(evt, "PathClip");
36 return true;
37 }
38 return this->INHERITED::onQuery(evt);
39 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000040
mtkleinf0599002015-07-13 06:18:39 -070041 void onDrawContent(SkCanvas* canvas) override {
reed67c65132015-09-28 06:16:07 -070042 const SkRect oval = fOval.makeOffset(fCenter.fX - fOval.centerX(),
43 fCenter.fY - fOval.centerY());
rmistry@google.comae933ce2012-08-23 18:19:56 +000044
reed@android.comc07d23a2009-02-06 13:30:58 +000045 SkPaint p;
46 p.setAntiAlias(true);
rmistry@google.comae933ce2012-08-23 18:19:56 +000047
reed@android.comc07d23a2009-02-06 13:30:58 +000048 p.setStyle(SkPaint::kStroke_Style);
49 canvas->drawOval(oval, p);
50
reed67c65132015-09-28 06:16:07 -070051 const SkRect r = SkRect::MakeLTRB(200, 200, 300, 300);
reed@android.comc07d23a2009-02-06 13:30:58 +000052 canvas->clipRect(r);
rmistry@google.comae933ce2012-08-23 18:19:56 +000053
reed@android.comc07d23a2009-02-06 13:30:58 +000054 p.setStyle(SkPaint::kFill_Style);
55 p.setColor(SK_ColorRED);
56 canvas->drawRect(r, p);
rmistry@google.comae933ce2012-08-23 18:19:56 +000057
reed@android.comc07d23a2009-02-06 13:30:58 +000058 p.setColor(0x800000FF);
reed@android.comc07d23a2009-02-06 13:30:58 +000059 canvas->drawOval(oval, p);
60 }
reed@android.come72fee52009-11-16 14:52:01 +000061
mtklein36352bf2015-03-25 18:17:31 -070062 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
reed@android.come72fee52009-11-16 14:52:01 +000063 return new Click(this);
64 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000065
mtkleinf0599002015-07-13 06:18:39 -070066 bool onClick(Click* click) override {
reed@android.come72fee52009-11-16 14:52:01 +000067 fCenter.set(click->fCurr.fX, click->fCurr.fY);
caryclark@google.com02939ce2012-06-06 12:09:51 +000068 return false;
reed@android.comc07d23a2009-02-06 13:30:58 +000069 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000070
reed@android.comc07d23a2009-02-06 13:30:58 +000071private:
reed@google.com81e3d7f2011-06-01 12:42:36 +000072 typedef SampleView INHERITED;
reed@android.comc07d23a2009-02-06 13:30:58 +000073};
reed67c65132015-09-28 06:16:07 -070074DEF_SAMPLE( return new PathClipView; )
reed@android.comc07d23a2009-02-06 13:30:58 +000075
76//////////////////////////////////////////////////////////////////////////////
77
reed67c65132015-09-28 06:16:07 -070078static int clip_line(const SkRect& bounds, SkPoint p0, SkPoint p1, SkPoint edges[]) {
79 SkPoint* edgesStart = edges;
80
81 if (p0.fY == p1.fY) {
82 return 0;
83 }
halcanary9d524f22016-03-29 09:03:52 -070084
reed67c65132015-09-28 06:16:07 -070085 if (p0.fY > p1.fY) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -040086 using std::swap;
87 swap(p0, p1);
reed67c65132015-09-28 06:16:07 -070088 }
89 // now we're monotonic in Y: p0 <= p1
90 if (p1.fY <= bounds.top() || p0.fY >= bounds.bottom()) {
91 return 0;
92 }
halcanary9d524f22016-03-29 09:03:52 -070093
reed67c65132015-09-28 06:16:07 -070094 double dxdy = (double)(p1.fX - p0.fX) / (p1.fY - p0.fY);
95 if (p0.fY < bounds.top()) {
96 p0.fX = SkDoubleToScalar(p0.fX + dxdy * (bounds.top() - p0.fY));
97 p0.fY = bounds.top();
98 }
99 if (p1.fY > bounds.bottom()) {
100 p1.fX = SkDoubleToScalar(p1.fX + dxdy * (bounds.bottom() - p1.fY));
101 p1.fY = bounds.bottom();
102 }
halcanary9d524f22016-03-29 09:03:52 -0700103
reed67c65132015-09-28 06:16:07 -0700104 // Now p0...p1 is strictly inside bounds vertically, so we just need to clip horizontally
halcanary9d524f22016-03-29 09:03:52 -0700105
reed67c65132015-09-28 06:16:07 -0700106 if (p0.fX > p1.fX) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400107 using std::swap;
108 swap(p0, p1);
reed67c65132015-09-28 06:16:07 -0700109 }
110 // now we're left-to-right: p0 .. p1
halcanary9d524f22016-03-29 09:03:52 -0700111
reed67c65132015-09-28 06:16:07 -0700112 if (p1.fX <= bounds.left()) { // entirely to the left
113 p0.fX = p1.fX = bounds.left();
114 *edges++ = p0;
115 *edges++ = p1;
116 return 2;
117 }
118 if (p0.fX >= bounds.right()) { // entirely to the right
119 p0.fX = p1.fX = bounds.right();
120 *edges++ = p0;
121 *edges++ = p1;
122 return 2;
123 }
halcanary9d524f22016-03-29 09:03:52 -0700124
reed67c65132015-09-28 06:16:07 -0700125 if (p0.fX < bounds.left()) {
126 float y = SkDoubleToScalar(p0.fY + (bounds.left() - p0.fX) / dxdy);
127 *edges++ = SkPoint::Make(bounds.left(), p0.fY);
128 *edges++ = SkPoint::Make(bounds.left(), y);
129 p0.set(bounds.left(), y);
130 }
131 if (p1.fX > bounds.right()) {
132 float y = SkDoubleToScalar(p0.fY + (bounds.right() - p0.fX) / dxdy);
133 *edges++ = p0;
134 *edges++ = SkPoint::Make(bounds.right(), y);
135 *edges++ = SkPoint::Make(bounds.right(), p1.fY);
136 } else {
137 *edges++ = p0;
138 *edges++ = p1;
139 }
140 return SkToInt(edges - edgesStart);
141}
142
143static void draw_clipped_line(SkCanvas* canvas, const SkRect& bounds,
144 SkPoint p0, SkPoint p1, const SkPaint& paint) {
145 SkPoint verts[6];
146 int count = clip_line(bounds, p0, p1, verts);
147
148 SkPath path;
149 path.addPoly(verts, count, false);
150 canvas->drawPath(path, paint);
151}
152
153// Demonstrate edge-clipping that is used in the scan converter
154//
155class EdgeClipView : public SampleView {
156 enum {
157 N = 3
158 };
159public:
160 SkPoint fPoly[N];
161 SkRect fClip;
162 SkColor fEdgeColor[N];
halcanary9d524f22016-03-29 09:03:52 -0700163
reed67c65132015-09-28 06:16:07 -0700164 EdgeClipView() : fClip(SkRect::MakeLTRB(150, 150, 550, 450)) {
165 fPoly[0].set(300, 40);
166 fPoly[1].set(550, 250);
167 fPoly[2].set(40, 450);
168
169 fEdgeColor[0] = 0xFFFF0000;
170 fEdgeColor[1] = 0xFF00FF00;
171 fEdgeColor[2] = 0xFF0000FF;
172 }
halcanary9d524f22016-03-29 09:03:52 -0700173
reed67c65132015-09-28 06:16:07 -0700174protected:
175 bool onQuery(SkEvent* evt) override {
176 if (SampleCode::TitleQ(*evt)) {
177 SampleCode::TitleR(evt, "EdgeClip");
178 return true;
179 }
180 return this->INHERITED::onQuery(evt);
181 }
182
183 static SkScalar snap(SkScalar x) {
184 return SkScalarRoundToScalar(x * 0.5f) * 2;
185 }
186 static SkPoint snap(const SkPoint& pt) {
187 return SkPoint::Make(snap(pt.x()), snap(pt.y()));
188 }
189 static void snap(SkPoint dst[], const SkPoint src[], int count) {
190 for (int i = 0; i < count; ++i) {
191 dst[i] = snap(src[i]);
192 }
193 }
194
195 void onDrawContent(SkCanvas* canvas) override {
196 SkPath path;
197 path.addPoly(fPoly, N, true);
198
199 // Draw the full triangle, stroked and filled
200 SkPaint p;
201 p.setAntiAlias(true);
202 p.setColor(0xFFE0E0E0);
203 canvas->drawPath(path, p);
204 p.setStyle(SkPaint::kStroke_Style);
205 p.setStrokeWidth(2);
206 for (int i = 0; i < N; ++i) {
207 const int j = (i + 1) % N;
208 p.setColor(fEdgeColor[i]);
209 p.setAlpha(0x88);
Hal Canary23e474c2017-05-15 13:35:35 -0400210 canvas->drawLine(fPoly[i], fPoly[j], p);
reed67c65132015-09-28 06:16:07 -0700211 }
212 p.setStyle(SkPaint::kFill_Style);
213
214 // Draw the clip itself
215 p.setColor(0xFF8888CC);
216 canvas->drawRect(fClip, p);
217
218 // Draw the filled triangle through the clip
219 p.setColor(0xFF88CC88);
220 canvas->save();
221 canvas->clipRect(fClip);
222 canvas->drawPath(path, p);
223 canvas->restore();
224
225 p.setStyle(SkPaint::kStroke_Style);
226 p.setStrokeWidth(6);
227
228 // Draw each of the "Edges" that survived the clipping
229 // We use a layer, so we can PLUS the different edge-colors, showing where two edges
230 // canceled each other out.
231 canvas->saveLayer(nullptr, nullptr);
reed374772b2016-10-05 17:33:02 -0700232 p.setBlendMode(SkBlendMode::kPlus);
reed67c65132015-09-28 06:16:07 -0700233 for (int i = 0; i < N; ++i) {
234 const int j = (i + 1) % N;
235 p.setColor(fEdgeColor[i]);
236 draw_clipped_line(canvas, fClip, fPoly[i], fPoly[j], p);
237 }
238 canvas->restore();
239 }
240
241 class MyClick : public Click {
242 public:
243 MyClick(SkView* view) : Click(view) {}
244 virtual void handleMove() = 0;
245 };
halcanary9d524f22016-03-29 09:03:52 -0700246
reed67c65132015-09-28 06:16:07 -0700247 class VertClick : public MyClick {
248 SkPoint* fPt;
249 public:
250 VertClick(SkView* view, SkPoint* pt) : MyClick(view), fPt(pt) {}
251 void handleMove() override { *fPt = snap(fCurr); }
252 };
halcanary9d524f22016-03-29 09:03:52 -0700253
reed67c65132015-09-28 06:16:07 -0700254 class DragRectClick : public MyClick {
255 SkRect* fRect;
256 public:
257 DragRectClick(SkView* view, SkRect* rect) : MyClick(view), fRect(rect) {}
258 void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
259 };
halcanary9d524f22016-03-29 09:03:52 -0700260
reed67c65132015-09-28 06:16:07 -0700261 class DragPolyClick : public MyClick {
262 SkPoint fSrc[100];
263 SkPoint* fPoly;
264 int fCount;
265 public:
266 DragPolyClick(SkView* view, SkPoint poly[], int count)
267 : MyClick(view), fPoly(poly), fCount(count)
268 {
269 SkASSERT((size_t)count <= SK_ARRAY_COUNT(fSrc));
270 memcpy(fSrc, poly, count * sizeof(SkPoint));
271 }
272 void handleMove() override {
273 const SkScalar dx = fCurr.x() - fOrig.x();
274 const SkScalar dy = fCurr.y() - fOrig.y();
275 for (int i = 0; i < fCount; ++i) {
276 fPoly[i].set(snap(fSrc[i].x() + dx), snap(fSrc[i].y() + dy));
277 }
278 }
279 };
280
281 class DoNothingClick : public MyClick {
282 public:
283 DoNothingClick(SkView* view) : MyClick(view) {}
284 void handleMove() override {}
285 };
286
287 static bool hit_test(const SkPoint& pt, SkScalar x, SkScalar y) {
288 const SkScalar rad = 8;
289 const SkScalar dx = pt.x() - x;
290 const SkScalar dy = pt.y() - y;
291 return dx*dx + dy*dy <= rad*rad;
292 }
293
294 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
295 for (int i = 0; i < N; ++i) {
296 if (hit_test(fPoly[i], x, y)) {
297 return new VertClick(this, &fPoly[i]);
298 }
299 }
halcanary9d524f22016-03-29 09:03:52 -0700300
reed67c65132015-09-28 06:16:07 -0700301 SkPath path;
302 path.addPoly(fPoly, N, true);
303 if (path.contains(x, y)) {
304 return new DragPolyClick(this, fPoly, N);
305 }
306
307 if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
308 return new DragRectClick(this, &fClip);
309 }
310 return new DoNothingClick(this);
311 }
halcanary9d524f22016-03-29 09:03:52 -0700312
reed67c65132015-09-28 06:16:07 -0700313 bool onClick(Click* click) override {
314 ((MyClick*)click)->handleMove();
reed67c65132015-09-28 06:16:07 -0700315 return false;
316 }
halcanary9d524f22016-03-29 09:03:52 -0700317
reed67c65132015-09-28 06:16:07 -0700318private:
319 typedef SampleView INHERITED;
320};
321DEF_SAMPLE( return new EdgeClipView; )