skia2/gm/polygonoffset.cpp
Ben Wagner 7fde8e1728 IWYU for gms.
This almost gets gms to be iwyu clean. The last bit is around gm.cpp
and the tracing framework and its use of atomic. Will also need a way
of keeping things from regressing, which is difficult due to needing to
do this outside-in.

Change-Id: I1393531e99da8b0f1a29f55c53c86d53f459af7d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/211593
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
2019-05-02 17:48:53 +00:00

627 lines
17 KiB
C++

/*
* 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);)
}