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