polygonoffset.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. * Copyright 2018 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 "gm/gm.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkColor.h"
  10. #include "include/core/SkPaint.h"
  11. #include "include/core/SkPath.h"
  12. #include "include/core/SkPoint.h"
  13. #include "include/core/SkRect.h"
  14. #include "include/core/SkScalar.h"
  15. #include "include/core/SkSize.h"
  16. #include "include/core/SkString.h"
  17. #include "include/core/SkTypes.h"
  18. #include "include/private/SkTDArray.h"
  19. #include "src/utils/SkPolyUtils.h"
  20. #include "tools/ToolUtils.h"
  21. #include <functional>
  22. #include <memory>
  23. static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPath::Direction dir) {
  24. float angleStep = 360.0f / n, angle = 0.0f;
  25. if ((n % 2) == 1) {
  26. angle = angleStep/2.0f;
  27. }
  28. if (SkPath::kCCW_Direction == dir) {
  29. angle = -angle;
  30. angleStep = -angleStep;
  31. }
  32. for (int i = 0; i < n; ++i) {
  33. pts[i].fX = -SkScalarSin(SkDegreesToRadians(angle)) * w;
  34. pts[i].fY = SkScalarCos(SkDegreesToRadians(angle)) * h;
  35. angle += angleStep;
  36. }
  37. }
  38. namespace PolygonOffsetData {
  39. // narrow rect
  40. const SkPoint gPoints0[] = {
  41. { -1.5f, -50.0f },
  42. { 1.5f, -50.0f },
  43. { 1.5f, 50.0f },
  44. { -1.5f, 50.0f }
  45. };
  46. // narrow rect on an angle
  47. const SkPoint gPoints1[] = {
  48. { -50.0f, -49.0f },
  49. { -49.0f, -50.0f },
  50. { 50.0f, 49.0f },
  51. { 49.0f, 50.0f }
  52. };
  53. // trap - narrow on top - wide on bottom
  54. const SkPoint gPoints2[] = {
  55. { -10.0f, -50.0f },
  56. { 10.0f, -50.0f },
  57. { 50.0f, 50.0f },
  58. { -50.0f, 50.0f }
  59. };
  60. // wide skewed rect
  61. const SkPoint gPoints3[] = {
  62. { -50.0f, -50.0f },
  63. { 0.0f, -50.0f },
  64. { 50.0f, 50.0f },
  65. { 0.0f, 50.0f }
  66. };
  67. // thin rect with colinear-ish lines
  68. const SkPoint gPoints4[] = {
  69. { -6.0f, -50.0f },
  70. { 4.0f, -50.0f },
  71. { 5.0f, -25.0f },
  72. { 6.0f, 0.0f },
  73. { 5.0f, 25.0f },
  74. { 4.0f, 50.0f },
  75. { -4.0f, 50.0f }
  76. };
  77. // degenerate
  78. const SkPoint gPoints5[] = {
  79. { -0.025f, -0.025f },
  80. { 0.025f, -0.025f },
  81. { 0.025f, 0.025f },
  82. { -0.025f, 0.025f }
  83. };
  84. // Quad with near coincident point
  85. const SkPoint gPoints6[] = {
  86. { -20.0f, -13.0f },
  87. { -20.0f, -13.05f },
  88. { 20.0f, -13.0f },
  89. { 20.0f, 27.0f }
  90. };
  91. // thin rect with colinear lines
  92. const SkPoint gPoints7[] = {
  93. { -10.0f, -50.0f },
  94. { 10.0f, -50.0f },
  95. { 10.0f, -20.0f },
  96. { 10.0f, 0.0f },
  97. { 10.0f, 35.0f },
  98. { 10.0f, 50.0f },
  99. { -10.0f, 50.0f }
  100. };
  101. // capped teardrop
  102. const SkPoint gPoints8[] = {
  103. { 50.00f, 50.00f },
  104. { 0.00f, 50.00f },
  105. { -15.45f, 47.55f },
  106. { -29.39f, 40.45f },
  107. { -40.45f, 29.39f },
  108. { -47.55f, 15.45f },
  109. { -50.00f, 0.00f },
  110. { -47.55f, -15.45f },
  111. { -40.45f, -29.39f },
  112. { -29.39f, -40.45f },
  113. { -15.45f, -47.55f },
  114. { 0.00f, -50.00f },
  115. { 50.00f, -50.00f }
  116. };
  117. // teardrop
  118. const SkPoint gPoints9[] = {
  119. { 4.39f, 40.45f },
  120. { -9.55f, 47.55f },
  121. { -25.00f, 50.00f },
  122. { -40.45f, 47.55f },
  123. { -54.39f, 40.45f },
  124. { -65.45f, 29.39f },
  125. { -72.55f, 15.45f },
  126. { -75.00f, 0.00f },
  127. { -72.55f, -15.45f },
  128. { -65.45f, -29.39f },
  129. { -54.39f, -40.45f },
  130. { -40.45f, -47.55f },
  131. { -25.0f, -50.0f },
  132. { -9.55f, -47.55f },
  133. { 4.39f, -40.45f },
  134. { 75.00f, 0.00f }
  135. };
  136. // clipped triangle
  137. const SkPoint gPoints10[] = {
  138. { -10.0f, -50.0f },
  139. { 10.0f, -50.0f },
  140. { 50.0f, 31.0f },
  141. { 40.0f, 50.0f },
  142. { -40.0f, 50.0f },
  143. { -50.0f, 31.0f },
  144. };
  145. // tab
  146. const SkPoint gPoints11[] = {
  147. { -45, -25 },
  148. { 45, -25 },
  149. { 45, 25 },
  150. { 20, 25 },
  151. { 19.6157f, 25.f + 3.9018f },
  152. { 18.4776f, 25.f + 7.6537f },
  153. { 16.6294f, 25.f + 11.1114f },
  154. { 14.1421f, 25.f + 14.1421f },
  155. { 11.1114f, 25.f + 16.6294f },
  156. { 7.6537f, 25.f + 18.4776f },
  157. { 3.9018f, 25.f + 19.6157f },
  158. { 0, 45.f },
  159. { -3.9018f, 25.f + 19.6157f },
  160. { -7.6537f, 25.f + 18.4776f },
  161. { -11.1114f, 25.f + 16.6294f },
  162. { -14.1421f, 25.f + 14.1421f },
  163. { -16.6294f, 25.f + 11.1114f },
  164. { -18.4776f, 25.f + 7.6537f },
  165. { -19.6157f, 25.f + 3.9018f },
  166. { -20, 25 },
  167. { -45, 25 }
  168. };
  169. // star of david
  170. const SkPoint gPoints12[] = {
  171. { 0.0f, -50.0f },
  172. { 14.43f, -25.0f },
  173. { 43.30f, -25.0f },
  174. { 28.86f, 0.0f },
  175. { 43.30f, 25.0f },
  176. { 14.43f, 25.0f },
  177. { 0.0f, 50.0f },
  178. { -14.43f, 25.0f },
  179. { -43.30f, 25.0f },
  180. { -28.86f, 0.0f },
  181. { -43.30f, -25.0f },
  182. { -14.43f, -25.0f },
  183. };
  184. // notch
  185. const SkScalar kBottom = 25.f;
  186. const SkPoint gPoints13[] = {
  187. { -50, kBottom - 50.f },
  188. { 50, kBottom - 50.f },
  189. { 50, kBottom },
  190. { 20, kBottom },
  191. { 19.6157f, kBottom - 3.9018f },
  192. { 18.4776f, kBottom - 7.6537f },
  193. { 16.6294f, kBottom - 11.1114f },
  194. { 14.1421f, kBottom - 14.1421f },
  195. { 11.1114f, kBottom - 16.6294f },
  196. { 7.6537f, kBottom - 18.4776f },
  197. { 3.9018f, kBottom - 19.6157f },
  198. { 0, kBottom - 20.f },
  199. { -3.9018f, kBottom - 19.6157f },
  200. { -7.6537f, kBottom - 18.4776f },
  201. { -11.1114f, kBottom - 16.6294f },
  202. { -14.1421f, kBottom - 14.1421f },
  203. { -16.6294f, kBottom - 11.1114f },
  204. { -18.4776f, kBottom - 7.6537f },
  205. { -19.6157f, kBottom - 3.9018f },
  206. { -20, kBottom },
  207. { -50, kBottom }
  208. };
  209. // crown
  210. const SkPoint gPoints14[] = {
  211. { -40, -39 },
  212. { 40, -39 },
  213. { 40, -20 },
  214. { 30, 40 },
  215. { 20, -20 },
  216. { 10, 40 },
  217. { 0, -20 },
  218. { -10, 40 },
  219. { -20, -20 },
  220. { -30, 40 },
  221. { -40, -20 }
  222. };
  223. // dumbbell
  224. const SkPoint gPoints15[] = {
  225. { -26, -3 },
  226. { -24, -6.2f },
  227. { -22.5f, -8 },
  228. { -20, -9.9f },
  229. { -17.5f, -10.3f },
  230. { -15, -10.9f },
  231. { -12.5f, -10.2f },
  232. { -10, -9.7f },
  233. { -7.5f, -8.1f },
  234. { -5, -7.7f },
  235. { -2.5f, -7.4f },
  236. { 0, -7.7f },
  237. { 3, -9 },
  238. { 6.5f, -11.5f },
  239. { 10.6f, -14 },
  240. { 14, -15.2f },
  241. { 17, -15.5f },
  242. { 20, -15.2f },
  243. { 23.4f, -14 },
  244. { 27.5f, -11.5f },
  245. { 30, -8 },
  246. { 32, -4 },
  247. { 32.5f, 0 },
  248. { 32, 4 },
  249. { 30, 8 },
  250. { 27.5f, 11.5f },
  251. { 23.4f, 14 },
  252. { 20, 15.2f },
  253. { 17, 15.5f },
  254. { 14, 15.2f },
  255. { 10.6f, 14 },
  256. { 6.5f, 11.5f },
  257. { 3, 9 },
  258. { 0, 7.7f },
  259. { -2.5f, 7.4f },
  260. { -5, 7.7f },
  261. { -7.5f, 8.1f },
  262. { -10, 9.7f },
  263. { -12.5f, 10.2f },
  264. { -15, 10.9f },
  265. { -17.5f, 10.3f },
  266. { -20, 9.9f },
  267. { -22.5f, 8 },
  268. { -24, 6.2f },
  269. { -26, 3 },
  270. { -26.5f, 0 }
  271. };
  272. // truncated dumbbell
  273. // (checks winding computation in OffsetSimplePolygon)
  274. const SkPoint gPoints16[] = {
  275. { -15 + 3, -9 },
  276. { -15 + 6.5f, -11.5f },
  277. { -15 + 10.6f, -14 },
  278. { -15 + 14, -15.2f },
  279. { -15 + 17, -15.5f },
  280. { -15 + 20, -15.2f },
  281. { -15 + 23.4f, -14 },
  282. { -15 + 27.5f, -11.5f },
  283. { -15 + 30, -8 },
  284. { -15 + 32, -4 },
  285. { -15 + 32.5f, 0 },
  286. { -15 + 32, 4 },
  287. { -15 + 30, 8 },
  288. { -15 + 27.5f, 11.5f },
  289. { -15 + 23.4f, 14 },
  290. { -15 + 20, 15.2f },
  291. { -15 + 17, 15.5f },
  292. { -15 + 14, 15.2f },
  293. { -15 + 10.6f, 14 },
  294. { -15 + 6.5f, 11.5f },
  295. { -15 + 3, 9 },
  296. };
  297. // square notch
  298. // (to detect segment-segment intersection)
  299. const SkPoint gPoints17[] = {
  300. { -50, kBottom - 50.f },
  301. { 50, kBottom - 50.f },
  302. { 50, kBottom },
  303. { 20, kBottom },
  304. { 20, kBottom - 20.f },
  305. { -20, kBottom - 20.f },
  306. { -20, kBottom },
  307. { -50, kBottom }
  308. };
  309. // box with Peano curve
  310. const SkPoint gPoints18[] = {
  311. { 0, 0 },
  312. { 0, -12 },
  313. { -6, -12 },
  314. { -6, 0 },
  315. { -12, 0 },
  316. { -12, -12},
  317. { -18, -12},
  318. { -18, 18},
  319. { -12, 18},
  320. {-12, 6},
  321. {-6, 6},
  322. {-6, 36},
  323. {-12, 36},
  324. {-12, 24},
  325. {-18, 24},
  326. {-18, 36},
  327. {-24, 36},
  328. {-24, 24},
  329. {-30, 24},
  330. {-30, 36},
  331. {-36, 36},
  332. {-36, 6},
  333. {-30, 6},
  334. {-30, 18},
  335. {-24, 18},
  336. {-24, -12},
  337. {-30, -12},
  338. {-30, 0},
  339. {-36, 0},
  340. {-36, -36},
  341. {36, -36},
  342. {36, 36},
  343. {12, 36},
  344. {12, 24},
  345. {6, 24},
  346. {6, 36},
  347. {0, 36},
  348. {0, 6},
  349. {6, 6},
  350. {6, 18},
  351. {12, 18},
  352. {12, -12},
  353. {6, -12},
  354. {6, 0}
  355. };
  356. const SkPoint* gConvexPoints[] = {
  357. gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
  358. gPoints7, gPoints8, gPoints9, gPoints10,
  359. };
  360. const size_t gConvexSizes[] = {
  361. SK_ARRAY_COUNT(gPoints0),
  362. SK_ARRAY_COUNT(gPoints1),
  363. SK_ARRAY_COUNT(gPoints2),
  364. SK_ARRAY_COUNT(gPoints3),
  365. SK_ARRAY_COUNT(gPoints4),
  366. SK_ARRAY_COUNT(gPoints5),
  367. SK_ARRAY_COUNT(gPoints6),
  368. SK_ARRAY_COUNT(gPoints7),
  369. SK_ARRAY_COUNT(gPoints8),
  370. SK_ARRAY_COUNT(gPoints9),
  371. SK_ARRAY_COUNT(gPoints10),
  372. };
  373. static_assert(SK_ARRAY_COUNT(gConvexSizes) == SK_ARRAY_COUNT(gConvexPoints), "array_mismatch");
  374. const SkPoint* gSimplePoints[] = {
  375. gPoints0, gPoints1, gPoints2, gPoints4, gPoints5, gPoints7,
  376. gPoints8, gPoints11, gPoints12, gPoints13, gPoints14, gPoints15,
  377. gPoints16, gPoints17, gPoints18,
  378. };
  379. const size_t gSimpleSizes[] = {
  380. SK_ARRAY_COUNT(gPoints0),
  381. SK_ARRAY_COUNT(gPoints1),
  382. SK_ARRAY_COUNT(gPoints2),
  383. SK_ARRAY_COUNT(gPoints4),
  384. SK_ARRAY_COUNT(gPoints5),
  385. SK_ARRAY_COUNT(gPoints7),
  386. SK_ARRAY_COUNT(gPoints8),
  387. SK_ARRAY_COUNT(gPoints11),
  388. SK_ARRAY_COUNT(gPoints12),
  389. SK_ARRAY_COUNT(gPoints13),
  390. SK_ARRAY_COUNT(gPoints14),
  391. SK_ARRAY_COUNT(gPoints15),
  392. SK_ARRAY_COUNT(gPoints16),
  393. SK_ARRAY_COUNT(gPoints17),
  394. SK_ARRAY_COUNT(gPoints18),
  395. };
  396. static_assert(SK_ARRAY_COUNT(gSimpleSizes) == SK_ARRAY_COUNT(gSimplePoints), "array_mismatch");
  397. }
  398. namespace skiagm {
  399. // This GM is intended to exercise the offsetting of polygons
  400. // When fVariableOffset is true it will skew the offset by x,
  401. // to test perspective and other variable offset functions
  402. class PolygonOffsetGM : public GM {
  403. public:
  404. PolygonOffsetGM(bool convexOnly)
  405. : fConvexOnly(convexOnly) {
  406. this->setBGColor(0xFFFFFFFF);
  407. }
  408. protected:
  409. SkString onShortName() override {
  410. if (fConvexOnly) {
  411. return SkString("convex-polygon-inset");
  412. } else {
  413. return SkString("simple-polygon-offset");
  414. }
  415. }
  416. SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
  417. bool runAsBench() const override { return true; }
  418. static void GetConvexPolygon(int index, SkPath::Direction dir,
  419. std::unique_ptr<SkPoint[]>* data, int* numPts) {
  420. if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints)) {
  421. // manually specified
  422. *numPts = (int)PolygonOffsetData::gConvexSizes[index];
  423. data->reset(new SkPoint[*numPts]);
  424. if (SkPath::kCW_Direction == dir) {
  425. for (int i = 0; i < *numPts; ++i) {
  426. (*data)[i] = PolygonOffsetData::gConvexPoints[index][i];
  427. }
  428. } else {
  429. for (int i = 0; i < *numPts; ++i) {
  430. (*data)[i] = PolygonOffsetData::gConvexPoints[index][*numPts - i - 1];
  431. }
  432. }
  433. } else {
  434. // procedurally generated
  435. SkScalar width = kMaxPathHeight / 2;
  436. SkScalar height = kMaxPathHeight / 2;
  437. int numPtsArray[] = { 3, 4, 5, 5, 6, 8, 8, 20, 100 };
  438. size_t arrayIndex = index - SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints);
  439. SkASSERT(arrayIndex < SK_ARRAY_COUNT(numPtsArray));
  440. *numPts = numPtsArray[arrayIndex];
  441. if (arrayIndex == 3 || arrayIndex == 6) {
  442. // squashed pentagon and octagon
  443. width = kMaxPathHeight / 5;
  444. }
  445. data->reset(new SkPoint[*numPts]);
  446. create_ngon(*numPts, data->get(), width, height, dir);
  447. }
  448. }
  449. static void GetSimplePolygon(int index, SkPath::Direction dir,
  450. std::unique_ptr<SkPoint[]>* data, int* numPts) {
  451. if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints)) {
  452. // manually specified
  453. *numPts = (int)PolygonOffsetData::gSimpleSizes[index];
  454. data->reset(new SkPoint[*numPts]);
  455. if (SkPath::kCW_Direction == dir) {
  456. for (int i = 0; i < *numPts; ++i) {
  457. (*data)[i] = PolygonOffsetData::gSimplePoints[index][i];
  458. }
  459. } else {
  460. for (int i = 0; i < *numPts; ++i) {
  461. (*data)[i] = PolygonOffsetData::gSimplePoints[index][*numPts - i - 1];
  462. }
  463. }
  464. } else {
  465. // procedurally generated
  466. SkScalar width = kMaxPathHeight / 2;
  467. SkScalar height = kMaxPathHeight / 2;
  468. int numPtsArray[] = { 5, 7, 8, 20, 100 };
  469. size_t arrayIndex = index - SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints);
  470. arrayIndex = SkTMin(arrayIndex, SK_ARRAY_COUNT(numPtsArray) - 1);
  471. SkASSERT(arrayIndex < SK_ARRAY_COUNT(numPtsArray));
  472. *numPts = numPtsArray[arrayIndex];
  473. // squash horizontally
  474. width = kMaxPathHeight / 5;
  475. data->reset(new SkPoint[*numPts]);
  476. create_ngon(*numPts, data->get(), width, height, dir);
  477. }
  478. }
  479. // Draw a single polygon with insets and potentially outsets
  480. void drawPolygon(SkCanvas* canvas, int index, SkPoint* offset) {
  481. SkPoint center;
  482. SkRect bounds;
  483. {
  484. std::unique_ptr<SkPoint[]> data(nullptr);
  485. int numPts;
  486. if (fConvexOnly) {
  487. GetConvexPolygon(index, SkPath::kCW_Direction, &data, &numPts);
  488. } else {
  489. GetSimplePolygon(index, SkPath::kCW_Direction, &data, &numPts);
  490. }
  491. bounds.set(data.get(), numPts);
  492. if (!fConvexOnly) {
  493. bounds.outset(kMaxOutset, kMaxOutset);
  494. }
  495. if (offset->fX + bounds.width() > kGMWidth) {
  496. offset->fX = 0;
  497. offset->fY += kMaxPathHeight;
  498. }
  499. center = { offset->fX + SkScalarHalf(bounds.width()), offset->fY };
  500. offset->fX += bounds.width();
  501. }
  502. const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
  503. const float insets[] = { 5, 10, 15, 20, 25, 30, 35, 40 };
  504. const float offsets[] = { 2, 5, 9, 14, 20, 27, 35, 44, -2, -5, -9 };
  505. const SkColor colors[] = { 0xFF901313, 0xFF8D6214, 0xFF698B14, 0xFF1C8914,
  506. 0xFF148755, 0xFF146C84, 0xFF142482, 0xFF4A1480,
  507. 0xFF901313, 0xFF8D6214, 0xFF698B14 };
  508. SkPaint paint;
  509. paint.setAntiAlias(true);
  510. paint.setStyle(SkPaint::kStroke_Style);
  511. paint.setStrokeWidth(1);
  512. std::unique_ptr<SkPoint[]> data(nullptr);
  513. int numPts;
  514. if (fConvexOnly) {
  515. GetConvexPolygon(index, dirs[index % 2], &data, &numPts);
  516. } else {
  517. GetSimplePolygon(index, dirs[index % 2], &data, &numPts);
  518. }
  519. {
  520. SkPath path;
  521. path.moveTo(data.get()[0]);
  522. for (int i = 1; i < numPts; ++i) {
  523. path.lineTo(data.get()[i]);
  524. }
  525. path.close();
  526. canvas->save();
  527. canvas->translate(center.fX, center.fY);
  528. canvas->drawPath(path, paint);
  529. canvas->restore();
  530. }
  531. SkTDArray<SkPoint> offsetPoly;
  532. size_t count = fConvexOnly ? SK_ARRAY_COUNT(insets) : SK_ARRAY_COUNT(offsets);
  533. for (size_t i = 0; i < count; ++i) {
  534. SkScalar offset = fConvexOnly ? insets[i] : offsets[i];
  535. std::function<SkScalar(const SkPoint&)> offsetFunc;
  536. bool result;
  537. if (fConvexOnly) {
  538. result = SkInsetConvexPolygon(data.get(), numPts, offset, &offsetPoly);
  539. } else {
  540. SkRect bounds;
  541. bounds.setBoundsCheck(data.get(), numPts);
  542. result = SkOffsetSimplePolygon(data.get(), numPts, bounds, offset, &offsetPoly);
  543. }
  544. if (result) {
  545. SkPath path;
  546. path.moveTo(offsetPoly[0]);
  547. for (int i = 1; i < offsetPoly.count(); ++i) {
  548. path.lineTo(offsetPoly[i]);
  549. }
  550. path.close();
  551. paint.setColor(ToolUtils::color_to_565(colors[i]));
  552. canvas->save();
  553. canvas->translate(center.fX, center.fY);
  554. canvas->drawPath(path, paint);
  555. canvas->restore();
  556. }
  557. }
  558. }
  559. void onDraw(SkCanvas* canvas) override {
  560. // the right edge of the last drawn path
  561. SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) };
  562. if (!fConvexOnly) {
  563. offset.fY += kMaxOutset;
  564. }
  565. for (int i = 0; i < kNumPaths; ++i) {
  566. this->drawPolygon(canvas, i, &offset);
  567. }
  568. }
  569. private:
  570. static constexpr int kNumPaths = 20;
  571. static constexpr int kMaxPathHeight = 100;
  572. static constexpr int kMaxOutset = 16;
  573. static constexpr int kGMWidth = 512;
  574. static constexpr int kGMHeight = 512;
  575. bool fConvexOnly;
  576. typedef GM INHERITED;
  577. };
  578. //////////////////////////////////////////////////////////////////////////////
  579. DEF_GM(return new PolygonOffsetGM(true);)
  580. DEF_GM(return new PolygonOffsetGM(false);)
  581. }