blob: f3861a1e0b230131c2d47794d4cd17ae7fc6d6e6 [file] [log] [blame]
/*
* 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 "SkIntersections.h"
#include "SkOpContour.h"
#include "SkPathWriter.h"
#include "SkTSort.h"
void SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
const SkIntersections& ts, bool swap) {
SkCoincidence& coincidence = fCoincidences.push_back();
coincidence.fContours[0] = this; // FIXME: no need to store
coincidence.fContours[1] = other;
coincidence.fSegments[0] = index;
coincidence.fSegments[1] = otherIndex;
coincidence.fTs[swap][0] = ts[0][0];
coincidence.fTs[swap][1] = ts[0][1];
coincidence.fTs[!swap][0] = ts[1][0];
coincidence.fTs[!swap][1] = ts[1][1];
coincidence.fPts[0] = ts.pt(0).asSkPoint();
coincidence.fPts[1] = ts.pt(1).asSkPoint();
}
SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0);
for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
SkOpSegment* testSegment = fSortedSegments[sortedIndex];
if (testSegment->done()) {
continue;
}
*start = *end = 0;
while (testSegment->nextCandidate(start, end)) {
if (!testSegment->isVertical(*start, *end)) {
return testSegment;
}
}
}
return NULL;
}
// first pass, add missing T values
// second pass, determine winding values of overlaps
void SkOpContour::addCoincidentPoints() {
int count = fCoincidences.count();
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = fCoincidences[index];
SkASSERT(coincidence.fContours[0] == this);
int thisIndex = coincidence.fSegments[0];
SkOpSegment& thisOne = fSegments[thisIndex];
SkOpContour* otherContour = coincidence.fContours[1];
int otherIndex = coincidence.fSegments[1];
SkOpSegment& other = otherContour->fSegments[otherIndex];
if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
// OPTIMIZATION: remove from array
continue;
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs();
other.debugShowTs();
#endif
double startT = coincidence.fTs[0][0];
double endT = coincidence.fTs[0][1];
bool startSwapped, oStartSwapped, cancelers;
if ((cancelers = startSwapped = startT > endT)) {
SkTSwap(startT, endT);
}
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
if ((oStartSwapped = oStartT > oEndT)) {
SkTSwap(oStartT, oEndT);
cancelers ^= true;
}
SkASSERT(!approximately_negative(oEndT - oStartT));
if (cancelers) {
// make sure startT and endT have t entries
if (startT > 0 || oEndT < 1
|| thisOne.isMissing(startT) || other.isMissing(oEndT)) {
thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[startSwapped]);
}
if (oStartT > 0 || endT < 1
|| thisOne.isMissing(endT) || other.isMissing(oStartT)) {
other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[oStartSwapped]);
}
} else {
if (startT > 0 || oStartT > 0
|| thisOne.isMissing(startT) || other.isMissing(oStartT)) {
thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts[startSwapped]);
}
if (endT < 1 || oEndT < 1
|| thisOne.isMissing(endT) || other.isMissing(oEndT)) {
other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[!oStartSwapped]);
}
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs();
other.debugShowTs();
#endif
}
}
void SkOpContour::calcCoincidentWinding() {
int count = fCoincidences.count();
for (int index = 0; index < count; ++index) {
SkCoincidence& coincidence = fCoincidences[index];
SkASSERT(coincidence.fContours[0] == this);
int thisIndex = coincidence.fSegments[0];
SkOpSegment& thisOne = fSegments[thisIndex];
if (thisOne.done()) {
continue;
}
SkOpContour* otherContour = coincidence.fContours[1];
int otherIndex = coincidence.fSegments[1];
SkOpSegment& other = otherContour->fSegments[otherIndex];
if (other.done()) {
continue;
}
double startT = coincidence.fTs[0][0];
double endT = coincidence.fTs[0][1];
bool cancelers;
if ((cancelers = startT > endT)) {
SkTSwap<double>(startT, endT);
}
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
if (oStartT > oEndT) {
SkTSwap<double>(oStartT, oEndT);
cancelers ^= true;
}
SkASSERT(!approximately_negative(oEndT - oStartT));
if (cancelers) {
// make sure startT and endT have t entries
if (!thisOne.done() && !other.done()) {
thisOne.addTCancel(startT, endT, &other, oStartT, oEndT);
}
} else {
if (!thisOne.done() && !other.done()) {
thisOne.addTCoincident(startT, endT, &other, oStartT, oEndT);
}
}
#if DEBUG_CONCIDENT
thisOne.debugShowTs();
other.debugShowTs();
#endif
}
}
void SkOpContour::sortSegments() {
int segmentCount = fSegments.count();
fSortedSegments.push_back_n(segmentCount);
for (int test = 0; test < segmentCount; ++test) {
fSortedSegments[test] = &fSegments[test];
}
SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
fFirstSorted = 0;
}
void SkOpContour::toPath(SkPathWriter* path) const {
int segmentCount = fSegments.count();
const SkPoint& pt = fSegments.front().pts()[0];
path->deferredMove(pt);
for (int test = 0; test < segmentCount; ++test) {
fSegments[test].addCurveTo(0, 1, path, true);
}
path->close();
}
void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
SkOpSegment** topStart) {
int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0);
int sortedIndex = fFirstSorted;
fDone = true; // may be cleared below
for ( ; sortedIndex < segmentCount; ++sortedIndex) {
SkOpSegment* testSegment = fSortedSegments[sortedIndex];
if (testSegment->done()) {
if (sortedIndex == fFirstSorted) {
++fFirstSorted;
}
continue;
}
fDone = false;
SkPoint testXY = testSegment->activeLeftTop(true, NULL);
if (*topStart) {
if (testXY.fY < topLeft.fY) {
continue;
}
if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
continue;
}
if (bestXY->fY < testXY.fY) {
continue;
}
if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
continue;
}
}
*topStart = testSegment;
*bestXY = testXY;
}
}
SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
int segmentCount = fSegments.count();
for (int test = 0; test < segmentCount; ++test) {
SkOpSegment* testSegment = &fSegments[test];
if (testSegment->done()) {
continue;
}
testSegment->undoneSpan(start, end);
return testSegment;
}
return NULL;
}
#if DEBUG_SHOW_WINDING
int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
int count = fSegments.count();
int sum = 0;
for (int index = 0; index < count; ++index) {
sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
}
// SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
return sum;
}
static void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
// int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
// int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
int ofInterest = 1 << 5 | 1 << 8;
int total = 0;
int index;
for (index = 0; index < contourList.count(); ++index) {
total += contourList[index]->segments().count();
}
int sum = 0;
for (index = 0; index < contourList.count(); ++index) {
sum += contourList[index]->debugShowWindingValues(total, ofInterest);
}
// SkDebugf("%s total=%d\n", __FUNCTION__, sum);
}
#endif
void SkOpContour::setBounds() {
int count = fSegments.count();
if (count == 0) {
SkDebugf("%s empty contour\n", __FUNCTION__);
SkASSERT(0);
// FIXME: delete empty contour?
return;
}
fBounds = fSegments.front().bounds();
for (int index = 1; index < count; ++index) {
fBounds.add(fSegments[index].bounds());
}
}