skia2/gm/polygonoffset.cpp
Brian Osman 4428f2c39f Remove SkScalarSinCos
This differed from the separate versions in that it snapped to zero.
It was also strictly worse than calling the two separate versions.
Most clients don't need the snapping, so just call the two existing
functions. For clients that need the snapping, call new variants of
each that do snap.

Change-Id: Ia4e09fd9651932fe15caeab1399df7f6281bdc17
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/205303
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Reed <reed@google.com>
2019-04-02 15:46:57 +00:00

612 lines
16 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 "SkPathPriv.h"
#include "SkPolyUtils.h"
#include "ToolUtils.h"
#include "gm.h"
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 {
result = SkOffsetSimplePolygon(data.get(), numPts, 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);)
}