70fae35842
More details are in the bug, but the specific test case gets an overlap region in the AA outset geometry for the left and right shapes. Additionally, the mitered outset for the left shape is collinear with the bottom of the right shape, so the winding of exterior edge of the overlap region is updated to +2. When determining the polygons to fill, this +2 on the outer edges violates the winding rules that normally ensure the interior polygons (that use +/-2 instead of +/-1) are always filled. It appears a similar bug fix was added here: https://skia-review.googlesource.com/c/skia/+/141952/ but then didn't survive a heavy refactor later: https://skia-review.googlesource.com/c/skia/+/215094 To the best of my knowledge, this is achieving the same result as the original fix but is updated to preserve winding scale for interior polygons with overlap regions (the code checks for these, but they seem pretty rare to me). Bug: 1197461 Change-Id: I0d32820af8cfec92c46114aeaa58b6e340abdfca Reviewed-on: https://skia-review.googlesource.com/c/skia/+/397140 Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
195 lines
6.7 KiB
C++
195 lines
6.7 KiB
C++
/*
|
|
* Copyright 2013 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/SkPathBuilder.h"
|
|
#include "include/core/SkPoint.h"
|
|
#include "include/core/SkScalar.h"
|
|
#include "include/core/SkSize.h"
|
|
#include "include/core/SkString.h"
|
|
#include "include/core/SkTypes.h"
|
|
#include "include/private/SkTArray.h"
|
|
#include "include/utils/SkRandom.h"
|
|
|
|
namespace skiagm {
|
|
|
|
// This GM tests a grab-bag of convex and concave polygons. They are triangles,
|
|
// trapezoid, diamond, polygons with lots of edges, several concave polygons...
|
|
// But rectangles are excluded.
|
|
class PolygonsGM: public GM {
|
|
public:
|
|
PolygonsGM() {}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("polygons");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
int width = kNumPolygons * kCellSize + 40;
|
|
int height = (kNumJoins * kNumStrokeWidths + kNumExtraStyles) * kCellSize + 40;
|
|
return SkISize::Make(width, height);
|
|
}
|
|
|
|
// Construct all polygons
|
|
void onOnceBeforeDraw() override {
|
|
SkPoint p0[] = {{0, 0}, {60, 0}, {90, 40}}; // triangle
|
|
SkPoint p1[] = {{0, 0}, {0, 40}, {60, 40}, {40, 0}}; // trapezoid
|
|
SkPoint p2[] = {{0, 0}, {40, 40}, {80, 40}, {40, 0}}; // diamond
|
|
SkPoint p3[] = {{10, 0}, {50, 0}, {60, 10}, {60, 30}, {50, 40},
|
|
{10, 40}, {0, 30}, {0, 10}}; // octagon
|
|
SkPoint p4[32]; // circle-like polygons with 32-edges.
|
|
SkPoint p5[] = {{0, 0}, {20, 20}, {0, 40}, {60, 20}}; // concave polygon with 4 edges
|
|
SkPoint p6[] = {{0, 40}, {0, 30}, {15, 30}, {15, 20}, {30, 20},
|
|
{30, 10}, {45, 10}, {45, 0}, {60, 0}, {60, 40}}; // stairs-like polygon
|
|
SkPoint p7[] = {{0, 20}, {20, 20}, {30, 0}, {40, 20}, {60, 20},
|
|
{45, 30}, {55, 50}, {30, 40}, {5, 50}, {15, 30}}; // five-point stars
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(p4); ++i) {
|
|
SkScalar angle = 2 * SK_ScalarPI * i / SK_ARRAY_COUNT(p4);
|
|
p4[i].set(20 * SkScalarCos(angle) + 20, 20 * SkScalarSin(angle) + 20);
|
|
}
|
|
|
|
struct Polygons {
|
|
SkPoint* fPoints;
|
|
size_t fPointNum;
|
|
} pgs[] = {
|
|
{ p0, SK_ARRAY_COUNT(p0) },
|
|
{ p1, SK_ARRAY_COUNT(p1) },
|
|
{ p2, SK_ARRAY_COUNT(p2) },
|
|
{ p3, SK_ARRAY_COUNT(p3) },
|
|
{ p4, SK_ARRAY_COUNT(p4) },
|
|
{ p5, SK_ARRAY_COUNT(p5) },
|
|
{ p6, SK_ARRAY_COUNT(p6) },
|
|
{ p7, SK_ARRAY_COUNT(p7) }
|
|
};
|
|
|
|
SkASSERT(SK_ARRAY_COUNT(pgs) == kNumPolygons);
|
|
for (size_t pgIndex = 0; pgIndex < SK_ARRAY_COUNT(pgs); ++pgIndex) {
|
|
SkPathBuilder b;
|
|
b.moveTo(pgs[pgIndex].fPoints[0].fX,
|
|
pgs[pgIndex].fPoints[0].fY);
|
|
for (size_t ptIndex = 1; ptIndex < pgs[pgIndex].fPointNum; ++ptIndex) {
|
|
b.lineTo(pgs[pgIndex].fPoints[ptIndex].fX,
|
|
pgs[pgIndex].fPoints[ptIndex].fY);
|
|
}
|
|
b.close();
|
|
fPolygons.push_back(b.detach());
|
|
}
|
|
}
|
|
|
|
// Set the location for the current test on the canvas
|
|
static void SetLocation(SkCanvas* canvas, int counter, int lineNum) {
|
|
SkScalar x = SK_Scalar1 * kCellSize * (counter % lineNum) + 30 + SK_Scalar1 / 4;
|
|
SkScalar y = SK_Scalar1 * kCellSize * (counter / lineNum) + 30 + 3 * SK_Scalar1 / 4;
|
|
canvas->translate(x, y);
|
|
}
|
|
|
|
static void SetColorAndAlpha(SkPaint* paint, SkRandom* rand) {
|
|
SkColor color = rand->nextU();
|
|
color |= 0xff000000;
|
|
paint->setColor(color);
|
|
if (40 == paint->getStrokeWidth()) {
|
|
paint->setAlpha(0xA0);
|
|
}
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
// Stroke widths are:
|
|
// 0(may use hairline rendering), 10(common case for stroke-style)
|
|
// 40(>= geometry width/height, make the contour filled in fact)
|
|
constexpr int kStrokeWidths[] = {0, 10, 40};
|
|
SkASSERT(kNumStrokeWidths == SK_ARRAY_COUNT(kStrokeWidths));
|
|
|
|
constexpr SkPaint::Join kJoins[] = {
|
|
SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
|
|
};
|
|
SkASSERT(kNumJoins == SK_ARRAY_COUNT(kJoins));
|
|
|
|
int counter = 0;
|
|
SkPaint paint;
|
|
paint.setAntiAlias(true);
|
|
|
|
SkRandom rand;
|
|
// For stroke style painter
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
for (int join = 0; join < kNumJoins; ++join) {
|
|
for (int width = 0; width < kNumStrokeWidths; ++width) {
|
|
for (int i = 0; i < fPolygons.count(); ++i) {
|
|
canvas->save();
|
|
SetLocation(canvas, counter, fPolygons.count());
|
|
|
|
SetColorAndAlpha(&paint, &rand);
|
|
paint.setStrokeJoin(kJoins[join]);
|
|
paint.setStrokeWidth(SkIntToScalar(kStrokeWidths[width]));
|
|
|
|
canvas->drawPath(fPolygons[i], paint);
|
|
canvas->restore();
|
|
++counter;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For stroke-and-fill style painter and fill style painter
|
|
constexpr SkPaint::Style kStyles[] = {
|
|
SkPaint::kStrokeAndFill_Style, SkPaint::kFill_Style
|
|
};
|
|
SkASSERT(kNumExtraStyles == SK_ARRAY_COUNT(kStyles));
|
|
|
|
paint.setStrokeJoin(SkPaint::kMiter_Join);
|
|
paint.setStrokeWidth(SkIntToScalar(20));
|
|
for (int style = 0; style < kNumExtraStyles; ++style) {
|
|
paint.setStyle(kStyles[style]);
|
|
for (int i = 0; i < fPolygons.count(); ++i) {
|
|
canvas->save();
|
|
SetLocation(canvas, counter, fPolygons.count());
|
|
SetColorAndAlpha(&paint, &rand);
|
|
canvas->drawPath(fPolygons[i], paint);
|
|
canvas->restore();
|
|
++counter;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
static constexpr int kNumPolygons = 8;
|
|
static constexpr int kCellSize = 100;
|
|
static constexpr int kNumExtraStyles = 2;
|
|
static constexpr int kNumStrokeWidths = 3;
|
|
static constexpr int kNumJoins = 3;
|
|
|
|
SkTArray<SkPath> fPolygons;
|
|
using INHERITED = GM;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DEF_GM(return new PolygonsGM;)
|
|
|
|
// see crbug.com/1197461
|
|
DEF_SIMPLE_GM(conjoined_polygons, canvas, 400, 400) {
|
|
SkPathBuilder b;
|
|
b.moveTo(0.f, 120.f);
|
|
b.lineTo(0.f, 0.f);
|
|
b.lineTo(50.f, 330.f);
|
|
b.lineTo(90.f, 0.f);
|
|
b.lineTo(340.f, 0.f);
|
|
b.lineTo(90.f, 330.f);
|
|
b.lineTo(50.f, 330.f);
|
|
b.close();
|
|
|
|
SkPaint paint;
|
|
paint.setAntiAlias(true);
|
|
canvas->drawPath(b.detach(), paint);
|
|
}
|
|
|
|
} // namespace skiagm
|