123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "gm/gm.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkColor.h"
- #include "include/core/SkPaint.h"
- #include "include/core/SkPath.h"
- #include "include/core/SkPoint.h"
- #include "include/core/SkRect.h"
- #include "include/core/SkScalar.h"
- #include "include/core/SkSize.h"
- #include "include/core/SkString.h"
- #include "include/core/SkTypes.h"
- #include "include/private/SkTDArray.h"
- #include "src/utils/SkPolyUtils.h"
- #include "tools/ToolUtils.h"
- #include <functional>
- #include <memory>
- static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPath::Direction dir) {
- float angleStep = 360.0f / n, angle = 0.0f;
- if ((n % 2) == 1) {
- angle = angleStep/2.0f;
- }
- if (SkPath::kCCW_Direction == dir) {
- angle = -angle;
- angleStep = -angleStep;
- }
- for (int i = 0; i < n; ++i) {
- pts[i].fX = -SkScalarSin(SkDegreesToRadians(angle)) * w;
- pts[i].fY = SkScalarCos(SkDegreesToRadians(angle)) * h;
- angle += angleStep;
- }
- }
- namespace PolygonOffsetData {
- // narrow rect
- const SkPoint gPoints0[] = {
- { -1.5f, -50.0f },
- { 1.5f, -50.0f },
- { 1.5f, 50.0f },
- { -1.5f, 50.0f }
- };
- // narrow rect on an angle
- const SkPoint gPoints1[] = {
- { -50.0f, -49.0f },
- { -49.0f, -50.0f },
- { 50.0f, 49.0f },
- { 49.0f, 50.0f }
- };
- // trap - narrow on top - wide on bottom
- const SkPoint gPoints2[] = {
- { -10.0f, -50.0f },
- { 10.0f, -50.0f },
- { 50.0f, 50.0f },
- { -50.0f, 50.0f }
- };
- // wide skewed rect
- const SkPoint gPoints3[] = {
- { -50.0f, -50.0f },
- { 0.0f, -50.0f },
- { 50.0f, 50.0f },
- { 0.0f, 50.0f }
- };
- // thin rect with colinear-ish lines
- const SkPoint gPoints4[] = {
- { -6.0f, -50.0f },
- { 4.0f, -50.0f },
- { 5.0f, -25.0f },
- { 6.0f, 0.0f },
- { 5.0f, 25.0f },
- { 4.0f, 50.0f },
- { -4.0f, 50.0f }
- };
- // degenerate
- const SkPoint gPoints5[] = {
- { -0.025f, -0.025f },
- { 0.025f, -0.025f },
- { 0.025f, 0.025f },
- { -0.025f, 0.025f }
- };
- // Quad with near coincident point
- const SkPoint gPoints6[] = {
- { -20.0f, -13.0f },
- { -20.0f, -13.05f },
- { 20.0f, -13.0f },
- { 20.0f, 27.0f }
- };
- // thin rect with colinear lines
- const SkPoint gPoints7[] = {
- { -10.0f, -50.0f },
- { 10.0f, -50.0f },
- { 10.0f, -20.0f },
- { 10.0f, 0.0f },
- { 10.0f, 35.0f },
- { 10.0f, 50.0f },
- { -10.0f, 50.0f }
- };
- // capped teardrop
- const SkPoint gPoints8[] = {
- { 50.00f, 50.00f },
- { 0.00f, 50.00f },
- { -15.45f, 47.55f },
- { -29.39f, 40.45f },
- { -40.45f, 29.39f },
- { -47.55f, 15.45f },
- { -50.00f, 0.00f },
- { -47.55f, -15.45f },
- { -40.45f, -29.39f },
- { -29.39f, -40.45f },
- { -15.45f, -47.55f },
- { 0.00f, -50.00f },
- { 50.00f, -50.00f }
- };
- // teardrop
- const SkPoint gPoints9[] = {
- { 4.39f, 40.45f },
- { -9.55f, 47.55f },
- { -25.00f, 50.00f },
- { -40.45f, 47.55f },
- { -54.39f, 40.45f },
- { -65.45f, 29.39f },
- { -72.55f, 15.45f },
- { -75.00f, 0.00f },
- { -72.55f, -15.45f },
- { -65.45f, -29.39f },
- { -54.39f, -40.45f },
- { -40.45f, -47.55f },
- { -25.0f, -50.0f },
- { -9.55f, -47.55f },
- { 4.39f, -40.45f },
- { 75.00f, 0.00f }
- };
- // clipped triangle
- const SkPoint gPoints10[] = {
- { -10.0f, -50.0f },
- { 10.0f, -50.0f },
- { 50.0f, 31.0f },
- { 40.0f, 50.0f },
- { -40.0f, 50.0f },
- { -50.0f, 31.0f },
- };
- // tab
- const SkPoint gPoints11[] = {
- { -45, -25 },
- { 45, -25 },
- { 45, 25 },
- { 20, 25 },
- { 19.6157f, 25.f + 3.9018f },
- { 18.4776f, 25.f + 7.6537f },
- { 16.6294f, 25.f + 11.1114f },
- { 14.1421f, 25.f + 14.1421f },
- { 11.1114f, 25.f + 16.6294f },
- { 7.6537f, 25.f + 18.4776f },
- { 3.9018f, 25.f + 19.6157f },
- { 0, 45.f },
- { -3.9018f, 25.f + 19.6157f },
- { -7.6537f, 25.f + 18.4776f },
- { -11.1114f, 25.f + 16.6294f },
- { -14.1421f, 25.f + 14.1421f },
- { -16.6294f, 25.f + 11.1114f },
- { -18.4776f, 25.f + 7.6537f },
- { -19.6157f, 25.f + 3.9018f },
- { -20, 25 },
- { -45, 25 }
- };
- // star of david
- const SkPoint gPoints12[] = {
- { 0.0f, -50.0f },
- { 14.43f, -25.0f },
- { 43.30f, -25.0f },
- { 28.86f, 0.0f },
- { 43.30f, 25.0f },
- { 14.43f, 25.0f },
- { 0.0f, 50.0f },
- { -14.43f, 25.0f },
- { -43.30f, 25.0f },
- { -28.86f, 0.0f },
- { -43.30f, -25.0f },
- { -14.43f, -25.0f },
- };
- // notch
- const SkScalar kBottom = 25.f;
- const SkPoint gPoints13[] = {
- { -50, kBottom - 50.f },
- { 50, kBottom - 50.f },
- { 50, kBottom },
- { 20, kBottom },
- { 19.6157f, kBottom - 3.9018f },
- { 18.4776f, kBottom - 7.6537f },
- { 16.6294f, kBottom - 11.1114f },
- { 14.1421f, kBottom - 14.1421f },
- { 11.1114f, kBottom - 16.6294f },
- { 7.6537f, kBottom - 18.4776f },
- { 3.9018f, kBottom - 19.6157f },
- { 0, kBottom - 20.f },
- { -3.9018f, kBottom - 19.6157f },
- { -7.6537f, kBottom - 18.4776f },
- { -11.1114f, kBottom - 16.6294f },
- { -14.1421f, kBottom - 14.1421f },
- { -16.6294f, kBottom - 11.1114f },
- { -18.4776f, kBottom - 7.6537f },
- { -19.6157f, kBottom - 3.9018f },
- { -20, kBottom },
- { -50, kBottom }
- };
- // crown
- const SkPoint gPoints14[] = {
- { -40, -39 },
- { 40, -39 },
- { 40, -20 },
- { 30, 40 },
- { 20, -20 },
- { 10, 40 },
- { 0, -20 },
- { -10, 40 },
- { -20, -20 },
- { -30, 40 },
- { -40, -20 }
- };
- // dumbbell
- const SkPoint gPoints15[] = {
- { -26, -3 },
- { -24, -6.2f },
- { -22.5f, -8 },
- { -20, -9.9f },
- { -17.5f, -10.3f },
- { -15, -10.9f },
- { -12.5f, -10.2f },
- { -10, -9.7f },
- { -7.5f, -8.1f },
- { -5, -7.7f },
- { -2.5f, -7.4f },
- { 0, -7.7f },
- { 3, -9 },
- { 6.5f, -11.5f },
- { 10.6f, -14 },
- { 14, -15.2f },
- { 17, -15.5f },
- { 20, -15.2f },
- { 23.4f, -14 },
- { 27.5f, -11.5f },
- { 30, -8 },
- { 32, -4 },
- { 32.5f, 0 },
- { 32, 4 },
- { 30, 8 },
- { 27.5f, 11.5f },
- { 23.4f, 14 },
- { 20, 15.2f },
- { 17, 15.5f },
- { 14, 15.2f },
- { 10.6f, 14 },
- { 6.5f, 11.5f },
- { 3, 9 },
- { 0, 7.7f },
- { -2.5f, 7.4f },
- { -5, 7.7f },
- { -7.5f, 8.1f },
- { -10, 9.7f },
- { -12.5f, 10.2f },
- { -15, 10.9f },
- { -17.5f, 10.3f },
- { -20, 9.9f },
- { -22.5f, 8 },
- { -24, 6.2f },
- { -26, 3 },
- { -26.5f, 0 }
- };
- // truncated dumbbell
- // (checks winding computation in OffsetSimplePolygon)
- const SkPoint gPoints16[] = {
- { -15 + 3, -9 },
- { -15 + 6.5f, -11.5f },
- { -15 + 10.6f, -14 },
- { -15 + 14, -15.2f },
- { -15 + 17, -15.5f },
- { -15 + 20, -15.2f },
- { -15 + 23.4f, -14 },
- { -15 + 27.5f, -11.5f },
- { -15 + 30, -8 },
- { -15 + 32, -4 },
- { -15 + 32.5f, 0 },
- { -15 + 32, 4 },
- { -15 + 30, 8 },
- { -15 + 27.5f, 11.5f },
- { -15 + 23.4f, 14 },
- { -15 + 20, 15.2f },
- { -15 + 17, 15.5f },
- { -15 + 14, 15.2f },
- { -15 + 10.6f, 14 },
- { -15 + 6.5f, 11.5f },
- { -15 + 3, 9 },
- };
- // square notch
- // (to detect segment-segment intersection)
- const SkPoint gPoints17[] = {
- { -50, kBottom - 50.f },
- { 50, kBottom - 50.f },
- { 50, kBottom },
- { 20, kBottom },
- { 20, kBottom - 20.f },
- { -20, kBottom - 20.f },
- { -20, kBottom },
- { -50, kBottom }
- };
- // box with Peano curve
- const SkPoint gPoints18[] = {
- { 0, 0 },
- { 0, -12 },
- { -6, -12 },
- { -6, 0 },
- { -12, 0 },
- { -12, -12},
- { -18, -12},
- { -18, 18},
- { -12, 18},
- {-12, 6},
- {-6, 6},
- {-6, 36},
- {-12, 36},
- {-12, 24},
- {-18, 24},
- {-18, 36},
- {-24, 36},
- {-24, 24},
- {-30, 24},
- {-30, 36},
- {-36, 36},
- {-36, 6},
- {-30, 6},
- {-30, 18},
- {-24, 18},
- {-24, -12},
- {-30, -12},
- {-30, 0},
- {-36, 0},
- {-36, -36},
- {36, -36},
- {36, 36},
- {12, 36},
- {12, 24},
- {6, 24},
- {6, 36},
- {0, 36},
- {0, 6},
- {6, 6},
- {6, 18},
- {12, 18},
- {12, -12},
- {6, -12},
- {6, 0}
- };
- const SkPoint* gConvexPoints[] = {
- gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
- gPoints7, gPoints8, gPoints9, gPoints10,
- };
- const size_t gConvexSizes[] = {
- SK_ARRAY_COUNT(gPoints0),
- SK_ARRAY_COUNT(gPoints1),
- SK_ARRAY_COUNT(gPoints2),
- SK_ARRAY_COUNT(gPoints3),
- SK_ARRAY_COUNT(gPoints4),
- SK_ARRAY_COUNT(gPoints5),
- SK_ARRAY_COUNT(gPoints6),
- SK_ARRAY_COUNT(gPoints7),
- SK_ARRAY_COUNT(gPoints8),
- SK_ARRAY_COUNT(gPoints9),
- SK_ARRAY_COUNT(gPoints10),
- };
- static_assert(SK_ARRAY_COUNT(gConvexSizes) == SK_ARRAY_COUNT(gConvexPoints), "array_mismatch");
- const SkPoint* gSimplePoints[] = {
- gPoints0, gPoints1, gPoints2, gPoints4, gPoints5, gPoints7,
- gPoints8, gPoints11, gPoints12, gPoints13, gPoints14, gPoints15,
- gPoints16, gPoints17, gPoints18,
- };
- const size_t gSimpleSizes[] = {
- SK_ARRAY_COUNT(gPoints0),
- SK_ARRAY_COUNT(gPoints1),
- SK_ARRAY_COUNT(gPoints2),
- SK_ARRAY_COUNT(gPoints4),
- SK_ARRAY_COUNT(gPoints5),
- SK_ARRAY_COUNT(gPoints7),
- SK_ARRAY_COUNT(gPoints8),
- SK_ARRAY_COUNT(gPoints11),
- SK_ARRAY_COUNT(gPoints12),
- SK_ARRAY_COUNT(gPoints13),
- SK_ARRAY_COUNT(gPoints14),
- SK_ARRAY_COUNT(gPoints15),
- SK_ARRAY_COUNT(gPoints16),
- SK_ARRAY_COUNT(gPoints17),
- SK_ARRAY_COUNT(gPoints18),
- };
- static_assert(SK_ARRAY_COUNT(gSimpleSizes) == SK_ARRAY_COUNT(gSimplePoints), "array_mismatch");
- }
- namespace skiagm {
- // This GM is intended to exercise the offsetting of polygons
- // When fVariableOffset is true it will skew the offset by x,
- // to test perspective and other variable offset functions
- class PolygonOffsetGM : public GM {
- public:
- PolygonOffsetGM(bool convexOnly)
- : fConvexOnly(convexOnly) {
- this->setBGColor(0xFFFFFFFF);
- }
- protected:
- SkString onShortName() override {
- if (fConvexOnly) {
- return SkString("convex-polygon-inset");
- } else {
- return SkString("simple-polygon-offset");
- }
- }
- SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
- bool runAsBench() const override { return true; }
- static void GetConvexPolygon(int index, SkPath::Direction dir,
- std::unique_ptr<SkPoint[]>* data, int* numPts) {
- if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints)) {
- // manually specified
- *numPts = (int)PolygonOffsetData::gConvexSizes[index];
- data->reset(new SkPoint[*numPts]);
- if (SkPath::kCW_Direction == dir) {
- for (int i = 0; i < *numPts; ++i) {
- (*data)[i] = PolygonOffsetData::gConvexPoints[index][i];
- }
- } else {
- for (int i = 0; i < *numPts; ++i) {
- (*data)[i] = PolygonOffsetData::gConvexPoints[index][*numPts - i - 1];
- }
- }
- } else {
- // procedurally generated
- SkScalar width = kMaxPathHeight / 2;
- SkScalar height = kMaxPathHeight / 2;
- int numPtsArray[] = { 3, 4, 5, 5, 6, 8, 8, 20, 100 };
- size_t arrayIndex = index - SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints);
- SkASSERT(arrayIndex < SK_ARRAY_COUNT(numPtsArray));
- *numPts = numPtsArray[arrayIndex];
- if (arrayIndex == 3 || arrayIndex == 6) {
- // squashed pentagon and octagon
- width = kMaxPathHeight / 5;
- }
- data->reset(new SkPoint[*numPts]);
- create_ngon(*numPts, data->get(), width, height, dir);
- }
- }
- static void GetSimplePolygon(int index, SkPath::Direction dir,
- std::unique_ptr<SkPoint[]>* data, int* numPts) {
- if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints)) {
- // manually specified
- *numPts = (int)PolygonOffsetData::gSimpleSizes[index];
- data->reset(new SkPoint[*numPts]);
- if (SkPath::kCW_Direction == dir) {
- for (int i = 0; i < *numPts; ++i) {
- (*data)[i] = PolygonOffsetData::gSimplePoints[index][i];
- }
- } else {
- for (int i = 0; i < *numPts; ++i) {
- (*data)[i] = PolygonOffsetData::gSimplePoints[index][*numPts - i - 1];
- }
- }
- } else {
- // procedurally generated
- SkScalar width = kMaxPathHeight / 2;
- SkScalar height = kMaxPathHeight / 2;
- int numPtsArray[] = { 5, 7, 8, 20, 100 };
- size_t arrayIndex = index - SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints);
- arrayIndex = SkTMin(arrayIndex, SK_ARRAY_COUNT(numPtsArray) - 1);
- SkASSERT(arrayIndex < SK_ARRAY_COUNT(numPtsArray));
- *numPts = numPtsArray[arrayIndex];
- // squash horizontally
- width = kMaxPathHeight / 5;
- data->reset(new SkPoint[*numPts]);
- create_ngon(*numPts, data->get(), width, height, dir);
- }
- }
- // Draw a single polygon with insets and potentially outsets
- void drawPolygon(SkCanvas* canvas, int index, SkPoint* offset) {
- SkPoint center;
- SkRect bounds;
- {
- std::unique_ptr<SkPoint[]> data(nullptr);
- int numPts;
- if (fConvexOnly) {
- GetConvexPolygon(index, SkPath::kCW_Direction, &data, &numPts);
- } else {
- GetSimplePolygon(index, SkPath::kCW_Direction, &data, &numPts);
- }
- bounds.set(data.get(), numPts);
- if (!fConvexOnly) {
- bounds.outset(kMaxOutset, kMaxOutset);
- }
- if (offset->fX + bounds.width() > kGMWidth) {
- offset->fX = 0;
- offset->fY += kMaxPathHeight;
- }
- center = { offset->fX + SkScalarHalf(bounds.width()), offset->fY };
- offset->fX += bounds.width();
- }
- const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
- const float insets[] = { 5, 10, 15, 20, 25, 30, 35, 40 };
- const float offsets[] = { 2, 5, 9, 14, 20, 27, 35, 44, -2, -5, -9 };
- const SkColor colors[] = { 0xFF901313, 0xFF8D6214, 0xFF698B14, 0xFF1C8914,
- 0xFF148755, 0xFF146C84, 0xFF142482, 0xFF4A1480,
- 0xFF901313, 0xFF8D6214, 0xFF698B14 };
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(1);
- std::unique_ptr<SkPoint[]> data(nullptr);
- int numPts;
- if (fConvexOnly) {
- GetConvexPolygon(index, dirs[index % 2], &data, &numPts);
- } else {
- GetSimplePolygon(index, dirs[index % 2], &data, &numPts);
- }
- {
- SkPath path;
- path.moveTo(data.get()[0]);
- for (int i = 1; i < numPts; ++i) {
- path.lineTo(data.get()[i]);
- }
- path.close();
- canvas->save();
- canvas->translate(center.fX, center.fY);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- SkTDArray<SkPoint> offsetPoly;
- size_t count = fConvexOnly ? SK_ARRAY_COUNT(insets) : SK_ARRAY_COUNT(offsets);
- for (size_t i = 0; i < count; ++i) {
- SkScalar offset = fConvexOnly ? insets[i] : offsets[i];
- std::function<SkScalar(const SkPoint&)> offsetFunc;
- bool result;
- if (fConvexOnly) {
- result = SkInsetConvexPolygon(data.get(), numPts, offset, &offsetPoly);
- } else {
- SkRect bounds;
- bounds.setBoundsCheck(data.get(), numPts);
- result = SkOffsetSimplePolygon(data.get(), numPts, bounds, offset, &offsetPoly);
- }
- if (result) {
- SkPath path;
- path.moveTo(offsetPoly[0]);
- for (int i = 1; i < offsetPoly.count(); ++i) {
- path.lineTo(offsetPoly[i]);
- }
- path.close();
- paint.setColor(ToolUtils::color_to_565(colors[i]));
- canvas->save();
- canvas->translate(center.fX, center.fY);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- }
- }
- void onDraw(SkCanvas* canvas) override {
- // the right edge of the last drawn path
- SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) };
- if (!fConvexOnly) {
- offset.fY += kMaxOutset;
- }
- for (int i = 0; i < kNumPaths; ++i) {
- this->drawPolygon(canvas, i, &offset);
- }
- }
- private:
- static constexpr int kNumPaths = 20;
- static constexpr int kMaxPathHeight = 100;
- static constexpr int kMaxOutset = 16;
- static constexpr int kGMWidth = 512;
- static constexpr int kGMHeight = 512;
- bool fConvexOnly;
- typedef GM INHERITED;
- };
- //////////////////////////////////////////////////////////////////////////////
- DEF_GM(return new PolygonOffsetGM(true);)
- DEF_GM(return new PolygonOffsetGM(false);)
- }
|