SamplePath.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. /*
  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. #include "include/core/SkBitmap.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkColorFilter.h"
  10. #include "include/core/SkColorPriv.h"
  11. #include "include/core/SkFont.h"
  12. #include "include/core/SkGraphics.h"
  13. #include "include/core/SkPath.h"
  14. #include "include/core/SkRegion.h"
  15. #include "include/core/SkShader.h"
  16. #include "include/core/SkTime.h"
  17. #include "include/core/SkTypeface.h"
  18. #include "include/effects/SkGradientShader.h"
  19. #include "include/utils/SkParsePath.h"
  20. #include "samplecode/Sample.h"
  21. #include "src/utils/SkUTF.h"
  22. #include "tools/timer/TimeUtils.h"
  23. #include "src/core/SkGeometry.h"
  24. #include <stdlib.h>
  25. // http://code.google.com/p/skia/issues/detail?id=32
  26. static void test_cubic() {
  27. SkPoint src[4] = {
  28. { 556.25000f, 523.03003f },
  29. { 556.23999f, 522.96002f },
  30. { 556.21997f, 522.89001f },
  31. { 556.21997f, 522.82001f }
  32. };
  33. SkPoint dst[11];
  34. dst[10].set(42, -42); // one past the end, that we don't clobber these
  35. SkScalar tval[] = { 0.33333334f, 0.99999994f };
  36. SkChopCubicAt(src, dst, tval, 2);
  37. #if 0
  38. for (int i = 0; i < 11; i++) {
  39. SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY);
  40. }
  41. #endif
  42. }
  43. static void test_cubic2() {
  44. const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z";
  45. SkPath path;
  46. SkParsePath::FromSVGString(str, &path);
  47. {
  48. SkRect r = path.getBounds();
  49. SkIRect ir;
  50. r.round(&ir);
  51. SkDebugf("[%g %g %g %g] [%x %x %x %x]\n",
  52. SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop),
  53. SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom),
  54. ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
  55. }
  56. SkBitmap bitmap;
  57. bitmap.allocN32Pixels(300, 200);
  58. SkCanvas canvas(bitmap);
  59. SkPaint paint;
  60. paint.setAntiAlias(true);
  61. canvas.drawPath(path, paint);
  62. }
  63. class PathView : public Sample {
  64. SkScalar fPrevSecs;
  65. public:
  66. SkScalar fDStroke, fStroke, fMinStroke, fMaxStroke;
  67. SkPath fPath[6];
  68. bool fShowHairline;
  69. bool fOnce;
  70. PathView() {
  71. fPrevSecs = 0;
  72. fOnce = false;
  73. }
  74. void init() {
  75. if (fOnce) {
  76. return;
  77. }
  78. fOnce = true;
  79. test_cubic();
  80. test_cubic2();
  81. fShowHairline = false;
  82. fDStroke = 1;
  83. fStroke = 10;
  84. fMinStroke = 10;
  85. fMaxStroke = 180;
  86. const SkScalar V = 85;
  87. fPath[0].moveTo(40, 70);
  88. fPath[0].lineTo(70, 70 + SK_ScalarHalf);
  89. fPath[0].lineTo(110, 70);
  90. fPath[1].moveTo(40, 70);
  91. fPath[1].lineTo(70, 70 - SK_ScalarHalf);
  92. fPath[1].lineTo(110, 70);
  93. fPath[2].moveTo(V, V);
  94. fPath[2].lineTo(50, V);
  95. fPath[2].lineTo(50, 50);
  96. fPath[3].moveTo(50, 50);
  97. fPath[3].lineTo(50, V);
  98. fPath[3].lineTo(V, V);
  99. fPath[4].moveTo(50, 50);
  100. fPath[4].lineTo(50, V);
  101. fPath[4].lineTo(52, 50);
  102. fPath[5].moveTo(52, 50);
  103. fPath[5].lineTo(50, V);
  104. fPath[5].lineTo(50, 50);
  105. this->setBGColor(0xFFDDDDDD);
  106. }
  107. protected:
  108. SkString name() override { return SkString("Paths"); }
  109. void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
  110. SkPaint paint;
  111. paint.setAntiAlias(true);
  112. paint.setStyle(SkPaint::kStroke_Style);
  113. paint.setStrokeJoin(j);
  114. paint.setStrokeWidth(fStroke);
  115. if (fShowHairline) {
  116. SkPath fill;
  117. paint.getFillPath(path, &fill);
  118. paint.setStrokeWidth(0);
  119. canvas->drawPath(fill, paint);
  120. } else {
  121. canvas->drawPath(path, paint);
  122. }
  123. paint.setColor(SK_ColorRED);
  124. paint.setStrokeWidth(0);
  125. canvas->drawPath(path, paint);
  126. }
  127. void onDrawContent(SkCanvas* canvas) override {
  128. this->init();
  129. canvas->translate(50, 50);
  130. static const SkPaint::Join gJoins[] = {
  131. SkPaint::kBevel_Join,
  132. SkPaint::kMiter_Join,
  133. SkPaint::kRound_Join
  134. };
  135. for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) {
  136. canvas->save();
  137. for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) {
  138. this->drawPath(canvas, fPath[j], gJoins[i]);
  139. canvas->translate(200, 0);
  140. }
  141. canvas->restore();
  142. canvas->translate(0, 200);
  143. }
  144. }
  145. bool onAnimate(double nanos) override {
  146. SkScalar currSecs = TimeUtils::Scaled(1e-9 * nanos, 100);
  147. SkScalar delta = currSecs - fPrevSecs;
  148. fPrevSecs = currSecs;
  149. fStroke += fDStroke * delta;
  150. if (fStroke > fMaxStroke || fStroke < fMinStroke) {
  151. fDStroke = -fDStroke;
  152. }
  153. return true;
  154. }
  155. Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
  156. fShowHairline = !fShowHairline;
  157. return nullptr;
  158. }
  159. private:
  160. typedef Sample INHERITED;
  161. };
  162. DEF_SAMPLE( return new PathView; )
  163. //////////////////////////////////////////////////////////////////////////////
  164. #include "include/effects/SkCornerPathEffect.h"
  165. #include "include/utils/SkRandom.h"
  166. class ArcToView : public Sample {
  167. bool fDoFrame, fDoCorner, fDoConic;
  168. SkPaint fPtsPaint, fSkeletonPaint, fCornerPaint;
  169. public:
  170. enum {
  171. N = 4
  172. };
  173. SkPoint fPts[N];
  174. ArcToView()
  175. : fDoFrame(false), fDoCorner(false), fDoConic(false)
  176. {
  177. SkRandom rand;
  178. for (int i = 0; i < N; ++i) {
  179. fPts[i].fX = 20 + rand.nextUScalar1() * 640;
  180. fPts[i].fY = 20 + rand.nextUScalar1() * 480;
  181. }
  182. const SkScalar rad = 50;
  183. fPtsPaint.setAntiAlias(true);
  184. fPtsPaint.setStrokeWidth(15);
  185. fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
  186. fCornerPaint.setAntiAlias(true);
  187. fCornerPaint.setStyle(SkPaint::kStroke_Style);
  188. fCornerPaint.setStrokeWidth(13);
  189. fCornerPaint.setColor(SK_ColorGREEN);
  190. fCornerPaint.setPathEffect(SkCornerPathEffect::Make(rad*2));
  191. fSkeletonPaint.setAntiAlias(true);
  192. fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
  193. fSkeletonPaint.setColor(SK_ColorRED);
  194. }
  195. void toggle(bool& value) {
  196. value = !value;
  197. }
  198. protected:
  199. SkString name() override { return SkString("ArcTo"); }
  200. bool onChar(SkUnichar uni) override {
  201. switch (uni) {
  202. case '1': this->toggle(fDoFrame); return true;
  203. case '2': this->toggle(fDoCorner); return true;
  204. case '3': this->toggle(fDoConic); return true;
  205. default: break;
  206. }
  207. return false;
  208. }
  209. void makePath(SkPath* path) {
  210. path->moveTo(fPts[0]);
  211. for (int i = 1; i < N; ++i) {
  212. path->lineTo(fPts[i]);
  213. }
  214. if (!fDoFrame) {
  215. path->close();
  216. }
  217. }
  218. void onDrawContent(SkCanvas* canvas) override {
  219. canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
  220. SkPath path;
  221. this->makePath(&path);
  222. if (fDoCorner) {
  223. canvas->drawPath(path, fCornerPaint);
  224. }
  225. canvas->drawPath(path, fSkeletonPaint);
  226. }
  227. bool onClick(Click* click) override {
  228. int32_t index;
  229. if (click->fMeta.findS32("index", &index)) {
  230. SkASSERT((unsigned)index < N);
  231. fPts[index] = click->fCurr;
  232. return true;
  233. }
  234. return false;
  235. }
  236. Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
  237. const SkScalar tol = 4;
  238. const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
  239. for (int i = 0; i < N; ++i) {
  240. if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
  241. Click* click = new Click();
  242. click->fMeta.setS32("index", i);
  243. return click;
  244. }
  245. }
  246. return nullptr;
  247. }
  248. private:
  249. typedef Sample INHERITED;
  250. };
  251. DEF_SAMPLE( return new ArcToView; )
  252. /////////////
  253. class FatStroke : public Sample {
  254. bool fClosed, fShowStroke, fShowHidden, fShowSkeleton;
  255. int fJoinType, fCapType;
  256. float fWidth = 30;
  257. SkPaint fPtsPaint, fHiddenPaint, fSkeletonPaint, fStrokePaint;
  258. public:
  259. enum {
  260. N = 4
  261. };
  262. SkPoint fPts[N];
  263. FatStroke() : fClosed(false), fShowStroke(true), fShowHidden(false), fShowSkeleton(true),
  264. fJoinType(0), fCapType(0)
  265. {
  266. SkRandom rand;
  267. for (int i = 0; i < N; ++i) {
  268. fPts[i].fX = 20 + rand.nextUScalar1() * 640;
  269. fPts[i].fY = 20 + rand.nextUScalar1() * 480;
  270. }
  271. fPtsPaint.setAntiAlias(true);
  272. fPtsPaint.setStrokeWidth(10);
  273. fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
  274. fHiddenPaint.setAntiAlias(true);
  275. fHiddenPaint.setStyle(SkPaint::kStroke_Style);
  276. fHiddenPaint.setColor(0xFF0000FF);
  277. fStrokePaint.setAntiAlias(true);
  278. fStrokePaint.setStyle(SkPaint::kStroke_Style);
  279. fStrokePaint.setStrokeWidth(50);
  280. fStrokePaint.setColor(0x8000FF00);
  281. fSkeletonPaint.setAntiAlias(true);
  282. fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
  283. fSkeletonPaint.setColor(SK_ColorRED);
  284. }
  285. void toggle(bool& value) {
  286. value = !value;
  287. }
  288. void toggle3(int& value) {
  289. value = (value + 1) % 3;
  290. }
  291. protected:
  292. SkString name() override { return SkString("FatStroke"); }
  293. bool onChar(SkUnichar uni) override {
  294. switch (uni) {
  295. case '1': this->toggle(fShowSkeleton); return true;
  296. case '2': this->toggle(fShowStroke); return true;
  297. case '3': this->toggle(fShowHidden); return true;
  298. case '4': this->toggle3(fJoinType); return true;
  299. case '5': this->toggle3(fCapType); return true;
  300. case '6': this->toggle(fClosed); return true;
  301. case '-': fWidth -= 5; return true;
  302. case '=': fWidth += 5; return true;
  303. default: break;
  304. }
  305. return false;
  306. }
  307. void makePath(SkPath* path) {
  308. path->moveTo(fPts[0]);
  309. for (int i = 1; i < N; ++i) {
  310. path->lineTo(fPts[i]);
  311. }
  312. if (fClosed) {
  313. path->close();
  314. }
  315. }
  316. void onDrawContent(SkCanvas* canvas) override {
  317. canvas->drawColor(0xFFEEEEEE);
  318. SkPath path;
  319. this->makePath(&path);
  320. fStrokePaint.setStrokeWidth(fWidth);
  321. fStrokePaint.setStrokeJoin((SkPaint::Join)fJoinType);
  322. fStrokePaint.setStrokeCap((SkPaint::Cap)fCapType);
  323. if (fShowStroke) {
  324. canvas->drawPath(path, fStrokePaint);
  325. }
  326. if (fShowHidden) {
  327. SkPath hidden;
  328. fStrokePaint.getFillPath(path, &hidden);
  329. canvas->drawPath(hidden, fHiddenPaint);
  330. }
  331. if (fShowSkeleton) {
  332. canvas->drawPath(path, fSkeletonPaint);
  333. }
  334. canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
  335. }
  336. bool onClick(Click* click) override {
  337. int32_t index;
  338. if (click->fMeta.findS32("index", &index)) {
  339. SkASSERT((unsigned)index < N);
  340. fPts[index] = click->fCurr;
  341. return true;
  342. }
  343. return false;
  344. }
  345. Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
  346. const SkScalar tol = 4;
  347. const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
  348. for (int i = 0; i < N; ++i) {
  349. if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
  350. Click* click = new Click();
  351. click->fMeta.setS32("index", i);
  352. return click;
  353. }
  354. }
  355. return nullptr;
  356. }
  357. private:
  358. typedef Sample INHERITED;
  359. };
  360. DEF_SAMPLE( return new FatStroke; )
  361. static int compute_parallel_to_base(const SkPoint pts[4], SkScalar t[2]) {
  362. // F = At^3 + Bt^2 + Ct + D
  363. SkVector A = pts[3] - pts[0] + (pts[1] - pts[2]) * 3.0f;
  364. SkVector B = (pts[0] - pts[1] - pts[1] + pts[2]) * 3.0f;
  365. SkVector C = (pts[1] - pts[0]) * 3.0f;
  366. SkVector DA = pts[3] - pts[0];
  367. // F' = 3At^2 + 2Bt + C
  368. SkScalar a = 3 * A.cross(DA);
  369. SkScalar b = 2 * B.cross(DA);
  370. SkScalar c = C.cross(DA);
  371. int n = SkFindUnitQuadRoots(a, b, c, t);
  372. SkString str;
  373. for (int i = 0; i < n; ++i) {
  374. str.appendf(" %g", t[i]);
  375. }
  376. SkDebugf("roots %s\n", str.c_str());
  377. return n;
  378. }
  379. class CubicCurve : public Sample {
  380. public:
  381. enum {
  382. N = 4
  383. };
  384. SkPoint fPts[N];
  385. CubicCurve() {
  386. SkRandom rand;
  387. for (int i = 0; i < N; ++i) {
  388. fPts[i].fX = 20 + rand.nextUScalar1() * 640;
  389. fPts[i].fY = 20 + rand.nextUScalar1() * 480;
  390. }
  391. }
  392. protected:
  393. SkString name() override { return SkString("CubicCurve"); }
  394. void onDrawContent(SkCanvas* canvas) override {
  395. SkPaint paint;
  396. paint.setAntiAlias(true);
  397. {
  398. SkPath path;
  399. path.moveTo(fPts[0]);
  400. path.cubicTo(fPts[1], fPts[2], fPts[3]);
  401. paint.setStyle(SkPaint::kStroke_Style);
  402. canvas->drawPath(path, paint);
  403. }
  404. {
  405. paint.setColor(SK_ColorRED);
  406. SkScalar t[2];
  407. int n = compute_parallel_to_base(fPts, t);
  408. SkPoint loc;
  409. SkVector tan;
  410. for (int i = 0; i < n; ++i) {
  411. SkEvalCubicAt(fPts, t[i], &loc, &tan, nullptr);
  412. tan.setLength(30);
  413. canvas->drawLine(loc - tan, loc + tan, paint);
  414. }
  415. paint.setStrokeWidth(0.5f);
  416. canvas->drawLine(fPts[0], fPts[3], paint);
  417. paint.setColor(SK_ColorBLUE);
  418. paint.setStrokeWidth(6);
  419. SkEvalCubicAt(fPts, 0.5f, &loc, nullptr, nullptr);
  420. canvas->drawPoint(loc, paint);
  421. paint.setColor(0xFF008800);
  422. SkEvalCubicAt(fPts, 1.0f/3, &loc, nullptr, nullptr);
  423. canvas->drawPoint(loc, paint);
  424. SkEvalCubicAt(fPts, 2.0f/3, &loc, nullptr, nullptr);
  425. canvas->drawPoint(loc, paint);
  426. // n = SkFindCubicInflections(fPts, t);
  427. // printf("inflections %d %g %g\n", n, t[0], t[1]);
  428. }
  429. {
  430. paint.setStyle(SkPaint::kFill_Style);
  431. paint.setColor(SK_ColorRED);
  432. for (SkPoint p : fPts) {
  433. canvas->drawCircle(p.fX, p.fY, 8, paint);
  434. }
  435. }
  436. }
  437. bool onClick(Click* click) override {
  438. int32_t index;
  439. if (click->fMeta.findS32("index", &index)) {
  440. SkASSERT((unsigned)index < N);
  441. fPts[index] = click->fCurr;
  442. return true;
  443. }
  444. return false;
  445. }
  446. Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
  447. const SkScalar tol = 8;
  448. const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
  449. for (int i = 0; i < N; ++i) {
  450. if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
  451. Click* click = new Click();
  452. click->fMeta.setS32("index", i);
  453. return click;
  454. }
  455. }
  456. return this->INHERITED::onFindClickHandler(x, y, modi);
  457. }
  458. private:
  459. typedef Sample INHERITED;
  460. };
  461. DEF_SAMPLE( return new CubicCurve; )
  462. static SkPoint lerp(SkPoint a, SkPoint b, float t) {
  463. return a * (1 - t) + b * t;
  464. }
  465. static int find_max_deviation_cubic(const SkPoint src[4], SkScalar ts[2]) {
  466. // deviation = F' x (d - a) == 0, solve for t(s)
  467. // F = At^3 + Bt^2 + Ct + D
  468. // F' = 3At^2 + 2Bt + C
  469. // Z = d - a
  470. // F' x Z = 3(A x Z)t^2 + 2(B x Z)t + (C x Z)
  471. //
  472. SkVector A = src[3] + (src[1] - src[2]) * 3 - src[0];
  473. SkVector B = (src[2] - src[1] - src[1] + src[0]) * 3;
  474. SkVector C = (src[1] - src[0]) * 3;
  475. SkVector Z = src[3] - src[0];
  476. // now forumlate the quadratic coefficients we need to solve for t : F' x Z
  477. return SkFindUnitQuadRoots(3 * A.cross(Z), 2 * B.cross(Z), C.cross(Z), ts);
  478. }
  479. class CubicCurve2 : public Sample {
  480. public:
  481. enum {
  482. N = 7
  483. };
  484. SkPoint fPts[N];
  485. SkPoint* fQuad = fPts + 4;
  486. SkScalar fT = 0.5f;
  487. bool fShowSub = false;
  488. bool fShowFlatness = false;
  489. SkScalar fScale = 0.75;
  490. CubicCurve2() {
  491. fPts[0] = { 90, 300 };
  492. fPts[1] = { 30, 60 };
  493. fPts[2] = { 250, 30 };
  494. fPts[3] = { 350, 200 };
  495. fQuad[0] = fPts[0] + SkVector{ 300, 0};
  496. fQuad[1] = fPts[1] + SkVector{ 300, 0};
  497. fQuad[2] = fPts[2] + SkVector{ 300, 0};
  498. }
  499. protected:
  500. SkString name() override { return SkString("CubicCurve2"); }
  501. bool onChar(SkUnichar uni) override {
  502. switch (uni) {
  503. case 's': fShowSub = !fShowSub; break;
  504. case 'f': fShowFlatness = !fShowFlatness; break;
  505. case '-': fT -= 1.0f / 32; break;
  506. case '=': fT += 1.0f / 32; break;
  507. default: return false;
  508. }
  509. fT = std::min(1.0f, std::max(0.0f, fT));
  510. return true;
  511. }
  512. void showFrame(SkCanvas* canvas, const SkPoint pts[], int count, const SkPaint& p) {
  513. SkPaint paint(p);
  514. SkPoint storage[3 + 2 + 1];
  515. SkPoint* tmp = storage;
  516. const SkPoint* prev = pts;
  517. int n = count;
  518. for (int n = count; n > 0; --n) {
  519. for (int i = 0; i < n; ++i) {
  520. canvas->drawLine(prev[i], prev[i+1], paint);
  521. tmp[i] = lerp(prev[i], prev[i+1], fT);
  522. }
  523. prev = tmp;
  524. tmp += n;
  525. }
  526. paint.setColor(SK_ColorBLUE);
  527. paint.setStyle(SkPaint::kFill_Style);
  528. n = tmp - storage;
  529. for (int i = 0; i < n; ++i) {
  530. canvas->drawCircle(storage[i].fX, storage[i].fY, 4, paint);
  531. }
  532. }
  533. void showFlattness(SkCanvas* canvas) {
  534. SkPaint paint;
  535. paint.setStyle(SkPaint::kStroke_Style);
  536. paint.setAntiAlias(true);
  537. SkPaint paint2(paint);
  538. paint2.setColor(0xFF008800);
  539. paint.setColor(0xFF888888);
  540. canvas->drawLine(fPts[0], fPts[3], paint);
  541. canvas->drawLine(fQuad[0], fQuad[2], paint);
  542. paint.setColor(0xFF0000FF);
  543. SkPoint pts[2];
  544. pts[0] = (fQuad[0] + fQuad[1] + fQuad[1] + fQuad[2])*0.25;
  545. pts[1] = (fQuad[0] + fQuad[2]) * 0.5;
  546. canvas->drawLine(pts[0], pts[1], paint);
  547. // cubic
  548. SkVector v0 = (fPts[0] - fPts[1] - fPts[1] + fPts[2]) * fScale;
  549. SkVector v1 = (fPts[1] - fPts[2] - fPts[2] + fPts[3]) * fScale;
  550. SkVector v = (v0 + v1) * 0.5f;
  551. SkPoint anchor;
  552. SkScalar ts[2];
  553. int n = find_max_deviation_cubic(fPts, ts);
  554. if (n > 0) {
  555. SkEvalCubicAt(fPts, ts[0], &anchor, nullptr, nullptr);
  556. canvas->drawLine(anchor, anchor + v, paint2);
  557. canvas->drawLine(anchor, anchor + v0, paint);
  558. if (n == 2) {
  559. SkEvalCubicAt(fPts, ts[1], &anchor, nullptr, nullptr);
  560. canvas->drawLine(anchor, anchor + v, paint2);
  561. }
  562. canvas->drawLine(anchor, anchor + v1, paint);
  563. }
  564. // not sure we can get here
  565. }
  566. void onDrawContent(SkCanvas* canvas) override {
  567. SkPaint paint;
  568. paint.setAntiAlias(true);
  569. {
  570. paint.setStyle(SkPaint::kStroke_Style);
  571. SkPath path;
  572. path.moveTo(fPts[0]);
  573. path.cubicTo(fPts[1], fPts[2], fPts[3]);
  574. path.moveTo(fQuad[0]);
  575. path.quadTo(fQuad[1], fQuad[2]);
  576. canvas->drawPath(path, paint);
  577. }
  578. if (fShowSub) {
  579. paint.setColor(SK_ColorRED);
  580. paint.setStrokeWidth(1.7f);
  581. this->showFrame(canvas, fPts, 3, paint);
  582. this->showFrame(canvas, fQuad, 2, paint);
  583. paint.setColor(SK_ColorBLACK);
  584. paint.setStyle(SkPaint::kFill_Style);
  585. SkFont font(nullptr, 20);
  586. canvas->drawString(SkStringPrintf("t = %g", fT), 20, 20, font, paint);
  587. }
  588. if (fShowFlatness) {
  589. this->showFlattness(canvas);
  590. }
  591. paint.setStyle(SkPaint::kFill_Style);
  592. paint.setColor(SK_ColorRED);
  593. for (SkPoint p : fPts) {
  594. canvas->drawCircle(p.fX, p.fY, 7, paint);
  595. }
  596. {
  597. SkScalar ts[2];
  598. int n = SkFindCubicInflections(fPts, ts);
  599. for (int i = 0; i < n; ++i) {
  600. SkPoint p;
  601. SkEvalCubicAt(fPts, ts[i], &p, nullptr, nullptr);
  602. canvas->drawCircle(p.fX, p.fY, 3, paint);
  603. }
  604. }
  605. }
  606. bool onClick(Click* click) override {
  607. int32_t index;
  608. if (click->fMeta.findS32("index", &index)) {
  609. SkASSERT((unsigned)index < N);
  610. fPts[index] = click->fCurr;
  611. return true;
  612. }
  613. return false;
  614. }
  615. Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, ModifierKey modi) override {
  616. const SkScalar tol = 8;
  617. const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
  618. for (int i = 0; i < N; ++i) {
  619. if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
  620. Click* click = new Click();
  621. click->fMeta.setS32("index", i);
  622. return click;
  623. }
  624. }
  625. return this->INHERITED::onFindClickHandler(x, y, modi);
  626. }
  627. private:
  628. typedef Sample INHERITED;
  629. };
  630. DEF_SAMPLE( return new CubicCurve2; )