123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- /*
- * Copyright 2015 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/SkMatrix.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 "src/core/SkPathPriv.h"
- #include <memory>
- static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
- float angleStep = 360.0f / n, angle = 0.0f;
- if ((n % 2) == 1) {
- angle = angleStep/2.0f;
- }
- for (int i = 0; i < n; ++i) {
- pts[i].fX = -SkScalarSin(SkDegreesToRadians(angle)) * width;
- pts[i].fY = SkScalarCos(SkDegreesToRadians(angle)) * height;
- angle += angleStep;
- }
- }
- namespace ConvexLineOnlyData {
- // 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 }, // remove if collinear diagonal points are not concave
- { 6.0f, 0.0f },
- { 5.0f, 25.0f }, // remove if collinear diagonal points are not concave
- { 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 }
- };
- // Triangle in which the first point should fuse with last
- 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, -25.0f },
- { 10.0f, 0.0f },
- { 10.0f, 25.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 },
- };
- const SkPoint* gPoints[] = {
- gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
- gPoints7, gPoints8, gPoints9, gPoints10,
- };
- const size_t gSizes[] = {
- 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(gSizes) == SK_ARRAY_COUNT(gPoints), "array_mismatch");
- }
- namespace skiagm {
- // This GM is intended to exercise Ganesh's handling of convex line-only
- // paths
- class ConvexLineOnlyPathsGM : public GM {
- public:
- ConvexLineOnlyPathsGM(bool doStrokeAndFill) : fDoStrokeAndFill(doStrokeAndFill) {
- this->setBGColor(0xFFFFFFFF);
- }
- protected:
- SkString onShortName() override {
- if (fDoStrokeAndFill) {
- return SkString("convex-lineonly-paths-stroke-and-fill");
- }
- return SkString("convex-lineonly-paths");
- }
- SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
- bool runAsBench() const override { return true; }
- static SkPath GetPath(int index, SkPath::Direction dir) {
- std::unique_ptr<SkPoint[]> data(nullptr);
- const SkPoint* points;
- int numPts;
- if (index < (int) SK_ARRAY_COUNT(ConvexLineOnlyData::gPoints)) {
- // manually specified
- points = ConvexLineOnlyData::gPoints[index];
- numPts = (int)ConvexLineOnlyData::gSizes[index];
- } else {
- // procedurally generated
- SkScalar width = kMaxPathHeight/2;
- SkScalar height = kMaxPathHeight/2;
- switch (index-SK_ARRAY_COUNT(ConvexLineOnlyData::gPoints)) {
- case 0:
- numPts = 3;
- break;
- case 1:
- numPts = 4;
- break;
- case 2:
- numPts = 5;
- break;
- case 3: // squashed pentagon
- numPts = 5;
- width = kMaxPathHeight/5;
- break;
- case 4:
- numPts = 6;
- break;
- case 5:
- numPts = 8;
- break;
- case 6: // squashed octogon
- numPts = 8;
- width = kMaxPathHeight/5;
- break;
- case 7:
- numPts = 20;
- break;
- case 8:
- numPts = 100;
- break;
- default:
- numPts = 3;
- break;
- }
- data.reset(new SkPoint[numPts]);
- create_ngon(numPts, data.get(), width, height);
- points = data.get();
- }
- SkPath path;
- if (SkPath::kCW_Direction == dir) {
- path.moveTo(points[0]);
- for (int i = 1; i < numPts; ++i) {
- path.lineTo(points[i]);
- }
- } else {
- path.moveTo(points[numPts-1]);
- for (int i = numPts-2; i >= 0; --i) {
- path.lineTo(points[i]);
- }
- }
- path.close();
- #ifdef SK_DEBUG
- // Each path this method returns should be convex, only composed of
- // lines, wound the right direction, and short enough to fit in one
- // of the GMs rows.
- SkASSERT(path.isConvex());
- SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
- SkPathPriv::FirstDirection actualDir;
- SkASSERT(SkPathPriv::CheapComputeFirstDirection(path, &actualDir));
- SkASSERT(SkPathPriv::AsFirstDirection(dir) == actualDir);
- SkRect bounds = path.getBounds();
- SkASSERT(SkScalarNearlyEqual(bounds.centerX(), 0.0f));
- SkASSERT(bounds.height() <= kMaxPathHeight);
- #endif
- return path;
- }
- // Draw a single path several times, shrinking it, flipping its direction
- // and changing its start vertex each time.
- void drawPath(SkCanvas* canvas, int index, SkPoint* offset) {
- SkPoint center;
- {
- SkPath path = GetPath(index, SkPath::kCW_Direction);
- if (offset->fX+path.getBounds().width() > kGMWidth) {
- offset->fX = 0;
- offset->fY += kMaxPathHeight;
- if (fDoStrokeAndFill) {
- offset->fX += kStrokeWidth / 2.0f;
- offset->fY += kStrokeWidth / 2.0f;
- }
- }
- center = { offset->fX + SkScalarHalf(path.getBounds().width()), offset->fY};
- offset->fX += path.getBounds().width();
- if (fDoStrokeAndFill) {
- offset->fX += kStrokeWidth;
- }
- }
- const SkColor colors[2] = { SK_ColorBLACK, SK_ColorWHITE };
- const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
- const float scales[] = { 1.0f, 0.75f, 0.5f, 0.25f, 0.1f, 0.01f, 0.001f };
- const SkPaint::Join joins[3] = { SkPaint::kRound_Join,
- SkPaint::kBevel_Join,
- SkPaint::kMiter_Join };
- SkPaint paint;
- paint.setAntiAlias(true);
- for (size_t i = 0; i < SK_ARRAY_COUNT(scales); ++i) {
- SkPath path = GetPath(index, dirs[i%2]);
- if (fDoStrokeAndFill) {
- paint.setStyle(SkPaint::kStrokeAndFill_Style);
- paint.setStrokeJoin(joins[i%3]);
- paint.setStrokeWidth(SkIntToScalar(kStrokeWidth));
- }
- canvas->save();
- canvas->translate(center.fX, center.fY);
- canvas->scale(scales[i], scales[i]);
- paint.setColor(colors[i%2]);
- 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 (fDoStrokeAndFill) {
- offset.fX += kStrokeWidth / 2.0f;
- offset.fY += kStrokeWidth / 2.0f;
- }
- for (int i = 0; i < kNumPaths; ++i) {
- this->drawPath(canvas, i, &offset);
- }
- {
- // Repro for crbug.com/472723 (Missing AA on portions of graphic with GPU rasterization)
- SkPaint p;
- p.setAntiAlias(true);
- if (fDoStrokeAndFill) {
- p.setStyle(SkPaint::kStrokeAndFill_Style);
- p.setStrokeJoin(SkPaint::kMiter_Join);
- p.setStrokeWidth(SkIntToScalar(kStrokeWidth));
- }
- SkPath p1;
- p1.moveTo(60.8522949f, 364.671021f);
- p1.lineTo(59.4380493f, 364.671021f);
- p1.lineTo(385.414276f, 690.647217f);
- p1.lineTo(386.121399f, 689.940125f);
- canvas->save();
- canvas->translate(356.0f, 50.0f);
- canvas->drawPath(p1, p);
- canvas->restore();
- // Repro for crbug.com/869172 (SVG path incorrectly simplified when using GPU
- // Rasterization). This will only draw anything in the stroke-and-fill version.
- SkPath p2;
- p2.moveTo(10.f, 0.f);
- p2.lineTo(38.f, 0.f);
- p2.lineTo(66.f, 0.f);
- p2.lineTo(94.f, 0.f);
- p2.lineTo(122.f, 0.f);
- p2.lineTo(150.f, 0.f);
- p2.lineTo(150.f, 0.f);
- p2.lineTo(122.f, 0.f);
- p2.lineTo(94.f, 0.f);
- p2.lineTo(66.f, 0.f);
- p2.lineTo(38.f, 0.f);
- p2.lineTo(10.f, 0.f);
- p2.close();
- canvas->save();
- canvas->translate(0.0f, 500.0f);
- canvas->drawPath(p2, p);
- canvas->restore();
- // Repro for crbug.com/856137. This path previously caused GrAAConvexTessellator to turn
- // inset rings into outsets when adjacent bisector angles converged outside the previous
- // ring due to accumulated error.
- SkPath p3;
- p3.setFillType(SkPath::kEvenOdd_FillType);
- p3.moveTo(1184.96f, 982.557f);
- p3.lineTo(1183.71f, 982.865f);
- p3.lineTo(1180.99f, 982.734f);
- p3.lineTo(1178.5f, 981.541f);
- p3.lineTo(1176.35f, 979.367f);
- p3.lineTo(1178.94f, 938.854f);
- p3.lineTo(1181.35f, 936.038f);
- p3.lineTo(1183.96f, 934.117f);
- p3.lineTo(1186.67f, 933.195f);
- p3.lineTo(1189.36f, 933.342f);
- p3.lineTo(1191.58f, 934.38f);
- p3.close();
- canvas->save();
- SkMatrix m;
- m.setAll(0.0893210843f, 0, 79.1197586f, 0, 0.0893210843f, 300, 0, 0, 1);
- canvas->concat(m);
- canvas->drawPath(p3, p);
- canvas->restore();
- }
- }
- private:
- static constexpr int kStrokeWidth = 10;
- static constexpr int kNumPaths = 20;
- static constexpr int kMaxPathHeight = 100;
- static constexpr int kGMWidth = 512;
- static constexpr int kGMHeight = 512;
- bool fDoStrokeAndFill;
- typedef GM INHERITED;
- };
- //////////////////////////////////////////////////////////////////////////////
- DEF_GM(return new ConvexLineOnlyPathsGM(false);)
- DEF_GM(return new ConvexLineOnlyPathsGM(true);)
- }
|