blob: c004e9f24ec97695c60c89c87889ab12a1046e51 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
reed@android.comc07d23a2009-02-06 13:30:58 +000021
Ben Wagnerf08d1d02018-06-18 15:11:00 -040022#include <utility>
23
Ben Wagnerb2c4ea62018-08-08 11:36:17 -040024class PathClipView : public Sample {
reed@android.comc07d23a2009-02-06 13:30:58 +000025public:
26 SkRect fOval;
27 SkPoint fCenter;
28
reed67c65132015-09-28 06:16:07 -070029 PathClipView() : fOval(SkRect::MakeWH(200, 50)), fCenter(SkPoint::Make(250, 250)) {}
rmistry@google.comae933ce2012-08-23 18:19:56 +000030
reed@android.comc07d23a2009-02-06 13:30:58 +000031protected:
Hal Canary8a027312019-07-03 10:55:44 -040032 SkString name() override { return SkString("PathClip"); }
rmistry@google.comae933ce2012-08-23 18:19:56 +000033
mtkleinf0599002015-07-13 06:18:39 -070034 void onDrawContent(SkCanvas* canvas) override {
reed67c65132015-09-28 06:16:07 -070035 const SkRect oval = fOval.makeOffset(fCenter.fX - fOval.centerX(),
36 fCenter.fY - fOval.centerY());
rmistry@google.comae933ce2012-08-23 18:19:56 +000037
reed@android.comc07d23a2009-02-06 13:30:58 +000038 SkPaint p;
39 p.setAntiAlias(true);
rmistry@google.comae933ce2012-08-23 18:19:56 +000040
reed@android.comc07d23a2009-02-06 13:30:58 +000041 p.setStyle(SkPaint::kStroke_Style);
42 canvas->drawOval(oval, p);
43
reed67c65132015-09-28 06:16:07 -070044 const SkRect r = SkRect::MakeLTRB(200, 200, 300, 300);
reed@android.comc07d23a2009-02-06 13:30:58 +000045 canvas->clipRect(r);
rmistry@google.comae933ce2012-08-23 18:19:56 +000046
reed@android.comc07d23a2009-02-06 13:30:58 +000047 p.setStyle(SkPaint::kFill_Style);
48 p.setColor(SK_ColorRED);
49 canvas->drawRect(r, p);
rmistry@google.comae933ce2012-08-23 18:19:56 +000050
reed@android.comc07d23a2009-02-06 13:30:58 +000051 p.setColor(0x800000FF);
reed@android.comc07d23a2009-02-06 13:30:58 +000052 canvas->drawOval(oval, p);
53 }
reed@android.come72fee52009-11-16 14:52:01 +000054
Hal Canaryb1f411a2019-08-29 10:39:22 -040055 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
Mike Reed0ce3e882020-04-22 11:09:30 -040056 return new Click([&](Click* c) {
57 fCenter = c->fCurr;
58 return false;
59 });
reed@android.comc07d23a2009-02-06 13:30:58 +000060 }
rmistry@google.comae933ce2012-08-23 18:19:56 +000061
reed@android.comc07d23a2009-02-06 13:30:58 +000062private:
John Stiles7571f9e2020-09-02 22:42:33 -040063 using INHERITED = Sample;
reed@android.comc07d23a2009-02-06 13:30:58 +000064};
reed67c65132015-09-28 06:16:07 -070065DEF_SAMPLE( return new PathClipView; )
reed@android.comc07d23a2009-02-06 13:30:58 +000066
67//////////////////////////////////////////////////////////////////////////////
68
reed67c65132015-09-28 06:16:07 -070069static 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 }
halcanary9d524f22016-03-29 09:03:52 -070075
reed67c65132015-09-28 06:16:07 -070076 if (p0.fY > p1.fY) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -040077 using std::swap;
78 swap(p0, p1);
reed67c65132015-09-28 06:16:07 -070079 }
80 // now we're monotonic in Y: p0 <= p1
81 if (p1.fY <= bounds.top() || p0.fY >= bounds.bottom()) {
82 return 0;
83 }
halcanary9d524f22016-03-29 09:03:52 -070084
reed67c65132015-09-28 06:16:07 -070085 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 }
halcanary9d524f22016-03-29 09:03:52 -070094
reed67c65132015-09-28 06:16:07 -070095 // Now p0...p1 is strictly inside bounds vertically, so we just need to clip horizontally
halcanary9d524f22016-03-29 09:03:52 -070096
reed67c65132015-09-28 06:16:07 -070097 if (p0.fX > p1.fX) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -040098 using std::swap;
99 swap(p0, p1);
reed67c65132015-09-28 06:16:07 -0700100 }
101 // now we're left-to-right: p0 .. p1
halcanary9d524f22016-03-29 09:03:52 -0700102
reed67c65132015-09-28 06:16:07 -0700103 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 }
halcanary9d524f22016-03-29 09:03:52 -0700115
reed67c65132015-09-28 06:16:07 -0700116 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
134static 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//
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400146class EdgeClipView : public Sample {
reed67c65132015-09-28 06:16:07 -0700147 enum {
148 N = 3
149 };
150public:
151 SkPoint fPoly[N];
152 SkRect fClip;
153 SkColor fEdgeColor[N];
halcanary9d524f22016-03-29 09:03:52 -0700154
reed67c65132015-09-28 06:16:07 -0700155 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 }
halcanary9d524f22016-03-29 09:03:52 -0700164
reed67c65132015-09-28 06:16:07 -0700165protected:
Hal Canary8a027312019-07-03 10:55:44 -0400166 SkString name() override { return SkString("EdgeClip"); }
reed67c65132015-09-28 06:16:07 -0700167
168 static SkScalar snap(SkScalar x) {
169 return SkScalarRoundToScalar(x * 0.5f) * 2;
170 }
171 static SkPoint snap(const SkPoint& pt) {
172 return SkPoint::Make(snap(pt.x()), snap(pt.y()));
173 }
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
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);
Hal Canary23e474c2017-05-15 13:35:35 -0400195 canvas->drawLine(fPoly[i], fPoly[j], p);
reed67c65132015-09-28 06:16:07 -0700196 }
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);
reed374772b2016-10-05 17:33:02 -0700217 p.setBlendMode(SkBlendMode::kPlus);
reed67c65132015-09-28 06:16:07 -0700218 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:
Hal Canaryfcf63592019-07-12 11:32:43 -0400228 MyClick() {}
reed67c65132015-09-28 06:16:07 -0700229 virtual void handleMove() = 0;
230 };
halcanary9d524f22016-03-29 09:03:52 -0700231
reed67c65132015-09-28 06:16:07 -0700232 class VertClick : public MyClick {
233 SkPoint* fPt;
234 public:
Hal Canaryfcf63592019-07-12 11:32:43 -0400235 VertClick(SkPoint* pt) : fPt(pt) {}
reed67c65132015-09-28 06:16:07 -0700236 void handleMove() override { *fPt = snap(fCurr); }
237 };
halcanary9d524f22016-03-29 09:03:52 -0700238
reed67c65132015-09-28 06:16:07 -0700239 class DragRectClick : public MyClick {
240 SkRect* fRect;
241 public:
Hal Canaryfcf63592019-07-12 11:32:43 -0400242 DragRectClick(SkRect* rect) : fRect(rect) {}
reed67c65132015-09-28 06:16:07 -0700243 void handleMove() override { fRect->offset(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()); }
244 };
halcanary9d524f22016-03-29 09:03:52 -0700245
reed67c65132015-09-28 06:16:07 -0700246 class DragPolyClick : public MyClick {
247 SkPoint fSrc[100];
248 SkPoint* fPoly;
249 int fCount;
250 public:
Hal Canaryfcf63592019-07-12 11:32:43 -0400251 DragPolyClick(SkPoint poly[], int count) : fPoly(poly), fCount(count)
reed67c65132015-09-28 06:16:07 -0700252 {
253 SkASSERT((size_t)count <= SK_ARRAY_COUNT(fSrc));
254 memcpy(fSrc, poly, count * sizeof(SkPoint));
255 }
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:
Hal Canaryfcf63592019-07-12 11:32:43 -0400267 DoNothingClick() {}
reed67c65132015-09-28 06:16:07 -0700268 void handleMove() override {}
269 };
270
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
Hal Canaryb1f411a2019-08-29 10:39:22 -0400278 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
reed67c65132015-09-28 06:16:07 -0700279 for (int i = 0; i < N; ++i) {
280 if (hit_test(fPoly[i], x, y)) {
Hal Canaryfcf63592019-07-12 11:32:43 -0400281 return new VertClick(&fPoly[i]);
reed67c65132015-09-28 06:16:07 -0700282 }
283 }
halcanary9d524f22016-03-29 09:03:52 -0700284
reed67c65132015-09-28 06:16:07 -0700285 SkPath path;
286 path.addPoly(fPoly, N, true);
287 if (path.contains(x, y)) {
Hal Canaryfcf63592019-07-12 11:32:43 -0400288 return new DragPolyClick(fPoly, N);
reed67c65132015-09-28 06:16:07 -0700289 }
290
291 if (fClip.intersects(SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1))) {
Hal Canaryfcf63592019-07-12 11:32:43 -0400292 return new DragRectClick(&fClip);
reed67c65132015-09-28 06:16:07 -0700293 }
Hal Canaryfcf63592019-07-12 11:32:43 -0400294 return new DoNothingClick();
reed67c65132015-09-28 06:16:07 -0700295 }
halcanary9d524f22016-03-29 09:03:52 -0700296
reed67c65132015-09-28 06:16:07 -0700297 bool onClick(Click* click) override {
298 ((MyClick*)click)->handleMove();
reed67c65132015-09-28 06:16:07 -0700299 return false;
300 }
halcanary9d524f22016-03-29 09:03:52 -0700301
reed67c65132015-09-28 06:16:07 -0700302private:
John Stiles7571f9e2020-09-02 22:42:33 -0400303 using INHERITED = Sample;
reed67c65132015-09-28 06:16:07 -0700304};
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400305
reed67c65132015-09-28 06:16:07 -0700306DEF_SAMPLE( return new EdgeClipView; )