SampleQuadStroker.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. /*
  2. * Copyright 2012 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/SkBlendMode.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkColor.h"
  10. #include "include/core/SkFont.h"
  11. #include "include/core/SkImageInfo.h"
  12. #include "include/core/SkMatrix.h"
  13. #include "include/core/SkPaint.h"
  14. #include "include/core/SkPath.h"
  15. #include "include/core/SkPathMeasure.h"
  16. #include "include/core/SkPoint.h"
  17. #include "include/core/SkRRect.h"
  18. #include "include/core/SkRect.h"
  19. #include "include/core/SkRefCnt.h"
  20. #include "include/core/SkScalar.h"
  21. #include "include/core/SkShader.h"
  22. #include "include/core/SkString.h"
  23. #include "include/core/SkSurface.h"
  24. #include "include/core/SkTypes.h"
  25. #include "include/private/SkTArray.h"
  26. #include "include/private/SkTemplates.h"
  27. #include "include/utils/SkTextUtils.h"
  28. #include "samplecode/Sample.h"
  29. #include "src/core/SkGeometry.h"
  30. #include "src/core/SkPointPriv.h"
  31. #include "src/core/SkStroke.h"
  32. #include "tools/ToolUtils.h"
  33. #include <cfloat>
  34. class SkEvent;
  35. static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
  36. const SkScalar TOL = 7;
  37. return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
  38. }
  39. static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
  40. SkPath::RawIter iter(path);
  41. SkPoint pts[4];
  42. SkPath::Verb verb;
  43. int count = 0;
  44. while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
  45. switch (verb) {
  46. case SkPath::kMove_Verb:
  47. case SkPath::kLine_Verb:
  48. case SkPath::kQuad_Verb:
  49. case SkPath::kConic_Verb:
  50. case SkPath::kCubic_Verb:
  51. storage[count++] = pts[0];
  52. break;
  53. default:
  54. break;
  55. }
  56. }
  57. return count;
  58. }
  59. static void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) {
  60. SkPath::RawIter iter(path);
  61. SkPoint pts[4];
  62. SkPath::Verb verb;
  63. int count = 0;
  64. while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
  65. switch (verb) {
  66. case SkPath::kMove_Verb:
  67. case SkPath::kLine_Verb:
  68. count += 1;
  69. break;
  70. case SkPath::kQuad_Verb:
  71. case SkPath::kConic_Verb:
  72. count += 2;
  73. break;
  74. case SkPath::kCubic_Verb:
  75. count += 3;
  76. break;
  77. case SkPath::kClose_Verb:
  78. contourCounts->push_back(count);
  79. count = 0;
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. if (count > 0) {
  86. contourCounts->push_back(count);
  87. }
  88. }
  89. static void erase(const sk_sp<SkSurface>& surface) {
  90. SkCanvas* canvas = surface->getCanvas();
  91. if (canvas) {
  92. canvas->clear(SK_ColorTRANSPARENT);
  93. }
  94. }
  95. struct StrokeTypeButton {
  96. SkRect fBounds;
  97. char fLabel;
  98. bool fEnabled;
  99. };
  100. struct CircleTypeButton : public StrokeTypeButton {
  101. bool fFill;
  102. };
  103. class QuadStrokerView : public Sample {
  104. enum {
  105. SKELETON_COLOR = 0xFF0000FF,
  106. WIREFRAME_COLOR = 0x80FF0000
  107. };
  108. enum {
  109. kCount = 18
  110. };
  111. SkPoint fPts[kCount];
  112. SkRect fWeightControl;
  113. SkRect fRadiusControl;
  114. SkRect fErrorControl;
  115. SkRect fWidthControl;
  116. SkRect fBounds;
  117. SkMatrix fMatrix, fInverse;
  118. sk_sp<SkShader> fShader;
  119. sk_sp<SkSurface> fMinSurface;
  120. sk_sp<SkSurface> fMaxSurface;
  121. StrokeTypeButton fCubicButton;
  122. StrokeTypeButton fConicButton;
  123. StrokeTypeButton fQuadButton;
  124. StrokeTypeButton fArcButton;
  125. StrokeTypeButton fRRectButton;
  126. CircleTypeButton fCircleButton;
  127. StrokeTypeButton fTextButton;
  128. SkString fText;
  129. SkScalar fTextSize;
  130. SkScalar fWeight;
  131. SkScalar fRadius;
  132. SkScalar fWidth, fDWidth;
  133. SkScalar fWidthScale;
  134. int fW, fH, fZoom;
  135. bool fAnimate;
  136. bool fDrawRibs;
  137. bool fDrawTangents;
  138. bool fDrawTDivs;
  139. #ifdef SK_DEBUG
  140. #define kStrokerErrorMin 0.001f
  141. #define kStrokerErrorMax 5
  142. #endif
  143. #define kWidthMin 1
  144. #define kWidthMax 100
  145. public:
  146. QuadStrokerView() {
  147. this->setBGColor(SK_ColorLTGRAY);
  148. fPts[0].set(50, 200); // cubic
  149. fPts[1].set(50, 100);
  150. fPts[2].set(150, 50);
  151. fPts[3].set(300, 50);
  152. fPts[4].set(350, 200); // conic
  153. fPts[5].set(350, 100);
  154. fPts[6].set(450, 50);
  155. fPts[7].set(150, 300); // quad
  156. fPts[8].set(150, 200);
  157. fPts[9].set(250, 150);
  158. fPts[10].set(250, 200); // arc
  159. fPts[11].set(250, 300);
  160. fPts[12].set(150, 350);
  161. fPts[13].set(200, 200); // rrect
  162. fPts[14].set(400, 400);
  163. fPts[15].set(250, 250); // oval
  164. fPts[16].set(450, 450);
  165. fText = "a";
  166. fTextSize = 12;
  167. fWidth = 50;
  168. fDWidth = 0.25f;
  169. fWeight = 1;
  170. fRadius = 150;
  171. fCubicButton.fLabel = 'C';
  172. fCubicButton.fEnabled = false;
  173. fConicButton.fLabel = 'K';
  174. fConicButton.fEnabled = false;
  175. fQuadButton.fLabel = 'Q';
  176. fQuadButton.fEnabled = false;
  177. fArcButton.fLabel = 'A';
  178. fArcButton.fEnabled = true;
  179. fRRectButton.fLabel = 'R';
  180. fRRectButton.fEnabled = false;
  181. fCircleButton.fLabel = 'O';
  182. fCircleButton.fEnabled = true;
  183. fCircleButton.fFill = true;
  184. fTextButton.fLabel = 'T';
  185. fTextButton.fEnabled = false;
  186. fAnimate = false;
  187. setAsNeeded();
  188. }
  189. protected:
  190. SkString name() override { return SkString("QuadStroker"); }
  191. bool onChar(SkUnichar uni) override {
  192. if (fTextButton.fEnabled) {
  193. switch (uni) {
  194. case ' ':
  195. fText = "";
  196. break;
  197. case '-':
  198. fTextSize = SkTMax(1.0f, fTextSize - 1);
  199. break;
  200. case '+':
  201. case '=':
  202. fTextSize += 1;
  203. break;
  204. default:
  205. fText.appendUnichar(uni);
  206. }
  207. return true;
  208. }
  209. return false;
  210. }
  211. void onSizeChange() override {
  212. fRadiusControl.setXYWH(this->width() - 200, 30, 30, 400);
  213. fWeightControl.setXYWH(this->width() - 150, 30, 30, 400);
  214. fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
  215. fWidthControl.setXYWH(this->width() - 50, 30, 30, 400);
  216. int buttonOffset = 450;
  217. fCubicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  218. buttonOffset += 50;
  219. fConicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  220. buttonOffset += 50;
  221. fQuadButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  222. buttonOffset += 50;
  223. fArcButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  224. buttonOffset += 50;
  225. fRRectButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  226. buttonOffset += 50;
  227. fCircleButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  228. buttonOffset += 50;
  229. fTextButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
  230. this->INHERITED::onSizeChange();
  231. }
  232. void copyMinToMax() {
  233. erase(fMaxSurface);
  234. SkCanvas* canvas = fMaxSurface->getCanvas();
  235. canvas->save();
  236. canvas->concat(fMatrix);
  237. fMinSurface->draw(canvas, 0, 0, nullptr);
  238. canvas->restore();
  239. SkPaint paint;
  240. paint.setBlendMode(SkBlendMode::kClear);
  241. for (int iy = 1; iy < fH; ++iy) {
  242. SkScalar y = SkIntToScalar(iy * fZoom);
  243. canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
  244. }
  245. for (int ix = 1; ix < fW; ++ix) {
  246. SkScalar x = SkIntToScalar(ix * fZoom);
  247. canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
  248. }
  249. }
  250. void setWHZ(int width, int height, int zoom) {
  251. fZoom = zoom;
  252. fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
  253. fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
  254. fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
  255. fShader = ToolUtils::create_checkerboard_shader(0xFFCCCCCC, 0xFFFFFFFF, zoom);
  256. SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
  257. fMinSurface = SkSurface::MakeRaster(info);
  258. info = info.makeWH(width * zoom, height * zoom);
  259. fMaxSurface = SkSurface::MakeRaster(info);
  260. }
  261. void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
  262. bool show_lines) {
  263. SkPaint paint;
  264. paint.setColor(color);
  265. paint.setAlpha(0x80);
  266. paint.setAntiAlias(true);
  267. int n = path.countPoints();
  268. SkAutoSTArray<32, SkPoint> pts(n);
  269. if (show_lines && fDrawTangents) {
  270. SkTArray<int> contourCounts;
  271. getContourCounts(path, &contourCounts);
  272. SkPoint* ptPtr = pts.get();
  273. for (int i = 0; i < contourCounts.count(); ++i) {
  274. int count = contourCounts[i];
  275. path.getPoints(ptPtr, count);
  276. canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, paint);
  277. ptPtr += count;
  278. }
  279. } else {
  280. n = getOnCurvePoints(path, pts.get());
  281. }
  282. paint.setStrokeWidth(5);
  283. canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
  284. }
  285. void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
  286. SkColor color) {
  287. const SkScalar radius = width / 2;
  288. SkPathMeasure meas(path, false);
  289. SkScalar total = meas.getLength();
  290. SkScalar delta = 8;
  291. SkPaint paint, labelP;
  292. paint.setColor(color);
  293. labelP.setColor(color & 0xff5f9f5f);
  294. SkFont font;
  295. SkPoint pos, tan;
  296. int index = 0;
  297. for (SkScalar dist = 0; dist <= total; dist += delta) {
  298. if (meas.getPosTan(dist, &pos, &tan)) {
  299. tan.scale(radius);
  300. SkPointPriv::RotateCCW(&tan);
  301. canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
  302. pos.x() - tan.x(), pos.y() - tan.y(), paint);
  303. if (0 == index % 10) {
  304. SkString label;
  305. label.appendS32(index);
  306. SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4);
  307. canvas->drawRect(dot, labelP);
  308. canvas->drawString(label,
  309. pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, font, labelP);
  310. }
  311. }
  312. ++index;
  313. }
  314. }
  315. void draw_t_divs(SkCanvas* canvas, const SkPath& path, SkScalar width, SkColor color) {
  316. const SkScalar radius = width / 2;
  317. SkPaint paint;
  318. paint.setColor(color);
  319. SkPathMeasure meas(path, false);
  320. SkScalar total = meas.getLength();
  321. SkScalar delta = 8;
  322. int ribs = 0;
  323. for (SkScalar dist = 0; dist <= total; dist += delta) {
  324. ++ribs;
  325. }
  326. SkPath::RawIter iter(path);
  327. SkPoint pts[4];
  328. if (SkPath::kMove_Verb != iter.next(pts)) {
  329. SkASSERT(0);
  330. return;
  331. }
  332. SkPath::Verb verb = iter.next(pts);
  333. SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
  334. SkPoint pos, tan;
  335. for (int index = 0; index < ribs; ++index) {
  336. SkScalar t = (SkScalar) index / ribs;
  337. switch (verb) {
  338. case SkPath::kLine_Verb:
  339. tan = pts[1] - pts[0];
  340. pos = pts[0];
  341. pos.fX += tan.fX * t;
  342. pos.fY += tan.fY * t;
  343. break;
  344. case SkPath::kQuad_Verb:
  345. pos = SkEvalQuadAt(pts, t);
  346. tan = SkEvalQuadTangentAt(pts, t);
  347. break;
  348. case SkPath::kConic_Verb: {
  349. SkConic conic(pts, iter.conicWeight());
  350. pos = conic.evalAt(t);
  351. tan = conic.evalTangentAt(t);
  352. } break;
  353. case SkPath::kCubic_Verb:
  354. SkEvalCubicAt(pts, t, &pos, &tan, nullptr);
  355. break;
  356. default:
  357. SkASSERT(0);
  358. return;
  359. }
  360. tan.setLength(radius);
  361. SkPointPriv::RotateCCW(&tan);
  362. canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
  363. pos.x() - tan.x(), pos.y() - tan.y(), paint);
  364. if (0 == index % 10) {
  365. SkString label;
  366. label.appendS32(index);
  367. canvas->drawString(label,
  368. pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, SkFont(), paint);
  369. }
  370. }
  371. }
  372. void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale,
  373. bool drawText) {
  374. if (path.isEmpty()) {
  375. return;
  376. }
  377. SkRect bounds = path.getBounds();
  378. this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText
  379. ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale),
  380. SkScalarRoundToInt(950.0f / scale));
  381. erase(fMinSurface);
  382. SkPaint paint;
  383. paint.setColor(0x1f1f0f0f);
  384. paint.setStyle(SkPaint::kStroke_Style);
  385. paint.setStrokeWidth(width * scale * scale);
  386. paint.setColor(0x3f0f1f3f);
  387. if (drawText) {
  388. fMinSurface->getCanvas()->drawPath(path, paint);
  389. this->copyMinToMax();
  390. fMaxSurface->draw(canvas, 0, 0, nullptr);
  391. }
  392. paint.setAntiAlias(true);
  393. paint.setStyle(SkPaint::kStroke_Style);
  394. paint.setStrokeWidth(1);
  395. paint.setColor(SKELETON_COLOR);
  396. SkPath scaled;
  397. SkMatrix matrix;
  398. matrix.reset();
  399. matrix.setScale(950 / scale, 950 / scale);
  400. if (drawText) {
  401. path.transform(matrix, &scaled);
  402. } else {
  403. scaled = path;
  404. }
  405. canvas->drawPath(scaled, paint);
  406. draw_points(canvas, scaled, SKELETON_COLOR, true);
  407. if (fDrawRibs) {
  408. draw_ribs(canvas, scaled, width, 0xFF00FF00);
  409. }
  410. if (fDrawTDivs) {
  411. draw_t_divs(canvas, scaled, width, 0xFF3F3F00);
  412. }
  413. SkPath fill;
  414. SkPaint p;
  415. p.setStyle(SkPaint::kStroke_Style);
  416. if (drawText) {
  417. p.setStrokeWidth(width * scale * scale);
  418. } else {
  419. p.setStrokeWidth(width);
  420. }
  421. p.getFillPath(path, &fill);
  422. SkPath scaledFill;
  423. if (drawText) {
  424. fill.transform(matrix, &scaledFill);
  425. } else {
  426. scaledFill = fill;
  427. }
  428. paint.setColor(WIREFRAME_COLOR);
  429. canvas->drawPath(scaledFill, paint);
  430. draw_points(canvas, scaledFill, WIREFRAME_COLOR, false);
  431. }
  432. void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) {
  433. if (rect.isEmpty()) {
  434. return;
  435. }
  436. SkPaint paint;
  437. paint.setColor(0x1f1f0f0f);
  438. paint.setStyle(SkPaint::kStroke_Style);
  439. paint.setStrokeWidth(width);
  440. SkPath path;
  441. SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2;
  442. SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide };
  443. path.addCircle(center.fX, center.fY, maxSide);
  444. canvas->drawPath(path, paint);
  445. paint.setStyle(SkPaint::kFill_Style);
  446. path.reset();
  447. path.addCircle(center.fX, center.fY, maxSide - width / 2);
  448. paint.setColor(0x3f0f1f3f);
  449. canvas->drawPath(path, paint);
  450. path.reset();
  451. path.setFillType(SkPath::kEvenOdd_FillType);
  452. path.addCircle(center.fX, center.fY, maxSide + width / 2);
  453. SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width,
  454. (maxSide + width) * 2, (maxSide + width) * 2);
  455. path.addRect(outside);
  456. canvas->drawPath(path, paint);
  457. }
  458. void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) {
  459. SkPaint paint;
  460. paint.setAntiAlias(true);
  461. paint.setStyle(SkPaint::kStroke_Style);
  462. paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
  463. canvas->drawRect(button.fBounds, paint);
  464. paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
  465. paint.setStyle(SkPaint::kFill_Style);
  466. SkFont font;
  467. font.setSize(25.0f);
  468. SkTextUtils::Draw(canvas, &button.fLabel, 1, SkTextEncoding::kUTF8,
  469. button.fBounds.centerX(), button.fBounds.fBottom - 5,
  470. font, paint, SkTextUtils::kCenter_Align);
  471. }
  472. void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value,
  473. SkScalar min, SkScalar max, const char* name) {
  474. SkPaint paint;
  475. paint.setAntiAlias(true);
  476. paint.setStyle(SkPaint::kStroke_Style);
  477. canvas->drawRect(bounds, paint);
  478. SkScalar scale = max - min;
  479. SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale;
  480. paint.setColor(0xFFFF0000);
  481. canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint);
  482. SkString label;
  483. label.printf("%0.3g", value);
  484. paint.setColor(0xFF000000);
  485. paint.setStyle(SkPaint::kFill_Style);
  486. SkFont font(nullptr, 11.0f);
  487. canvas->drawString(label, bounds.fLeft + 5, yPos - 5, font, paint);
  488. font.setSize(13.0f);
  489. canvas->drawString(name, bounds.fLeft, bounds.bottom() + 11, font, paint);
  490. }
  491. void setForGeometry() {
  492. fDrawRibs = true;
  493. fDrawTangents = true;
  494. fDrawTDivs = false;
  495. fWidthScale = 1;
  496. }
  497. void setForText() {
  498. fDrawRibs = fDrawTangents = fDrawTDivs = false;
  499. fWidthScale = 0.002f;
  500. }
  501. void setForSingles() {
  502. setForGeometry();
  503. fDrawTDivs = true;
  504. }
  505. void setAsNeeded() {
  506. if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) {
  507. setForSingles();
  508. } else if (fRRectButton.fEnabled || fCircleButton.fEnabled || fArcButton.fEnabled) {
  509. setForGeometry();
  510. } else {
  511. setForText();
  512. }
  513. }
  514. bool arcCenter(SkPoint* center) {
  515. SkPath path;
  516. path.moveTo(fPts[10]);
  517. path.arcTo(fPts[11], fPts[12], fRadius);
  518. SkPath::Iter iter(path, false);
  519. SkPoint pts[4];
  520. iter.next(pts);
  521. if (SkPath::kLine_Verb == iter.next(pts)) {
  522. iter.next(pts);
  523. }
  524. SkVector before = pts[0] - pts[1];
  525. SkVector after = pts[1] - pts[2];
  526. before.setLength(fRadius);
  527. after.setLength(fRadius);
  528. SkVector beforeCCW, afterCCW;
  529. SkPointPriv::RotateCCW(before, &beforeCCW);
  530. SkPointPriv::RotateCCW(after, &afterCCW);
  531. beforeCCW += pts[0];
  532. afterCCW += pts[2];
  533. *center = beforeCCW;
  534. if (SkScalarNearlyEqual(beforeCCW.fX, afterCCW.fX)
  535. && SkScalarNearlyEqual(beforeCCW.fY, afterCCW.fY)) {
  536. return true;
  537. }
  538. SkVector beforeCW, afterCW;
  539. SkPointPriv::RotateCW(before, &beforeCW);
  540. SkPointPriv::RotateCW(after, &afterCW);
  541. beforeCW += pts[0];
  542. afterCW += pts[2];
  543. *center = beforeCW;
  544. return SkScalarNearlyEqual(beforeCW.fX, afterCW.fX)
  545. && SkScalarNearlyEqual(beforeCCW.fY, afterCW.fY);
  546. }
  547. void onDrawContent(SkCanvas* canvas) override {
  548. SkPath path;
  549. SkScalar width = fWidth;
  550. if (fCubicButton.fEnabled) {
  551. path.moveTo(fPts[0]);
  552. path.cubicTo(fPts[1], fPts[2], fPts[3]);
  553. setForSingles();
  554. draw_stroke(canvas, path, width, 950, false);
  555. }
  556. if (fConicButton.fEnabled) {
  557. path.reset();
  558. path.moveTo(fPts[4]);
  559. path.conicTo(fPts[5], fPts[6], fWeight);
  560. setForSingles();
  561. draw_stroke(canvas, path, width, 950, false);
  562. }
  563. if (fQuadButton.fEnabled) {
  564. path.reset();
  565. path.moveTo(fPts[7]);
  566. path.quadTo(fPts[8], fPts[9]);
  567. setForSingles();
  568. draw_stroke(canvas, path, width, 950, false);
  569. }
  570. if (fArcButton.fEnabled) {
  571. path.reset();
  572. path.moveTo(fPts[10]);
  573. path.arcTo(fPts[11], fPts[12], fRadius);
  574. setForGeometry();
  575. draw_stroke(canvas, path, width, 950, false);
  576. SkPath pathPts;
  577. pathPts.moveTo(fPts[10]);
  578. pathPts.lineTo(fPts[11]);
  579. pathPts.lineTo(fPts[12]);
  580. draw_points(canvas, pathPts, SK_ColorDKGRAY, true);
  581. }
  582. if (fRRectButton.fEnabled) {
  583. SkScalar rad = 32;
  584. SkRect r;
  585. r.set(&fPts[13], 2);
  586. path.reset();
  587. SkRRect rr;
  588. rr.setRectXY(r, rad, rad);
  589. path.addRRect(rr);
  590. setForGeometry();
  591. draw_stroke(canvas, path, width, 950, false);
  592. path.reset();
  593. SkRRect rr2;
  594. rr.inset(width/2, width/2, &rr2);
  595. path.addRRect(rr2, SkPath::kCCW_Direction);
  596. rr.inset(-width/2, -width/2, &rr2);
  597. path.addRRect(rr2, SkPath::kCW_Direction);
  598. SkPaint paint;
  599. paint.setAntiAlias(true);
  600. paint.setColor(0x40FF8844);
  601. canvas->drawPath(path, paint);
  602. }
  603. if (fCircleButton.fEnabled) {
  604. path.reset();
  605. SkRect r;
  606. r.set(&fPts[15], 2);
  607. path.addOval(r);
  608. setForGeometry();
  609. if (fCircleButton.fFill) {
  610. if (fArcButton.fEnabled) {
  611. SkPoint center;
  612. if (arcCenter(&center)) {
  613. r.set(center.fX - fRadius, center.fY - fRadius, center.fX + fRadius,
  614. center.fY + fRadius);
  615. }
  616. }
  617. draw_fill(canvas, r, width);
  618. } else {
  619. draw_stroke(canvas, path, width, 950, false);
  620. }
  621. }
  622. if (fTextButton.fEnabled) {
  623. path.reset();
  624. SkFont font;
  625. font.setSize(fTextSize);
  626. SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8,
  627. 0, fTextSize, font, &path);
  628. setForText();
  629. draw_stroke(canvas, path, width * fWidthScale / fTextSize, fTextSize, true);
  630. }
  631. if (fAnimate) {
  632. fWidth += fDWidth;
  633. if (fDWidth > 0 && fWidth > kWidthMax) {
  634. fDWidth = -fDWidth;
  635. } else if (fDWidth < 0 && fWidth < kWidthMin) {
  636. fDWidth = -fDWidth;
  637. }
  638. }
  639. setAsNeeded();
  640. if (fConicButton.fEnabled) {
  641. draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight");
  642. }
  643. if (fArcButton.fEnabled) {
  644. draw_control(canvas, fRadiusControl, fRadius, 0, 500, "radius");
  645. }
  646. #ifdef SK_DEBUG
  647. draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax,
  648. "error");
  649. #endif
  650. draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fWidthScale,
  651. kWidthMax * fWidthScale, "width");
  652. draw_button(canvas, fQuadButton);
  653. draw_button(canvas, fCubicButton);
  654. draw_button(canvas, fConicButton);
  655. draw_button(canvas, fArcButton);
  656. draw_button(canvas, fRRectButton);
  657. draw_button(canvas, fCircleButton);
  658. draw_button(canvas, fTextButton);
  659. }
  660. class MyClick : public Click {
  661. public:
  662. int fIndex;
  663. MyClick(int index) : fIndex(index) {}
  664. };
  665. virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y,
  666. ModifierKey modi) override {
  667. for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
  668. if (hittest(fPts[i], x, y)) {
  669. return new MyClick((int)i);
  670. }
  671. }
  672. const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
  673. if (fWeightControl.contains(rectPt)) {
  674. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 1);
  675. }
  676. if (fRadiusControl.contains(rectPt)) {
  677. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 2);
  678. }
  679. #ifdef SK_DEBUG
  680. if (fErrorControl.contains(rectPt)) {
  681. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 3);
  682. }
  683. #endif
  684. if (fWidthControl.contains(rectPt)) {
  685. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 4);
  686. }
  687. if (fCubicButton.fBounds.contains(rectPt)) {
  688. fCubicButton.fEnabled ^= true;
  689. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 5);
  690. }
  691. if (fConicButton.fBounds.contains(rectPt)) {
  692. fConicButton.fEnabled ^= true;
  693. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 6);
  694. }
  695. if (fQuadButton.fBounds.contains(rectPt)) {
  696. fQuadButton.fEnabled ^= true;
  697. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 7);
  698. }
  699. if (fArcButton.fBounds.contains(rectPt)) {
  700. fArcButton.fEnabled ^= true;
  701. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 8);
  702. }
  703. if (fRRectButton.fBounds.contains(rectPt)) {
  704. fRRectButton.fEnabled ^= true;
  705. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 9);
  706. }
  707. if (fCircleButton.fBounds.contains(rectPt)) {
  708. bool wasEnabled = fCircleButton.fEnabled;
  709. fCircleButton.fEnabled = !fCircleButton.fFill;
  710. fCircleButton.fFill = wasEnabled && !fCircleButton.fFill;
  711. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 10);
  712. }
  713. if (fTextButton.fBounds.contains(rectPt)) {
  714. fTextButton.fEnabled ^= true;
  715. return new MyClick((int) SK_ARRAY_COUNT(fPts) + 11);
  716. }
  717. return nullptr;
  718. }
  719. static SkScalar MapScreenYtoValue(SkScalar y, const SkRect& control, SkScalar min,
  720. SkScalar max) {
  721. return (y - control.fTop) / control.height() * (max - min) + min;
  722. }
  723. bool onClick(Click* click) override {
  724. int index = ((MyClick*)click)->fIndex;
  725. if (index < (int) SK_ARRAY_COUNT(fPts)) {
  726. fPts[index].offset(click->fCurr.fX - click->fPrev.fX,
  727. click->fCurr.fY - click->fPrev.fY);
  728. } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) {
  729. fWeight = MapScreenYtoValue(click->fCurr.fY, fWeightControl, 0, 5);
  730. } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) {
  731. fRadius = MapScreenYtoValue(click->fCurr.fY, fRadiusControl, 0, 500);
  732. }
  733. #ifdef SK_DEBUG
  734. else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
  735. gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY,
  736. fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
  737. gDebugStrokerErrorSet = true;
  738. }
  739. #endif
  740. else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) {
  741. fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY, fWidthControl,
  742. kWidthMin, kWidthMax));
  743. fAnimate = fWidth <= kWidthMin;
  744. }
  745. return true;
  746. }
  747. private:
  748. typedef Sample INHERITED;
  749. };
  750. ///////////////////////////////////////////////////////////////////////////////
  751. DEF_SAMPLE( return new QuadStrokerView(); )