blob: 0e2546e771d89cade72940ad935ba6ac522d3799 [file] [log] [blame]
reed@google.com3bcf8d32011-10-10 15:42:36 +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 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkColorPriv.h"
10#include "include/core/SkFont.h"
11#include "include/core/SkPaint.h"
12#include "include/core/SkPath.h"
13#include "include/utils/SkRandom.h"
14#include "samplecode/Sample.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/core/SkClipOpPriv.h"
reed@google.com3bcf8d32011-10-10 15:42:36 +000016
Ben Wagner8a1036c2016-11-09 15:00:49 -050017constexpr int W = 150;
18constexpr int H = 200;
reed@google.com3bcf8d32011-10-10 15:42:36 +000019
reed@google.com045e62d2011-10-24 12:19:46 +000020static void show_text(SkCanvas* canvas, bool doAA) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000021 SkRandom rand;
reed@google.com3bcf8d32011-10-10 15:42:36 +000022 SkPaint paint;
Hal Canary4484b8f2019-01-08 14:00:08 -050023 SkFont font(nullptr, 20);
24 font.setEdging(doAA ? SkFont::Edging::kSubpixelAntiAlias : SkFont::Edging::kAlias);
rmistry@google.comae933ce2012-08-23 18:19:56 +000025
reed@google.com045e62d2011-10-24 12:19:46 +000026 for (int i = 0; i < 200; ++i) {
reed@google.com3bcf8d32011-10-10 15:42:36 +000027 paint.setColor((SK_A32_MASK << SK_A32_SHIFT) | rand.nextU());
Hal Canary4484b8f2019-01-08 14:00:08 -050028 canvas->drawString("Hamburgefons", rand.nextSScalar1() * W, rand.nextSScalar1() * H + 20,
29 font, paint);
reed@google.com3bcf8d32011-10-10 15:42:36 +000030 }
31}
32
reed@google.com045e62d2011-10-24 12:19:46 +000033static void show_fill(SkCanvas* canvas, bool doAA) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000034 SkRandom rand;
reed@google.com3bcf8d32011-10-10 15:42:36 +000035 SkPaint paint;
reed@google.com045e62d2011-10-24 12:19:46 +000036 paint.setAntiAlias(doAA);
rmistry@google.comae933ce2012-08-23 18:19:56 +000037
reed@google.com045e62d2011-10-24 12:19:46 +000038 for (int i = 0; i < 50; ++i) {
reed@google.com3bcf8d32011-10-10 15:42:36 +000039 SkRect r;
40 SkPath p;
rmistry@google.comae933ce2012-08-23 18:19:56 +000041
reed@google.com3bcf8d32011-10-10 15:42:36 +000042 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
43 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
reed@google.com3bcf8d32011-10-10 15:42:36 +000044 paint.setColor(rand.nextU());
45 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000046
reed@google.com3bcf8d32011-10-10 15:42:36 +000047 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
48 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
reed@google.com3bcf8d32011-10-10 15:42:36 +000049 paint.setColor(rand.nextU());
50 p.addOval(r);
51 canvas->drawPath(p, paint);
52 }
53}
54
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000055static SkScalar randRange(SkRandom& rand, SkScalar min, SkScalar max) {
reed@google.com045e62d2011-10-24 12:19:46 +000056 SkASSERT(min <= max);
Mike Reeddf85c382017-02-14 10:59:19 -050057 return min + rand.nextUScalar1() * (max - min);
reed@google.com045e62d2011-10-24 12:19:46 +000058}
59
60static void show_stroke(SkCanvas* canvas, bool doAA, SkScalar strokeWidth, int n) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000061 SkRandom rand;
reed@google.com045e62d2011-10-24 12:19:46 +000062 SkPaint paint;
63 paint.setAntiAlias(doAA);
64 paint.setStyle(SkPaint::kStroke_Style);
65 paint.setStrokeWidth(strokeWidth);
rmistry@google.comae933ce2012-08-23 18:19:56 +000066
reed@google.com045e62d2011-10-24 12:19:46 +000067 for (int i = 0; i < n; ++i) {
68 SkRect r;
69 SkPath p;
rmistry@google.comae933ce2012-08-23 18:19:56 +000070
reed@google.com045e62d2011-10-24 12:19:46 +000071 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
72 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
73 paint.setColor(rand.nextU());
74 canvas->drawRect(r, paint);
rmistry@google.comae933ce2012-08-23 18:19:56 +000075
reed@google.com045e62d2011-10-24 12:19:46 +000076 r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
77 rand.nextUScalar1() * W, rand.nextUScalar1() * H);
78 paint.setColor(rand.nextU());
79 p.addOval(r);
80 canvas->drawPath(p, paint);
81
82 const SkScalar minx = -SkIntToScalar(W)/4;
83 const SkScalar maxx = 5*SkIntToScalar(W)/4;
84 const SkScalar miny = -SkIntToScalar(H)/4;
85 const SkScalar maxy = 5*SkIntToScalar(H)/4;
86 paint.setColor(rand.nextU());
87 canvas->drawLine(randRange(rand, minx, maxx), randRange(rand, miny, maxy),
88 randRange(rand, minx, maxx), randRange(rand, miny, maxy),
89 paint);
90 }
91}
92
93static void show_hair(SkCanvas* canvas, bool doAA) {
94 show_stroke(canvas, doAA, 0, 150);
95}
96
97static void show_thick(SkCanvas* canvas, bool doAA) {
98 show_stroke(canvas, doAA, SkIntToScalar(5), 50);
99}
100
101typedef void (*CanvasProc)(SkCanvas*, bool);
102
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400103class ClipView : public Sample {
Hal Canaryd7639af2019-07-17 09:08:11 -0400104 SkString name() override { return SkString("Clip"); }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000105
Hal Canaryd7639af2019-07-17 09:08:11 -0400106 void onDrawContent(SkCanvas* canvas) override {
reed@google.com045e62d2011-10-24 12:19:46 +0000107 canvas->drawColor(SK_ColorWHITE);
reed@google.com3bcf8d32011-10-10 15:42:36 +0000108 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
109
110 static const CanvasProc gProc[] = {
reed@google.com045e62d2011-10-24 12:19:46 +0000111 show_text, show_thick, show_hair, show_fill
reed@google.com3bcf8d32011-10-10 15:42:36 +0000112 };
rmistry@google.comae933ce2012-08-23 18:19:56 +0000113
reed@google.com3bcf8d32011-10-10 15:42:36 +0000114 SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
115 SkPath clipPath;
116 r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
Mike Reed4241f5e2019-09-14 19:13:23 +0000117 clipPath.addRoundRect(r, SkIntToScalar(20), SkIntToScalar(20));
reed@google.com045e62d2011-10-24 12:19:46 +0000118
119// clipPath.toggleInverseFillType();
reed@google.com3bcf8d32011-10-10 15:42:36 +0000120
121 for (int aa = 0; aa <= 1; ++aa) {
122 canvas->save();
123 for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
124 canvas->save();
Mike Reedc1f77742016-12-09 09:00:50 -0500125 canvas->clipPath(clipPath, kIntersect_SkClipOp, SkToBool(aa));
reed@google.com045e62d2011-10-24 12:19:46 +0000126// canvas->drawColor(SK_ColorWHITE);
127 gProc[i](canvas, SkToBool(aa));
reed@google.com3bcf8d32011-10-10 15:42:36 +0000128 canvas->restore();
129 canvas->translate(W * SK_Scalar1 * 8 / 7, 0);
130 }
131 canvas->restore();
132 canvas->translate(0, H * SK_Scalar1 * 8 / 7);
133 }
134 }
reed@google.com3bcf8d32011-10-10 15:42:36 +0000135};
136
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400137DEF_SAMPLE( return new ClipView(); )
Mike Reed63edc572019-11-15 21:58:01 -0500138
139///////////////////////////////////////////////////////////////////////////////
140
141struct SkHalfPlane {
142 SkScalar fA, fB, fC;
143
144 SkScalar operator()(SkScalar x, SkScalar y) const {
145 return fA * x + fB * y + fC;
146 }
147
148 bool twoPts(SkPoint pts[2]) const {
149 if (fB) {
150 pts[0] = { 0, -fC / fB };
151 pts[1] = { 1, (-fC - fA) / fB };
152 } else if (fA) {
153 pts[0] = { -fC / fA, 0 };
154 pts[1] = { (-fC - fB) / fA, 1 };
155 } else {
156 return false;
157 }
158 return true;
159 }
160};
161
162#include "src/core/SkEdgeClipper.h"
163
164static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
165 SkMatrix mx, inv;
166 SkVector v = p1 - p0;
167 mx.setAll(v.fX, -v.fY, p0.fX,
168 v.fY, v.fX, p0.fY,
169 0, 0, 1);
170 SkAssertResult(mx.invert(&inv));
171
172 SkPath rotated;
173 path.transform(inv, &rotated);
174
175 SkScalar big = 1e28f;
176 SkRect clip = {-big, 0, big, big };
177
178 struct Rec {
179 SkPath fResult;
180 SkPoint fPrev;
181 } rec;
182
183 SkEdgeClipper::ClipPath(rotated, clip, false,
184 [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
185 Rec* rec = (Rec*)ctx;
186
187 bool addLineTo = false;
188 SkPoint pts[4];
189 SkPath::Verb verb;
190 while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
191 if (newCtr) {
192 rec->fResult.moveTo(pts[0]);
193 rec->fPrev = pts[0];
194 newCtr = false;
195 }
196
197 if (addLineTo || pts[0] != rec->fPrev) {
198 rec->fResult.lineTo(pts[0]);
199 }
200
201 switch (verb) {
202 case SkPath::kLine_Verb:
203 rec->fResult.lineTo (pts[1]);
204 rec->fPrev = pts[1];
205 break;
206 case SkPath::kQuad_Verb:
207 rec->fResult.quadTo(pts[1], pts[2]);
208 rec->fPrev = pts[2];
209 break;
210 case SkPath::kCubic_Verb:
211 rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
212 rec->fPrev = pts[3];
213 break;
214 default: break;
215 }
216 addLineTo = true;
217 }
218 }, &rec);
219
220 rec.fResult.transform(mx);
221 return rec.fResult;
222}
223
224static SkPath clip(const SkPath& path, const SkHalfPlane& plane) {
225 SkPoint pts[2];
226 if (plane.twoPts(pts)) {
227 return clip(path, pts[0], pts[1]);
228 } else {
229 return SkPath();
230 }
231}
232
233static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
234 SkVector v = p1 - p0;
235 p0 = p0 - v * 1000;
236 p1 = p1 + v * 1000;
237
238 SkPaint paint;
239 paint.setColor(c);
240 canvas->drawLine(p0, p1, paint);
241}
242
243static SkPath make_path() {
244 SkRandom rand;
245 auto rand_pt = [&rand]() { return SkPoint{rand.nextF() * 400, rand.nextF() * 400}; };
246
247 SkPath path;
248 for (int i = 0; i < 4; ++i) {
249 path.moveTo(rand_pt()).quadTo(rand_pt(), rand_pt())
250 .quadTo(rand_pt(), rand_pt()).lineTo(rand_pt());
251 }
252 return path;
253}
254
255class HalfPlaneView : public Sample {
256 SkPoint fPts[2];
257 SkPath fPath;
258
259 SkString name() override { return SkString("halfplane"); }
260
261 void onOnceBeforeDraw() override {
262 fPts[0] = {0, 0};
263 fPts[1] = {3, 2};
264 fPath = make_path();
265 }
266
267 void onDrawContent(SkCanvas* canvas) override {
268 SkPaint paint;
269
270 paint.setColor({0.5f, 0.5f, 0.5f, 1.0f}, nullptr);
271 canvas->drawPath(fPath, paint);
272
273 paint.setColor({0, 0, 0, 1}, nullptr);
274 canvas->drawPath(clip(fPath, fPts[0], fPts[1]), paint);
275
276 draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
277 }
278
279 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
280 return new Click;
281 }
282
283 bool onClick(Click* click) override {
284 fPts[0] = click->fCurr;
285 fPts[1] = fPts[0] + SkPoint{3, 2};
286 return true;
287 }
288};
289DEF_SAMPLE( return new HalfPlaneView(); )
290
291static void draw_halfplane(SkCanvas* canvas, const SkHalfPlane& p, SkColor c) {
292 SkPoint pts[2];
293 p.twoPts(pts);
294 draw_halfplane(canvas, pts[0], pts[1], c);
295}
296
297static void compute_half_planes(const SkMatrix& mx, SkScalar W, SkScalar H,
298 SkHalfPlane planes[4]) {
299 SkScalar a = mx[0], b = mx[1], c = mx[2],
300 d = mx[3], e = mx[4], f = mx[5],
301 g = mx[6], h = mx[7], i = mx[8];
302
303 planes[0] = { 2*g - 2*a/W, 2*h - 2*b/W, 2*i - 2*c/W };
304 planes[1] = { 2*a/W, 2*b/W, 2*c/W };
305 planes[2] = { 2*g - 2*d/H, 2*h - 2*e/H, 2*i - 2*f/H };
306 planes[3] = { 2*d/H, 2*e/H, 2*f/H };
307}
308
309class HalfPlaneView2 : public Sample {
310 SkPoint fPts[4];
311 SkPath fPath;
312
313 SkString name() override { return SkString("halfplane2"); }
314
315 void onOnceBeforeDraw() override {
316 fPath = make_path();
317 SkRect r = fPath.getBounds();
318 r.toQuad(fPts);
319 }
320
321 void onDrawContent(SkCanvas* canvas) override {
322 SkMatrix mx;
323 {
324 SkRect r = fPath.getBounds();
325 SkPoint src[4];
326 r.toQuad(src);
327 mx.setPolyToPoly(src, fPts, 4);
328 }
329
330 SkPaint paint;
331 canvas->drawPath(fPath, paint);
332
333 canvas->save();
334 canvas->concat(mx);
335 paint.setColor(0x40FF0000);
336 canvas->drawPath(fPath, paint);
337 canvas->restore();
338
339 // draw the frame
340 paint.setStrokeWidth(10);
341 paint.setColor(SK_ColorGREEN);
342 canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPts, paint);
343
344 // draw the half-planes
345 SkHalfPlane planes[4];
346 compute_half_planes(mx, 400, 400, planes);
347 for (auto& p : planes) {
348 draw_halfplane(canvas, p, SK_ColorRED);
349 }
350 }
351
352 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
353 SkScalar r = 8;
354 SkRect rect = SkRect::MakeXYWH(x - r, y - r, 2*r, 2*r);
355 for (int i = 0; i < 4; ++i) {
356 if (rect.contains(fPts[i].fX, fPts[i].fY)) {
357 Click* c = new Click;
358 c->fMeta.setS32("index", i);
359 return c;
360 }
361 }
362 return nullptr;
363 }
364
365 bool onClick(Click* click) override {
366 int32_t index;
367 SkAssertResult(click->fMeta.findS32("index", &index));
368 SkASSERT(index >= 0 && index < 4);
369 fPts[index] = click->fCurr;
370 return true;
371 }
372};
373DEF_SAMPLE( return new HalfPlaneView2(); )
374
375#include "include/core/SkMatrix44.h"
376#include "include/utils/Sk3D.h"
377#include "tools/Resources.h"
378
379static SkMatrix44 inv(const SkMatrix44& m) {
380 SkMatrix44 inverse;
381 SkAssertResult(m.invert(&inverse));
382 return inverse;
383}
384
385static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPlane planes[6]) {
386 float mx[16];
387 m44.asColMajorf(mx);
388
389 SkScalar a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
390 e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
391 i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
392 m = mx[3], n = mx[7], /* o = mx[11], */ p = mx[15];
393
394 a = 2*a/W - m; b = 2*b/W - n; d = 2*d/W - p;
395 e = 2*e/H - m; f = 2*f/H - n; h = 2*h/H - p;
396 i = 2*i - m; j = 2*j - n; l = 2*l - p;
397
398 planes[0] = { m - a, n - b, p - d }; // w - x
399 planes[1] = { m + a, n + b, p + d }; // w + x
400 planes[2] = { m - e, n - f, p - h }; // w - y
401 planes[3] = { m + e, n + f, p + h }; // w + y
402 planes[4] = { m - i, n - j, p - l }; // w - z
403 planes[5] = { m + i, n + j, p + l }; // w + z
404}
405
406class HalfPlaneView3 : public Sample {
407 float fNear = 0.05f;
408 float fFar = 4;
409 float fAngle = SK_ScalarPI / 4;
410
411 SkPoint3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
412 SkPoint3 fCOA { 0, 0, 0 };
413 SkPoint3 fUp { 0, 1, 0 };
414
415 SkMatrix44 fRot;
416
417 SkPath fPath;
418 sk_sp<SkShader> fShader;
419
420 SkString name() override { return SkString("halfplane3"); }
421
422 void onOnceBeforeDraw() override {
423 fPath = make_path();
424 fShader = GetResourceAsImage("images/mandrill_128.png")
425 ->makeShader(SkMatrix::MakeScale(3, 3));
426 }
427
428 void rotate(float x, float y, float z) {
429 SkMatrix44 r;
430 if (x) {
431 r.setRotateAboutUnit(1, 0, 0, x);
432 } else if (y) {
433 r.setRotateAboutUnit(0, 1, 0, y);
434 } else {
435 r.setRotateAboutUnit(0, 0, 1, z);
436 }
437 fRot.postConcat(r);
438 }
439
440 SkMatrix44 get44() const {
441 SkMatrix44 camera,
442 perspective,
443 viewport;
444
445 Sk3Perspective(&perspective, fNear, fFar, fAngle);
446 Sk3LookAt(&camera, fEye, fCOA, fUp);
447 viewport.setScale(200, 200, 1).postTranslate( 200, 200, 0);
448
449 return viewport * perspective * camera * fRot * inv(viewport);
450 }
451
452 void onDrawContent(SkCanvas* canvas) override {
453 SkMatrix44 mx44 = this->get44();
454 SkMatrix mx = mx44;
455
456 SkPaint paint;
457 paint.setColor({0.75, 0.75, 0.75, 1});
458 canvas->drawPath(fPath, paint);
459
460 paint.setShader(fShader);
461
462 canvas->save();
463 canvas->concat(mx);
464 paint.setAlphaf(0.33f);
465 canvas->drawPath(fPath, paint);
466 paint.setAlphaf(1.f);
467 canvas->restore();
468
469 SkHalfPlane planes[6];
470 half_planes(mx44, 400, 400, planes);
471
472 SkPath path = clip(fPath, planes[4]);
473 canvas->save();
474 canvas->concat(mx);
475 canvas->drawPath(path, paint);
476 canvas->restore();
477
478// for (auto& p : planes) {
479// draw_halfplane(canvas, p, SK_ColorRED);
480// }
481 draw_halfplane(canvas, planes[4], SK_ColorBLUE);
482 draw_halfplane(canvas, planes[5], SK_ColorGREEN);
483 }
484
485 bool onChar(SkUnichar uni) override {
486 float delta = SK_ScalarPI / 30;
487 switch (uni) {
488 case '8': this->rotate( delta, 0, 0); return true;
489 case '2': this->rotate(-delta, 0, 0); return true;
490 case '4': this->rotate(0, delta, 0); return true;
491 case '6': this->rotate(0, -delta, 0); return true;
492 case '-': this->rotate(0, 0, delta); return true;
493 case '+': this->rotate(0, 0, -delta); return true;
494
495 case 'i': fEye.fZ += 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
496 case 'k': fEye.fZ -= 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
497
498 case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
499 case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
500 case 'f': fFar += 0.1f; SkDebugf("far %g\n", fFar); return true;
501 case 'F': fFar -= 0.1f; SkDebugf("far %g\n", fFar); return true;
502 default: break;
503 }
504 return false;
505 }
506 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
507 return nullptr;
508 }
509
510 bool onClick(Click* click) override {
511 return false;
512 }
513};
514DEF_SAMPLE( return new HalfPlaneView3(); )