pathops remove degenerate line on close

If first and last point are very nearly the same,
pathops considers the contour closed, but SkPath
adds a degenerate line to connect the two.

Change pathops to emit the first point rather than
a point very nearly the same as the last point
in a contour to avoid the degenerate line.

TBR=reed@google.com
Bug: skia:8249
Change-Id: Ibcc18643c78db4955c9eda9ca90219aad81d56d5
Reviewed-on: https://skia-review.googlesource.com/147720
Reviewed-by: Cary Clark <caryclark@skia.org>
Commit-Queue: Cary Clark <caryclark@skia.org>
Auto-Submit: Cary Clark <caryclark@skia.org>
This commit is contained in:
Cary Clark 2018-08-17 12:38:22 -04:00 committed by Skia Commit-Bot
parent f9c5063fc4
commit 98900b52ba
3 changed files with 46 additions and 11 deletions

View File

@ -32,21 +32,21 @@ void SkPathWriter::close() {
}
void SkPathWriter::conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight) {
this->update(pt2);
SkPoint pt2pt = this->update(pt2);
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.conicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g);\n",
pt1.fX, pt1.fY, pt2->fPt.fX, pt2->fPt.fY, weight);
pt1.fX, pt1.fY, pt2pt.fX, pt2pt.fY, weight);
#endif
fCurrent.conicTo(pt1, pt2->fPt, weight);
fCurrent.conicTo(pt1, pt2pt, weight);
}
void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3) {
this->update(pt3);
SkPoint pt3pt = this->update(pt3);
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3->fPt.fX, pt3->fPt.fY);
pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3pt.fX, pt3pt.fY);
#endif
fCurrent.cubicTo(pt1, pt2, pt3->fPt);
fCurrent.cubicTo(pt1, pt2, pt3pt);
}
bool SkPathWriter::deferredLine(const SkOpPtT* pt) {
@ -144,21 +144,28 @@ void SkPathWriter::moveTo() {
}
void SkPathWriter::quadTo(const SkPoint& pt1, const SkOpPtT* pt2) {
this->update(pt2);
SkPoint pt2pt = this->update(pt2);
#if DEBUG_PATH_CONSTRUCTION
SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
pt1.fX, pt1.fY, pt2->fPt.fX, pt2->fPt.fY);
pt1.fX, pt1.fY, pt2pt.fX, pt2pt.fY);
#endif
fCurrent.quadTo(pt1, pt2->fPt);
fCurrent.quadTo(pt1, pt2pt);
}
void SkPathWriter::update(const SkOpPtT* pt) {
// if last point to be written matches the current path's first point, alter the
// last to avoid writing a degenerate lineTo when the path is closed
SkPoint SkPathWriter::update(const SkOpPtT* pt) {
if (!fDefer[1]) {
this->moveTo();
} else if (!this->matchedLast(fDefer[0])) {
this->lineTo();
}
SkPoint result = pt->fPt;
if (fFirstPtT && result != fFirstPtT->fPt && fFirstPtT->contains(pt)) {
result = fFirstPtT->fPt;
}
fDefer[0] = fDefer[1] = pt; // set both to know that there is not a pending deferred line
return result;
}
bool SkPathWriter::someAssemblyRequired() {

View File

@ -41,7 +41,7 @@ private:
void moveTo();
const SkTArray<SkPath>& partials() const { return fPartials; }
bool someAssemblyRequired();
void update(const SkOpPtT* pt);
SkPoint update(const SkOpPtT* pt);
SkPath fCurrent; // contour under construction
SkTArray<SkPath> fPartials; // contours with mismatched starts and ends

View File

@ -9267,11 +9267,39 @@ path.close();
testSimplifyFail(reporter, path, filename);
}
static void bug8249(skiatest::Reporter* reporter, const char* filename) {
SkPath path;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(SkBits2Float(0x43310000), SkBits2Float(0x43810000)); // 177, 258
path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x43868000)); // 200, 269
path.cubicTo(SkBits2Float(0x43480000), SkBits2Float(0x43b20000), SkBits2Float(0x437a0000), SkBits2Float(0x43cd0000), SkBits2Float(0x43c80000), SkBits2Float(0x43cd0000)); // 200, 356, 250, 410, 400, 410
path.cubicTo(SkBits2Float(0x44098000), SkBits2Float(0x43cd0000), SkBits2Float(0x44160000), SkBits2Float(0x43b20000), SkBits2Float(0x44160000), SkBits2Float(0x43868000)); // 550, 410, 600, 356, 600, 269
path.lineTo(SkBits2Float(0x44160000), SkBits2Float(0x43808000)); // 600, 257
path.cubicTo(SkBits2Float(0x44160000), SkBits2Float(0x43330000), SkBits2Float(0x44110000), SkBits2Float(0x429c0000), SkBits2Float(0x43cd0000), SkBits2Float(0x429c0000)); // 600, 179, 580, 78, 410, 78
path.cubicTo(SkBits2Float(0x43700000), SkBits2Float(0x429c0000), SkBits2Float(0x43480000), SkBits2Float(0x431f0000), SkBits2Float(0x43480000), SkBits2Float(0x438a8000)); // 240, 78, 200, 159, 200, 277
path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x4401c000)); // 200, 519
path.cubicTo(SkBits2Float(0x43480000), SkBits2Float(0x441f0000), SkBits2Float(0x43660000), SkBits2Float(0x44340000), SkBits2Float(0x43c80000), SkBits2Float(0x44340000)); // 200, 636, 230, 720, 400, 720
path.cubicTo(SkBits2Float(0x4404c000), SkBits2Float(0x44340000), SkBits2Float(0x440d0000), SkBits2Float(0x442b8000), SkBits2Float(0x44118000), SkBits2Float(0x4416c000)); // 531, 720, 564, 686, 582, 603
path.lineTo(SkBits2Float(0x442cc000), SkBits2Float(0x441c8000)); // 691, 626
path.cubicTo(SkBits2Float(0x44260000), SkBits2Float(0x443d4000), SkBits2Float(0x44114000), SkBits2Float(0x444a8000), SkBits2Float(0x43c88000), SkBits2Float(0x444a8000)); // 664, 757, 581, 810, 401, 810
path.cubicTo(SkBits2Float(0x43350000), SkBits2Float(0x444a8000), SkBits2Float(0x42c80000), SkBits2Float(0x442e0000), SkBits2Float(0x42c80000), SkBits2Float(0x4401c000)); // 181, 810, 100, 696, 100, 519
path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x438a8000)); // 100, 277
path.cubicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42cc0000), SkBits2Float(0x433e0000), SkBits2Float(0xc1200000), SkBits2Float(0x43cd0000), SkBits2Float(0xc1200000)); // 100, 102, 190, -10, 410, -10
path.cubicTo(SkBits2Float(0x441d8000), SkBits2Float(0xc1200000), SkBits2Float(0x442f0000), SkBits2Float(0x42e60000), SkBits2Float(0x442f0000), SkBits2Float(0x437a0000)); // 630, -10, 700, 115, 700, 250
path.lineTo(SkBits2Float(0x442f0000), SkBits2Float(0x43880000)); // 700, 272
path.cubicTo(SkBits2Float(0x442f0000), SkBits2Float(0x43d18000), SkBits2Float(0x44164000), SkBits2Float(0x43fa0000), SkBits2Float(0x43c88000), SkBits2Float(0x43fa0000)); // 700, 419, 601, 500, 401, 500
path.cubicTo(SkBits2Float(0x43490000), SkBits2Float(0x43fa0000), SkBits2Float(0x43160000), SkBits2Float(0x43d00000), SkBits2Float(0x43160000), SkBits2Float(0x43868000)); // 201, 500, 150, 416, 150, 269
path.lineTo(SkBits2Float(0x43310000), SkBits2Float(0x43810000)); // 177, 258
path.close();
testSimplify(reporter, path, filename);
}
static void (*skipTest)(skiatest::Reporter* , const char* filename) = nullptr;
static void (*firstTest)(skiatest::Reporter* , const char* filename) = nullptr;
static void (*stopTest)(skiatest::Reporter* , const char* filename) = nullptr;
static TestDesc tests[] = {
TEST(bug8249),
TEST(grshapearc),
TEST(coincubics),
TEST(joel_16x),