add asserts to point<-->verb helpers

patch from issue 16153005

BUG=

Review URL: https://codereview.chromium.org/16195004

git-svn-id: http://skia.googlecode.com/svn/trunk@9344 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2013-05-30 14:57:55 +00:00
parent a5d3e77420
commit 7950a9eba7
26 changed files with 671 additions and 149 deletions

View File

@ -275,6 +275,12 @@ protected:
fPoints[(fCurrPoint + 1) & (kNumPoints - 1)]);
fCurrPoint += 2;
break;
case SkPath::kConic_Verb:
path->conicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
SK_ScalarHalf);
fCurrPoint += 2;
break;
case SkPath::kCubic_Verb:
path->cubicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],

View File

@ -230,7 +230,16 @@ struct SkConic {
void computeAsQuadError(SkVector* err) const;
bool asQuadTol(SkScalar tol) const;
/**
* return the power-of-2 number of quads needed to approximate this conic
* with a sequence of quads. Will be >= 0.
*/
int computeQuadPOW2(SkScalar tol) const;
/**
* Chop this conic into N quads, stored continguously in pts[], where
* N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
*/
int chopIntoQuadsPOW2(SkPoint pts[], int pow2) const;
bool findXExtrema(SkScalar* t) const;

View File

@ -413,6 +413,14 @@ public:
*/
void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar w);
void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
}
void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
SkScalar w);
/** Add a cubic bezier from the last point, approaching control points
(x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
made for this contour, the first point is automatically set to (0,0).
@ -779,7 +787,8 @@ public:
enum SegmentMask {
kLine_SegmentMask = 1 << 0,
kQuad_SegmentMask = 1 << 1,
kCubic_SegmentMask = 1 << 2
kConic_SegmentMask = 1 << 2,
kCubic_SegmentMask = 1 << 3,
};
/**
@ -793,9 +802,10 @@ public:
kMove_Verb, //!< iter.next returns 1 point
kLine_Verb, //!< iter.next returns 2 points
kQuad_Verb, //!< iter.next returns 3 points
kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight()
kCubic_Verb, //!< iter.next returns 4 points
kClose_Verb, //!< iter.next returns 1 point (contour's moveTo pt)
kDone_Verb //!< iter.next returns 0 points
kDone_Verb, //!< iter.next returns 0 points
};
/** Iterate through all of the segments (lines, quadratics, cubics) of
@ -829,6 +839,12 @@ public:
return this->doNext(pts);
}
/**
* Return the weight for the current conic. Only valid if the current
* segment return by next() was a conic.
*/
SkScalar conicWeight() const { return *fConicWeights; }
/** If next() returns kLine_Verb, then this query returns true if the
line was the result of a close() command (i.e. the end point is the
initial moveto for this contour). If next() returned a different
@ -848,6 +864,7 @@ public:
const SkPoint* fPts;
const uint8_t* fVerbs;
const uint8_t* fVerbStop;
const SkScalar* fConicWeights;
SkPoint fMoveTo;
SkPoint fLastPt;
SkBool8 fForceClose;
@ -879,10 +896,13 @@ public:
*/
Verb next(SkPoint pts[4]);
SkScalar conicWeight() const { return *fConicWeights; }
private:
const SkPoint* fPts;
const uint8_t* fVerbs;
const uint8_t* fVerbStop;
const SkScalar* fConicWeights;
SkPoint fMoveTo;
SkPoint fLastPt;
};
@ -922,7 +942,7 @@ private:
kIsOval_SerializationShift = 24, // requires 1 bit
kConvexity_SerializationShift = 16, // requires 2 bits
kFillType_SerializationShift = 8, // requires 2 bits
kSegmentMask_SerializationShift = 0 // requires 3 bits
kSegmentMask_SerializationShift = 0 // requires 4 bits
};
#if SK_DEBUG_PATH_REF

View File

@ -153,12 +153,22 @@ int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip,
return edgePtr - fEdgeList;
}
static void handle_quad(SkEdgeBuilder* builder, const SkPoint pts[3]) {
SkPoint monoX[5];
int n = SkChopQuadAtYExtrema(pts, monoX);
for (int i = 0; i <= n; i++) {
builder->addQuad(&monoX[i * 2]);
}
}
int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
int shiftUp) {
fAlloc.reset();
fList.reset();
fShiftUp = shiftUp;
SkScalar conicTol = SK_ScalarHalf * (1 << shiftUp);
if (SkPath::kLine_SegmentMask == path.getSegmentMasks()) {
return this->buildPoly(path, iclip, shiftUp);
}
@ -192,6 +202,24 @@ int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
this->addClipper(&clipper);
}
break;
case SkPath::kConic_Verb: {
const int MAX_POW2 = 4;
const int MAX_QUADS = 1 << MAX_POW2;
const int MAX_QUAD_PTS = 1 + 2 * MAX_QUADS;
SkPoint storage[MAX_QUAD_PTS];
SkConic conic;
conic.set(pts, iter.conicWeight());
int pow2 = conic.computeQuadPOW2(conicTol);
pow2 = SkMin32(pow2, MAX_POW2);
int quadCount = conic.chopIntoQuadsPOW2(storage, pow2);
SkASSERT(quadCount <= MAX_QUADS);
for (int i = 0; i < quadCount; ++i) {
if (clipper.clipQuad(&storage[i * 2], clip)) {
this->addClipper(&clipper);
}
}
} break;
case SkPath::kCubic_Verb:
if (clipper.clipCubic(pts, clip)) {
this->addClipper(&clipper);
@ -214,13 +242,26 @@ int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
this->addLine(pts);
break;
case SkPath::kQuad_Verb: {
SkPoint monoX[5];
int n = SkChopQuadAtYExtrema(pts, monoX);
for (int i = 0; i <= n; i++) {
this->addQuad(&monoX[i * 2]);
}
handle_quad(this, pts);
break;
}
case SkPath::kConic_Verb: {
const int MAX_POW2 = 4;
const int MAX_QUADS = 1 << MAX_POW2;
const int MAX_QUAD_PTS = 1 + 2 * MAX_QUADS;
SkPoint storage[MAX_QUAD_PTS];
SkConic conic;
conic.set(pts, iter.conicWeight());
int pow2 = conic.computeQuadPOW2(conicTol);
pow2 = SkMin32(pow2, MAX_POW2);
int quadCount = conic.chopIntoQuadsPOW2(storage, pow2);
SkASSERT(quadCount <= MAX_QUADS);
SkDebugf("--- quadCount = %d\n", quadCount);
for (int i = 0; i < quadCount; ++i) {
handle_quad(this, &storage[i * 2]);
}
} break;
case SkPath::kCubic_Verb: {
SkPoint monoY[10];
int n = SkChopCubicAtYExtrema(pts, monoY);

View File

@ -40,6 +40,7 @@ private:
int fShiftUp;
public:
void addLine(const SkPoint pts[]);
void addQuad(const SkPoint pts[]);
void addCubic(const SkPoint pts[]);

View File

@ -369,6 +369,7 @@ bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
SkASSERT(moveCnt);
break;
case kQuad_Verb:
case kConic_Verb:
SkASSERT(moveCnt);
nextPt = 2;
break;
@ -558,12 +559,16 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
break;
}
case kQuad_Verb:
case kConic_Verb:
case kCubic_Verb:
return false; // quadratic, cubic not allowed
case kMove_Verb:
last = *pts++;
closedOrMoved = true;
break;
default:
SkASSERT(!"unexpected verb");
break;
}
*currVerb += 1;
lastDirection = nextDirection;
@ -824,6 +829,39 @@ void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
}
void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar w) {
// check for <= 0 or NaN with this test
if (!(w > 0)) {
this->lineTo(x2, y2);
} else if (!SkScalarIsFinite(w)) {
this->lineTo(x1, y1);
this->lineTo(x2, y2);
} else if (SK_Scalar1 == w) {
this->quadTo(x1, y1, x2, y2);
} else {
SkDEBUGCODE(this->validate();)
this->injectMoveToIfNeeded();
SkPathRef::Editor ed(&fPathRef);
SkPoint* pts = ed.growForConic(w);
pts[0].set(x1, y1);
pts[1].set(x2, y2);
fSegmentMask |= kConic_SegmentMask;
GEN_ID_INC;
DIRTY_AFTER_EDIT;
}
}
void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
SkScalar w) {
SkPoint pt;
this->getLastPt(&pt);
this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
}
void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar x3, SkScalar y3) {
SkDEBUGCODE(this->validate();)
@ -857,6 +895,7 @@ void SkPath::close() {
switch (fPathRef->atVerb(count - 1)) {
case kLine_Verb:
case kQuad_Verb:
case kConic_Verb:
case kCubic_Verb:
case kMove_Verb: {
SkPathRef::Editor ed(&fPathRef);
@ -864,9 +903,12 @@ void SkPath::close() {
GEN_ID_INC;
break;
}
default:
case kClose_Verb:
// don't add a close if it's the first verb or a repeat
break;
default:
SkASSERT(!"unexpected verb");
break;
}
}
@ -1430,6 +1472,10 @@ void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
proc(matrix, &pts[1], &pts[1], 2);
this->quadTo(pts[1], pts[2]);
break;
case kConic_Verb:
proc(matrix, &pts[1], &pts[1], 2);
this->conicTo(pts[1], pts[2], iter.conicWeight());
break;
case kCubic_Verb:
proc(matrix, &pts[1], &pts[1], 3);
this->cubicTo(pts[1], pts[2], pts[3]);
@ -1445,14 +1491,20 @@ void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
///////////////////////////////////////////////////////////////////////////////
static const uint8_t gPtsInVerb[] = {
1, // kMove
1, // kLine
2, // kQuad
3, // kCubic
0, // kClose
0 // kDone
};
static int pts_in_verb(unsigned verb) {
static const uint8_t gPtsInVerb[] = {
1, // kMove
1, // kLine
2, // kQuad
2, // kConic
3, // kCubic
0, // kClose
0 // kDone
};
SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
return gPtsInVerb[verb];
}
// ignore the initial moveto, and stop when the 1st contour ends
void SkPath::pathTo(const SkPath& path) {
@ -1469,6 +1521,7 @@ void SkPath::pathTo(const SkPath& path) {
const uint8_t* verbs = path.fPathRef->verbs();
// skip the initial moveTo
const SkPoint* pts = path.fPathRef->points() + 1;
const SkScalar* conicWeight = path.fPathRef->conicWeights();
SkASSERT(verbs[~0] == kMove_Verb);
for (i = 1; i < vcount; i++) {
@ -1479,13 +1532,16 @@ void SkPath::pathTo(const SkPath& path) {
case kQuad_Verb:
this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
break;
case kConic_Verb:
this->conicTo(pts[0], pts[1], *conicWeight++);
break;
case kCubic_Verb:
this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
break;
case kClose_Verb:
return;
}
pts += gPtsInVerb[verbs[~i]];
pts += pts_in_verb(verbs[~i]);
}
}
@ -1503,14 +1559,17 @@ void SkPath::reversePathTo(const SkPath& path) {
const uint8_t* verbs = path.fPathRef->verbs();
const SkPoint* pts = path.fPathRef->points();
const SkScalar* conicWeights = path.fPathRef->conicWeights();
SkASSERT(verbs[~0] == kMove_Verb);
for (i = 1; i < vcount; ++i) {
int n = gPtsInVerb[verbs[~i]];
unsigned v = verbs[~i];
int n = pts_in_verb(v);
if (n == 0) {
break;
}
pts += n;
conicWeights += (SkPath::kConic_Verb == v);
}
while (--i > 0) {
@ -1521,6 +1580,9 @@ void SkPath::reversePathTo(const SkPath& path) {
case kQuad_Verb:
this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
break;
case kConic_Verb:
this->conicTo(pts[-1], pts[-2], *--conicWeights);
break;
case kCubic_Verb:
this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
pts[-3].fX, pts[-3].fY);
@ -1529,7 +1591,7 @@ void SkPath::reversePathTo(const SkPath& path) {
SkDEBUGFAIL("bad verb");
break;
}
pts -= gPtsInVerb[verbs[~i]];
pts -= pts_in_verb(verbs[~i]);
}
}
@ -1540,6 +1602,7 @@ void SkPath::reverseAddPath(const SkPath& src) {
// we will iterator through src's verbs backwards
const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
fIsOval = false;
@ -1547,7 +1610,7 @@ void SkPath::reverseAddPath(const SkPath& src) {
bool needClose = false;
while (verbs < verbsEnd) {
uint8_t v = *(verbs++);
int n = gPtsInVerb[v];
int n = pts_in_verb(v);
if (needMove) {
--pts;
@ -1570,6 +1633,9 @@ void SkPath::reverseAddPath(const SkPath& src) {
case kQuad_Verb:
this->quadTo(pts[1], pts[0]);
break;
case kConic_Verb:
this->conicTo(pts[1], pts[0], *--conicWeights);
break;
case kCubic_Verb:
this->cubicTo(pts[2], pts[1], pts[0]);
break;
@ -1644,6 +1710,10 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
case kQuad_Verb:
subdivide_quad_to(&tmp, pts);
break;
case kConic_Verb:
SkASSERT(!"TODO: compute new weight");
tmp.conicTo(pts[1], pts[2], iter.conicWeight());
break;
case kCubic_Verb:
subdivide_cubic_to(&tmp, pts);
break;
@ -1741,6 +1811,7 @@ enum SegmentState {
SkPath::Iter::Iter() {
#ifdef SK_DEBUG
fPts = NULL;
fConicWeights = NULL;
fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
fForceClose = fCloseLine = false;
fSegmentState = kEmptyContour_SegmentState;
@ -1759,6 +1830,7 @@ void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
fPts = path.fPathRef->points();
fVerbs = path.fPathRef->verbs();
fVerbStop = path.fPathRef->verbsMemBegin();
fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
fLastPt.fX = fLastPt.fY = 0;
fMoveTo.fX = fMoveTo.fY = 0;
fForceClose = SkToU8(forceClose);
@ -1870,6 +1942,7 @@ void SkPath::Iter::consumeDegenerateSegments() {
fPts++;
break;
case kConic_Verb:
case kQuad_Verb:
if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) {
if (lastMoveVerb) {
@ -1882,6 +1955,7 @@ void SkPath::Iter::consumeDegenerateSegments() {
// Ignore this line and continue
fVerbs--;
fPts += 2;
fConicWeights += (kConic_Verb == verb);
break;
case kCubic_Verb:
@ -1951,6 +2025,9 @@ SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
fCloseLine = false;
srcPts += 1;
break;
case kConic_Verb:
fConicWeights += 1;
// fall-through
case kQuad_Verb:
pts[0] = this->cons_moveTo();
memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
@ -1983,6 +2060,7 @@ SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
SkPath::RawIter::RawIter() {
#ifdef SK_DEBUG
fPts = NULL;
fConicWeights = NULL;
fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
#endif
// need to init enough to make next() harmlessly return kDone_Verb
@ -1998,6 +2076,7 @@ void SkPath::RawIter::setPath(const SkPath& path) {
fPts = path.fPathRef->points();
fVerbs = path.fPathRef->verbs();
fVerbStop = path.fPathRef->verbsMemBegin();
fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
fMoveTo.fX = fMoveTo.fY = 0;
fLastPt.fX = fLastPt.fY = 0;
}
@ -2025,6 +2104,9 @@ SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
fLastPt = srcPts[0];
srcPts += 1;
break;
case kConic_Verb:
fConicWeights += 1;
// fall-through
case kQuad_Verb:
pts[0] = fLastPt;
memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
@ -2057,22 +2139,12 @@ uint32_t SkPath::writeToMemory(void* storage) const {
if (NULL == storage) {
const int byteCount = sizeof(int32_t)
#if NEW_PICTURE_FORMAT
+ fPathRef->writeSize()
#else
+ 2 * sizeof(int32_t)
+ sizeof(SkPoint) * fPathRef->countPoints()
+ sizeof(uint8_t) * fPathRef->countVerbs()
#endif
+ sizeof(SkRect);
return SkAlign4(byteCount);
}
SkWBuffer buffer(storage);
#if !NEW_PICTURE_FORMAT
buffer.write32(fPathRef->countPoints());
buffer.write32(fPathRef->countVerbs());
#endif
// Call getBounds() to ensure (as a side-effect) that fBounds
// and fIsFinite are computed.
@ -2098,24 +2170,16 @@ uint32_t SkPath::writeToMemory(void* storage) const {
uint32_t SkPath::readFromMemory(const void* storage) {
SkRBuffer buffer(storage);
#if !NEW_PICTURE_FORMAT
int32_t pcount = buffer.readS32();
int32_t vcount = buffer.readS32();
#endif
uint32_t packed = buffer.readS32();
fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
fIsOval = (packed >> kIsOval_SerializationShift) & 1;
fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0x7;
fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
fDirection = (packed >> kDirection_SerializationShift) & 0x3;
#if NEW_PICTURE_FORMAT
fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer));
#else
fPathRef.reset(SkPathRef::CreateFromBuffer(vcount, pcount, &buffer));
#endif
buffer.read(&fBounds, sizeof(fBounds));
fBoundsIsDirty = false;
@ -2142,7 +2206,7 @@ static void append_scalar(SkString* str, SkScalar value) {
}
static void append_params(SkString* str, const char label[], const SkPoint pts[],
int count) {
int count, SkScalar conicWeight = -1) {
str->append(label);
str->append("(");
@ -2155,6 +2219,10 @@ static void append_params(SkString* str, const char label[], const SkPoint pts[]
str->append(", ");
}
}
if (conicWeight >= 0) {
str->append(", ");
append_scalar(str, conicWeight);
}
str->append(");\n");
}
@ -2180,6 +2248,9 @@ void SkPath::dump(bool forceClose, const char title[]) const {
case kQuad_Verb:
append_params(&builder, "path.quadTo", &pts[1], 2);
break;
case kConic_Verb:
append_params(&builder, "path.conicTo", &pts[1], 2, iter.conicWeight());
break;
case kCubic_Verb:
append_params(&builder, "path.cubicTo", &pts[1], 3);
break;
@ -2239,6 +2310,9 @@ void SkPath::validate() const {
case kQuad_Verb:
mask |= kQuad_SegmentMask;
break;
case kConic_Verb:
mask |= kConic_SegmentMask;
break;
case kCubic_Verb:
mask |= kCubic_SegmentMask;
case kMove_Verb: // these verbs aren't included in the segment mask.
@ -2379,6 +2453,7 @@ SkPath::Convexity SkPath::internalGetConvexity() const {
break;
case kLine_Verb: count = 1; break;
case kQuad_Verb: count = 2; break;
case kConic_Verb: count = 2; break;
case kCubic_Verb: count = 3; break;
case kClose_Verb:
state.close();
@ -2423,6 +2498,7 @@ private:
const SkPoint* fCurrPt;
const uint8_t* fCurrVerb;
const uint8_t* fStopVerbs;
const SkScalar* fCurrConicWeight;
bool fDone;
SkDEBUGCODE(int fContourCounter;)
};
@ -2432,6 +2508,7 @@ ContourIter::ContourIter(const SkPathRef& pathRef) {
fDone = false;
fCurrPt = pathRef.points();
fCurrVerb = pathRef.verbs();
fCurrConicWeight = pathRef.conicWeights();
fCurrPtCount = 0;
SkDEBUGCODE(fContourCounter = 0;)
this->next();
@ -2459,13 +2536,19 @@ void ContourIter::next() {
case SkPath::kLine_Verb:
ptCount += 1;
break;
case SkPath::kConic_Verb:
fCurrConicWeight += 1;
// fall-through
case SkPath::kQuad_Verb:
ptCount += 2;
break;
case SkPath::kCubic_Verb:
ptCount += 3;
break;
default: // kClose_Verb, just keep going
case SkPath::kClose_Verb:
break;
default:
SkASSERT(!"unexpected verb");
break;
}
}
@ -2957,13 +3040,16 @@ bool SkPath::contains(SkScalar x, SkScalar y) const {
case SkPath::kQuad_Verb:
w += winding_quad(pts, x, y);
break;
case SkPath::kConic_Verb:
SkASSERT(0);
break;
case SkPath::kCubic_Verb:
w += winding_cubic(pts, x, y);
break;
case SkPath::kDone_Verb:
done = true;
break;
}
}
} while (!done);
switch (this->getFillType()) {

View File

@ -152,6 +152,9 @@ void SkPathMeasure::buildSegments() {
bool done = false;
do {
switch (fIter.next(pts)) {
case SkPath::kConic_Verb:
SkASSERT(0);
break;
case SkPath::kMove_Verb:
ptIndex += 1;
fPts.append(1, pts);

View File

@ -12,12 +12,6 @@
#include "SkRefCnt.h"
#include <stddef.h> // ptrdiff_t
// When we're ready to break the picture format. Changes:
// * Write genID.
// * SkPathRef read/write counts (which will change the field order)
// * SkPathRef reads/writes verbs backwards.
#define NEW_PICTURE_FORMAT 0
/**
* Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
* modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
@ -109,6 +103,13 @@ public:
return fPathRef->growForVerb(verb);
}
SkPoint* growForConic(SkScalar w) {
fPathRef->validate();
SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb);
*fPathRef->fConicWeights.append() = w;
return pts;
}
/**
* Allocates space for additional verbs and points and returns pointers to the new verbs and
* points. verbs will point one beyond the first new verb (index it using [~<i>]). pts points
@ -131,8 +132,8 @@ public:
* Resets the path ref to a new verb and point count. The new verbs and points are
* uninitialized.
*/
void resetToSize(int newVerbCnt, int newPointCnt) {
fPathRef->resetToSize(newVerbCnt, newPointCnt);
void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) {
fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount);
}
/**
* Gets the path ref that is wrapped in the Editor.
@ -202,40 +203,29 @@ public:
} else if (rcnt > 1) {
dst->reset(SkNEW(SkPathRef));
}
(*dst)->resetToSize(src.fVerbCnt, src.fPointCnt);
(*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count());
memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t));
matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
(*dst)->fConicWeights = src.fConicWeights;
(*dst)->validate();
}
#if NEW_PICTURE_FORMAT
static SkPathRef* CreateFromBuffer(SkRBuffer* buffer) {
SkPathRef* ref = SkNEW(SkPathRef);
ref->fGenerationID = buffer->readU32();
int32_t verbCount = buffer->readS32();
int32_t pointCount = buffer->readS32();
ref->resetToSize(verbCount, pointCount);
int32_t conicCount = buffer->readS32();
ref->resetToSize(verbCount, pointCount, conicCount);
SkASSERT(verbCount == ref->countVerbs());
SkASSERT(pointCount == ref->countPoints());
SkASSERT(conicCount == ref->fConicWeights.count());
buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t));
buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar));
return ref;
}
#else
static SkPathRef* CreateFromBuffer(int verbCount, int pointCount, SkRBuffer* buffer) {
SkPathRef* ref = SkNEW(SkPathRef);
ref->resetToSize(verbCount, pointCount);
SkASSERT(verbCount == ref->countVerbs());
SkASSERT(pointCount == ref->countPoints());
buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
for (int i = 0; i < verbCount; ++i) {
ref->fVerbs[~i] = buffer->readU8();
}
return ref;
}
#endif
/**
* Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
@ -249,12 +239,13 @@ public:
(*pathRef)->fPointCnt = 0;
(*pathRef)->fFreeSpace = (*pathRef)->currSize();
(*pathRef)->fGenerationID = 0;
(*pathRef)->fConicWeights.rewind();
(*pathRef)->validate();
} else {
int oldVCnt = (*pathRef)->countVerbs();
int oldPCnt = (*pathRef)->countPoints();
pathRef->reset(SkNEW(SkPathRef));
(*pathRef)->resetToSize(0, 0, oldVCnt, oldPCnt);
(*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
}
}
@ -299,6 +290,9 @@ public:
*/
const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
const SkScalar* conicWeights() const { this->validate(); return fConicWeights.begin(); }
const SkScalar* conicWeightsEnd() const { this->validate(); return fConicWeights.end(); }
/**
* Convenience methods for getting to a verb or point by index.
*/
@ -337,6 +331,10 @@ public:
SkASSERT(!genIDMatch);
return false;
}
if (fConicWeights != ref.fConicWeights) {
SkASSERT(!genIDMatch);
return false;
}
// We've done the work to determine that these are equal. If either has a zero genID, copy
// the other's. If both are 0 then genID() will compute the next ID.
if (0 == fGenerationID) {
@ -350,7 +348,6 @@ public:
/**
* Writes the path points and verbs to a buffer.
*/
#if NEW_PICTURE_FORMAT
void writeToBuffer(SkWBuffer* buffer) {
this->validate();
SkDEBUGCODE_X(size_t beforePos = buffer->pos();)
@ -358,10 +355,12 @@ public:
// TODO: write gen ID here. Problem: We don't know if we're cross process or not from
// SkWBuffer. Until this is fixed we write 0.
buffer->write32(0);
buffer->write32(this->fVerbCnt);
buffer->write32(this->fPointCnt);
buffer->write(this->verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
buffer->write32(fVerbCnt);
buffer->write32(fPointCnt);
buffer->write32(fConicWeights.count());
buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
buffer->write(fConicWeights.begin(), fConicWeights.bytes());
SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
}
@ -370,17 +369,11 @@ public:
* Gets the number of bytes that would be written in writeBuffer()
*/
uint32_t writeSize() {
return 3 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint);
return 4 * sizeof(uint32_t) +
fVerbCnt * sizeof(uint8_t) +
fPointCnt * sizeof(SkPoint) +
fConicWeights.bytes();
}
#else
void writeToBuffer(SkWBuffer* buffer) {
this->validate();
buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
for (int i = 0; i < fVerbCnt; ++i) {
buffer->write8(fVerbs[~i]);
}
}
#endif
private:
SkPathRef() {
@ -396,10 +389,11 @@ private:
void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints) {
this->validate();
this->resetToSize(ref.fVerbCnt, ref.fPointCnt,
this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(),
additionalReserveVerbs, additionalReservePoints);
memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t));
memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
fConicWeights = ref.fConicWeights;
// We could call genID() here to force a real ID (instead of 0). However, if we're making
// a copy then presumably we intend to make a modification immediately afterwards.
fGenerationID = ref.fGenerationID;
@ -416,7 +410,8 @@ private:
/** Resets the path ref with verbCount verbs and pointCount points, all unitialized. Also
* allocates space for reserveVerb additional verbs and reservePoints additional points.*/
void resetToSize(int verbCount, int pointCount, int reserveVerbs = 0, int reservePoints = 0) {
void resetToSize(int verbCount, int pointCount, int conicCount,
int reserveVerbs = 0, int reservePoints = 0) {
this->validate();
fGenerationID = 0;
@ -442,6 +437,7 @@ private:
fVerbCnt = verbCount;
fFreeSpace = this->currSize() - minSize;
}
fConicWeights.setCount(conicCount);
this->validate();
}
@ -474,13 +470,17 @@ private:
case SkPath::kLine_Verb:
pCnt = 1;
break;
case SkPath::kConic_Verb:
case SkPath::kQuad_Verb:
pCnt = 2;
break;
case SkPath::kCubic_Verb:
pCnt = 3;
break;
default:
case SkPath::kDone_Verb:
SkASSERT(!"growForVerb called for kDone");
// fall through
case SkPath::kClose_Verb:
pCnt = 0;
}
size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
@ -588,6 +588,8 @@ private:
int fVerbCnt;
int fPointCnt;
size_t fFreeSpace; // redundant but saves computation
SkTDArray<SkScalar> fConicWeights;
enum {
kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
};

View File

@ -216,25 +216,36 @@ void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const {
*runs = SkRegion::kRunTypeSentinel;
}
static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
static unsigned verb_to_initial_last_index(unsigned verb) {
static const uint8_t gPathVerbToInitialLastIndex[] = {
0, // kMove_Verb
1, // kLine_Verb
2, // kQuad_Verb
2, // kConic_Verb
3, // kCubic_Verb
0, // kClose_Verb
0 // kDone_Verb
};
SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToInitialLastIndex));
return gPathVerbToInitialLastIndex[verb];
}
static unsigned verb_to_max_edges(unsigned verb) {
static const uint8_t gPathVerbToMaxEdges[] = {
0, // kMove_Verb
1, // kLine_Verb
2, // kQuad_VerbB
2, // kConic_VerbB
3, // kCubic_Verb
0, // kClose_Verb
0 // kDone_Verb
};
SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToMaxEdges));
return gPathVerbToMaxEdges[verb];
}
static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
SkPath::Iter iter(path, true);
SkPoint pts[4];
SkPath::Verb verb;
@ -244,9 +255,9 @@ static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
SkScalar bot = SkIntToScalar(SK_MinS16);
while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
maxEdges += gPathVerbToMaxEdges[verb];
maxEdges += verb_to_max_edges(verb);
int lastIndex = gPathVerbToInitialLastIndex[verb];
int lastIndex = verb_to_initial_last_index(verb);
if (lastIndex > 0) {
for (int i = 1; i <= lastIndex; i++) {
if (top > pts[i].fY) {

View File

@ -111,6 +111,9 @@ bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src,
}
dst->close();
break;
case SkPath::kConic_Verb:
SkASSERT(0);
break;
case SkPath::kDone_Verb:
goto DONE;
}

View File

@ -221,7 +221,10 @@ int generate_lines_and_quads(const SkPath& path,
GrPoint devPts[4];
SkPath::Verb verb = iter.next(pathPts);
switch (verb) {
case SkPath::kMove_Verb:
case SkPath::kConic_Verb:
SkASSERT(0);
break;
case SkPath::kMove_Verb:
break;
case SkPath::kLine_Verb:
m.mapPoints(devPts, pathPts, 2);

View File

@ -255,6 +255,9 @@ bool GrDefaultPathRenderer::createGeom(const SkPath& path,
for (;;) {
SkPath::Verb verb = iter.next(pts);
switch (verb) {
case SkPath::kConic_Verb:
SkASSERT(0);
break;
case SkPath::kMove_Verb:
if (!first) {
uint16_t currIdx = (uint16_t) (vert - base);

View File

@ -20,14 +20,15 @@ inline GrGLubyte verb_to_gl_path_cmd(const SkPath::Verb verb) {
GR_GL_MOVE_TO,
GR_GL_LINE_TO,
GR_GL_QUADRATIC_CURVE_TO,
0xFF, // conic
GR_GL_CUBIC_CURVE_TO,
GR_GL_CLOSE_PATH,
};
GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
GR_STATIC_ASSERT(3 == SkPath::kCubic_Verb);
GR_STATIC_ASSERT(4 == SkPath::kClose_Verb);
GR_STATIC_ASSERT(4 == SkPath::kCubic_Verb);
GR_STATIC_ASSERT(5 == SkPath::kClose_Verb);
GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
return gTable[verb];
@ -39,14 +40,15 @@ inline int num_pts(const SkPath::Verb verb) {
1, // move
1, // line
2, // quad
2, // conic
3, // cubic
0, // close
};
GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
GR_STATIC_ASSERT(3 == SkPath::kCubic_Verb);
GR_STATIC_ASSERT(4 == SkPath::kClose_Verb);
GR_STATIC_ASSERT(4 == SkPath::kCubic_Verb);
GR_STATIC_ASSERT(5 == SkPath::kClose_Verb);
GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
return gTable[verb];

View File

@ -113,11 +113,11 @@ bool SkOpAngle::operator<(const SkOpAngle& rh) const {
SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb;
ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
part[2] : part[1];
ray[1].fX = (part[0].fX + part[partVerb].fX) / 2;
ray[1].fY = (part[0].fY + part[partVerb].fY) / 2;
ray[1].fX = (part[0].fX + part[SkPathOpsVerbToPoints(partVerb)].fX) / 2;
ray[1].fY = (part[0].fY + part[SkPathOpsVerbToPoints(partVerb)].fY) / 2;
SkASSERT(ray[0] != ray[1]);
roots = (i.*CurveRay[fVerb])(fPts, ray);
rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray);
roots = (i.*CurveRay[SkPathOpsVerbToPoints(fVerb)])(fPts, ray);
rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rh.fVerb)])(rh.fPts, ray);
} while ((roots == 0 || rroots == 0) && (flip ^= true));
if (roots == 0 || rroots == 0) {
// FIXME: we don't have a solution in this case. The interim solution
@ -314,8 +314,8 @@ void SkOpAngle::setSpans() {
fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd;
#if DEBUG_UNSORTABLE
if (fUnsortable) {
SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT);
SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT);
SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, thisSpan.fT);
SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, nextSpan.fT);
SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
}
@ -330,8 +330,8 @@ void SkOpAngle::setSpans() {
}
#if 1
#if DEBUG_UNSORTABLE
SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT);
SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT);
SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, startT);
SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, endT);
SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
#endif

View File

@ -114,7 +114,7 @@ public:
const SkPoint& end() const {
const SkOpSegment& segment = fSegments.back();
return segment.pts()[segment.verb()];
return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
}
void findTooCloseToCall() {
@ -195,7 +195,7 @@ public:
int updateSegment(int index, const SkPoint* pts) {
SkOpSegment& segment = fSegments[index];
segment.updatePts(pts);
return segment.verb() + 1;
return SkPathOpsVerbToPoints(segment.verb()) + 1;
}
#if DEBUG_TEST

View File

@ -76,7 +76,7 @@ int SkOpEdgeBuilder::preFetch() {
if (verb == SkPath::kMove_Verb) {
*fPathPts.append() = pts[0];
} else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
fPathPts.append(verb, &pts[1]);
fPathPts.append(SkPathOpsVerbToPoints(verb), &pts[1]);
}
} while (verb != SkPath::kDone_Verb);
return fPathVerbs.count() - 1;
@ -137,7 +137,7 @@ bool SkOpEdgeBuilder::walk() {
if (reducedVerb == 0) {
break; // skip degenerate points
}
if (reducedVerb == 1) {
if (reducedVerb == SkPath::kLine_Verb) {
const SkPoint* lineStart = fReducePts.end() - 2;
*fExtra.append() = fCurrentContour->addLine(lineStart);
break;
@ -150,12 +150,12 @@ bool SkOpEdgeBuilder::walk() {
if (reducedVerb == 0) {
break; // skip degenerate points
}
if (reducedVerb == 1) {
if (reducedVerb == SkPath::kLine_Verb) {
const SkPoint* lineStart = fReducePts.end() - 2;
*fExtra.append() = fCurrentContour->addLine(lineStart);
break;
}
if (reducedVerb == 2) {
if (reducedVerb == SkPath::kQuad_Verb) {
const SkPoint* quadStart = fReducePts.end() - 3;
*fExtra.append() = fCurrentContour->addQuad(quadStart);
break;
@ -172,8 +172,8 @@ bool SkOpEdgeBuilder::walk() {
SkDEBUGFAIL("bad verb");
return false;
}
fFinalCurveStart = &pointsPtr[verb - 1];
pointsPtr += verb;
fFinalCurveStart = &pointsPtr[SkPathOpsVerbToPoints(verb) - 1];
pointsPtr += SkPathOpsVerbToPoints(verb);
SkASSERT(fCurrentContour);
}
if (fCurrentContour && !fAllowOpenContours && !close()) {

View File

@ -133,7 +133,7 @@ SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
}
}
if (fVerb != SkPath::kLine_Verb && !lastDone) {
SkPoint curveTop = (*CurveTop[fVerb])(fPts, lastT, span.fT);
SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
&& topPt.fX > curveTop.fX)) {
topPt = curveTop;
@ -210,9 +210,9 @@ void SkOpSegment::addAngle(SkTDArray<SkOpAngle>* anglesPtr, int start, int end)
#if DEBUG_ANGLE
SkTDArray<SkOpAngle>& angles = *anglesPtr;
if (angles.count() > 1 && !fTs[start].fTiny) {
SkPoint angle0Pt = (*CurvePointAtT[angles[0].verb()])(angles[0].pts(),
SkPoint angle0Pt = (*CurvePointAtT[SkPathOpsVerbToPoints(angles[0].verb())])(angles[0].pts(),
(*angles[0].spans())[angles[0].start()].fT);
SkPoint newPt = (*CurvePointAtT[fVerb])(fPts, fTs[start].fT);
SkPoint newPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[start].fT);
bool match = AlmostEqualUlps(angle0Pt.fX, newPt.fX);
match &= AlmostEqualUlps(angle0Pt.fY, newPt.fY);
if (!match) {
@ -354,7 +354,7 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
if (active) {
bool reverse = ePtr == fPts && start != 0;
if (reverse) {
path->deferredMoveLine(ePtr[fVerb]);
path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
switch (fVerb) {
case SkPath::kLine_Verb:
path->deferredLine(ePtr[0]);
@ -386,7 +386,7 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
}
}
}
// return ePtr[fVerb];
// return ePtr[SkPathOpsVerbToPoints(fVerb)];
}
void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
@ -979,7 +979,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi
SkIntersections intersections;
// OPTIMIZE: use specialty function that intersects ray with curve,
// returning t values only for curve (we don't care about t on ray)
int pts = (intersections.*CurveVertical[fVerb])(fPts, top, bottom, basePt.fX, false);
int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, top, bottom, basePt.fX, false);
if (pts == 0 || (current && pts == 1)) {
return bestTIndex;
}
@ -1003,7 +1003,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi
|| approximately_greater_than_one(foundT)) {
continue;
}
SkScalar testY = (*CurvePointAtT[fVerb])(fPts, foundT).fY;
SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
if (approximately_negative(testY - *bestY)
|| approximately_negative(basePt.fY - testY)) {
continue;
@ -1012,7 +1012,7 @@ int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hi
return SK_MinS32; // if the intersection is edge on, wait for another one
}
if (fVerb > SkPath::kLine_Verb) {
SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, foundT).fX;
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
if (approximately_zero(dx)) {
return SK_MinS32; // hit vertical, wait for another one
}
@ -1738,7 +1738,7 @@ the same winding is shared by both.
void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
int oppWind, SkScalar hitOppDx) {
SkASSERT(hitDx || !winding);
SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, tHit).fX;
SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
SkASSERT(dx);
int windVal = windValue(SkMin32(start, end));
#if DEBUG_WINDING_AT_T
@ -2081,7 +2081,7 @@ bool SkOpSegment::clockwise(int tStart, int tEnd) const {
SkASSERT(fVerb != SkPath::kLine_Verb);
SkPoint edge[4];
subDivide(tStart, tEnd, edge);
double sum = (edge[0].fX - edge[fVerb].fX) * (edge[0].fY + edge[fVerb].fY);
double sum = (edge[0].fX - edge[SkPathOpsVerbToPoints(fVerb)].fX) * (edge[0].fY + edge[SkPathOpsVerbToPoints(fVerb)].fY);
if (fVerb == SkPath::kCubic_Verb) {
SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
if (edge[1].fY < lesser && edge[2].fY < lesser) {
@ -2095,7 +2095,7 @@ bool SkOpSegment::clockwise(int tStart, int tEnd) const {
}
}
}
for (int idx = 0; idx < fVerb; ++idx){
for (int idx = 0; idx < SkPathOpsVerbToPoints(fVerb); ++idx){
sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
}
return sum <= 0;
@ -2365,9 +2365,9 @@ bool SkOpSegment::SortAngles(const SkTDArray<SkOpAngle>& angles,
void SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
edge[0] = fTs[start].fPt;
edge[fVerb] = fTs[end].fPt;
edge[SkPathOpsVerbToPoints(fVerb)] = fTs[end].fPt;
if (fVerb == SkPath::kQuad_Verb || fVerb == SkPath::kCubic_Verb) {
SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[fVerb].fX, edge[fVerb].fY }};
SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[SkPathOpsVerbToPoints(fVerb)].fX, edge[SkPathOpsVerbToPoints(fVerb)].fY }};
if (fVerb == SkPath::kQuad_Verb) {
edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], fTs[start].fT,
fTs[end].fT).asSkPoint();
@ -2382,7 +2382,7 @@ void SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
SkPoint edge[4];
subDivide(start, end, edge);
(bounds->*SetCurveBounds[fVerb])(edge);
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
}
bool SkOpSegment::tiny(const SkOpAngle* angle) const {
@ -2473,7 +2473,7 @@ int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx
SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
#endif
// see if a + change in T results in a +/- change in X (compute x'(T))
*dx = (*CurveSlopeAtT[fVerb])(fPts, tHit).fX;
*dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
*dx = fPts[2].fX - fPts[1].fX - *dx;
}
@ -2611,7 +2611,7 @@ void SkOpSegment::debugShowActiveSpans() const {
#endif
SkDebugf("%s id=%d", __FUNCTION__, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
const SkOpSpan* span = &fTs[i];
@ -2640,7 +2640,7 @@ void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int
const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
@ -2661,7 +2661,7 @@ void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int
const SkPoint& pt = xyAtT(&span);
SkDebugf("%s id=%d", fun, fID);
SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
}
SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
@ -2737,7 +2737,7 @@ void SkOpSegment::debugShowSort(const char* fun, const SkTDArray<SkOpAngle*>& an
angle.unsortable() ? "*** UNSORTABLE *** " : "");
#if COMPACT_DEBUG_SORT
SkDebugf("id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)",
segment.fID, kLVerbStr[segment.fVerb],
segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)],
start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
segment.xAtT(&eSpan), segment.yAtT(&eSpan));
#else

View File

@ -54,7 +54,7 @@ public:
}
SkVector dxdy(int index) const {
return (*CurveSlopeAtT[fVerb])(fPts, fTs[index].fT);
return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
}
SkScalar dy(int index) const {
@ -82,7 +82,7 @@ public:
}
bool isVertical(int start, int end) const {
return (*CurveIsVertical[fVerb])(fPts, start, end);
return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
}
bool operand() const {
@ -206,7 +206,7 @@ public:
// used only by right angle winding finding
SkPoint xyAtT(double mid) const {
return (*CurvePointAtT[fVerb])(fPts, mid);
return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
}
const SkPoint& xyAtT(int index) const {

View File

@ -7,10 +7,13 @@
#ifndef SkPathOpsTypes_DEFINED
#define SkPathOpsTypes_DEFINED
#define SK_CONIC_SUPPORT_ENABLED 1
#include <float.h> // for FLT_EPSILON
#include <math.h> // for fabs, sqrt
#include "SkFloatingPoint.h"
#include "SkPath.h"
#include "SkPathOps.h"
#include "SkPathOpsDebug.h"
#include "SkScalar.h"
@ -189,6 +192,47 @@ struct SkDTriangle;
struct SkDCubic;
struct SkDRect;
#if SK_CONIC_SUPPORT_ENABLED
inline SkPath::Verb SkPathOpsPointsToVerb(int points) {
int verb = (1 << points) >> 1;
#ifdef SK_DEBUG
switch (points) {
case 0: SkASSERT(SkPath::kMove_Verb == verb); break;
case 1: SkASSERT(SkPath::kLine_Verb == verb); break;
case 2: SkASSERT(SkPath::kQuad_Verb == verb); break;
case 3: SkASSERT(SkPath::kCubic_Verb == verb); break;
default: SkASSERT(!"should not be here");
}
#endif
return (SkPath::Verb)verb;
}
inline int SkPathOpsVerbToPoints(SkPath::Verb verb) {
int points = (int) verb - ((int) verb >> 2);
#ifdef SK_DEBUG
switch (verb) {
case SkPath::kLine_Verb: SkASSERT(1 == points); break;
case SkPath::kQuad_Verb: SkASSERT(2 == points); break;
case SkPath::kCubic_Verb: SkASSERT(3 == points); break;
default: SkASSERT(!"should not get here");
}
#endif
return points;
}
#else
inline SkPath::Verb SkOpPointsToVerb(int points) {
return (SkPath::Verb) (points);
}
inline SkPath::Verb SkOpVerbToPoints(SkPath::Verb verb) {
return (int) verb ;
}
#endif
inline double SkDInterp(double A, double B, double t) {
return A + (B - A) * t;
}

View File

@ -437,7 +437,7 @@ SkPath::Verb SkReduceOrder::Quad(const SkPoint a[3], SkTDArray<SkPoint>* reduceP
pt->fY = SkDoubleToScalar(reducer.fLine[index].fY);
}
}
return (SkPath::Verb) (order - 1);
return SkPathOpsPointsToVerb(order - 1);
}
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkTDArray<SkPoint>* reducePts) {
@ -452,5 +452,5 @@ SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkTDArray<SkPoint>* reduce
pt->fY = SkDoubleToScalar(reducer.fQuad[index].fY);
}
}
return (SkPath::Verb) (order - 1);
return SkPathOpsPointsToVerb(order - 1);
}

View File

@ -84,6 +84,9 @@ static void dumpVerbs(const SkPath& path, SkString* str) {
break;
case SkPath::kDone_Verb:
return;
case SkPath::kConic_Verb:
SkASSERT(0);
break;
}
}
}

View File

@ -221,7 +221,10 @@ void SkParsePath::ToSVGString(const SkPath& path, SkString* str) {
for (;;) {
switch (iter.next(pts, false)) {
case SkPath::kMove_Verb:
case SkPath::kConic_Verb:
SkASSERT(0);
break;
case SkPath::kMove_Verb:
append_scalars(&stream, 'M', &pts[0].fX, 2);
break;
case SkPath::kLine_Verb:

View File

@ -8,16 +8,38 @@
#include "Test.h"
static const SkPoint cubics[][4] = {
{{0, 1}, {2, 6}, {4, 2}, {5, 3}},
{{10, 234}, {10, 229.581726f}, {13.5817204f, 226}, {18, 226}},
/* 0 */ {{0, 1}, {2, 6}, {4, 2}, {5, 3}},
/* 1 */ {{10, 234}, {10, 229.581726f}, {13.5817204f, 226}, {18, 226}},
/* 2 */ {{132, 11419}, {130.89543151855469f, 11419}, {130, 11418.1044921875f}, {130, 11417}},
/* 3 */ {{130.04275512695312f, 11417.4130859375f}, {130.23307800292969f, 11418.3193359375f},
{131.03709411621094f, 11419}, {132, 11419}},
/* 4 */ {{0,1}, {0,5}, {4,1}, {6,4}},
/* 5 */ {{1,5}, {4,6}, {1,0}, {4,0}},
/* 6 */ {{0,1}, {0,4}, {5,1}, {6,4}},
/* 7 */ {{0,1}, {1,2}, {1,0}, {6,1}},
/* 8 */ {{0,3}, {0,1}, {2,0}, {1,0}},
/* 9 */ {{189,7}, {189,5.3431458473205566f}, {190.3431396484375f,4}, {192,4}},
/* 10 */ {{0,1}, {1,3}, {1,0}, {6,4}},
/* 11 */ {{0,1}, {2,3}, {2,1}, {4,3}},
/* 12 */ {{1,2}, {3,4}, {1,0}, {3,2}},
};
static const SkPoint quads[][3] = {
{{12.3423996f, 228.342407f}, {10, 230.686295f}, {10, 234}},
/* 0 */ {{12.3423996f, 228.342407f}, {10, 230.686295f}, {10, 234}},
/* 1 */ {{304.24319458007812f,591.75677490234375f}, {306,593.51470947265625f}, {306,596}},
};
static const SkPoint lines[][2] = {
{{6, 2}, {2, 4}},
/* 0 */ {{6, 2}, {2, 4}},
/* 1 */ {{306,617}, {306,590}},
/* 2 */ {{306,596}, {306,617}},
/* 3 */ {{6,4}, {0,1}},
/* 4 */ {{6,1}, {0,1}},
/* 5 */ {{1,0}, {0,3}},
/* 6 */ {{246,4}, {189,4}},
/* 7 */ {{192,4}, {243,4}},
/* 8 */ {{4,3}, {0,1}},
/* 9 */ {{3,2}, {1,2}},
};
struct SortSet {
@ -46,14 +68,86 @@ static const SortSet set3[] = {
{quads[0], 3, 1, 0},
};
static const SortSet set4[] = {
{cubics[2], 4, 0.812114222, 1},
{cubics[3], 4, 0.0684734759, 0},
};
static const SortSet set5[] = {
{lines[1], 2, 0.777777778, 1},
{quads[1], 3, 1, 4.34137342e-06},
{lines[2], 2, 0, 1},
};
static const SortSet set6[] = {
{lines[3], 2, 0.407407407, 0.554627832},
{cubics[4], 4, 0.666666667, 0.548022446},
{lines[3], 2, 0.407407407, 0},
{cubics[4], 4, 0.666666667, 1},
};
static const SortSet set7[] = {
{cubics[5], 4, 0.545233342, 0.545454545},
{cubics[6], 4, 0.484938134, 0.484805744},
{cubics[5], 4, 0.545233342, 0},
{cubics[6], 4, 0.484938134, 0.545454545},
};
static const SortSet set8[] = {
{cubics[7], 4, 0.5, 0.522986744 },
{lines[4], 2, 0.75, 1},
{cubics[7], 4, 0.5, 0},
{lines[4], 2, 0.75, 0.737654321},
};
static const SortSet set9[] = {
{cubics[8], 4, 0.4, 1},
{lines[5], 2, 0.36, 0},
{cubics[8], 4, 0.4, 0.394675838},
{lines[5], 2, 0.36, 0.363999782},
};
static const SortSet set10[] = {
{lines[6], 2, 0.947368421, 1},
{cubics[9], 4, 1, 0.500000357},
{lines[7], 2, 0, 1},
};
static const SortSet set11[] = {
{lines[3], 2, 0.75, 1},
{cubics[10], 4, 0.5, 0.228744269},
{lines[3], 2, 0.75, 0.627112191},
{cubics[10], 4, 0.5, 0.6339746},
};
static const SortSet set12[] = {
{cubics[12], 4, 0.5, 1},
{lines[8], 2, 0.5, 1},
{cubics[11], 4, 0.5, 0},
{lines[9], 2, 0.5, 1},
{cubics[12], 4, 0.5, 0},
{lines[8], 2, 0.5, 0},
{cubics[11], 4, 0.5, 1},
{lines[9], 2, 0.5, 0},
};
struct SortSetTests {
const SortSet* set;
size_t count;
};
static const SortSetTests tests[] = {
{ set3, SK_ARRAY_COUNT(set3) },
{ set12, SK_ARRAY_COUNT(set12) },
{ set11, SK_ARRAY_COUNT(set11) },
{ set10, SK_ARRAY_COUNT(set10) },
{ set9, SK_ARRAY_COUNT(set9) },
{ set8, SK_ARRAY_COUNT(set8) },
{ set7, SK_ARRAY_COUNT(set7) },
{ set6, SK_ARRAY_COUNT(set6) },
{ set2, SK_ARRAY_COUNT(set2) },
{ set5, SK_ARRAY_COUNT(set5) },
{ set4, SK_ARRAY_COUNT(set4) },
{ set3, SK_ARRAY_COUNT(set3) },
{ set1, SK_ARRAY_COUNT(set1) },
};
@ -122,16 +216,18 @@ static void PathOpsAngleTest(skiatest::Reporter* reporter) {
size_t idxG = idxL + 1;
setup(set, idxG, &greaterData, &greater, greaterTs);
SkOpAngle first, second;
first.set(lesserData, (SkPath::Verb) (set[idxL].ptCount - 1), &lesser,
first.set(lesserData, SkPathOpsPointsToVerb(set[idxL].ptCount - 1), &lesser,
lesserTs[0], lesserTs[1], lesser.spans());
second.set(greaterData, (SkPath::Verb) (set[idxG].ptCount - 1), &greater,
second.set(greaterData, SkPathOpsPointsToVerb(set[idxG].ptCount - 1), &greater,
greaterTs[0], greaterTs[1], greater.spans());
bool compare = first < second;
if (!compare) {
SkDebugf("%s test[%d]: lesser[%d] > greater[%d]\n", __FUNCTION__,
index, idxL, idxG);
compare = first < second;
}
REPORTER_ASSERT(reporter, compare);
reporter->bumpTestCount();
}
}
}

View File

@ -1144,6 +1144,7 @@ static void cubicOp69d(skiatest::Reporter* reporter) {
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
SkPathOp ops[] = {
kUnion_PathOp,
kXOR_PathOp,
@ -1343,9 +1344,175 @@ static void cubicOp70d(skiatest::Reporter* reporter) {
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
static void (*firstTest)(skiatest::Reporter* ) = 0;
static void cubicOp71d(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,5, 4,1, 6,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,4);
pathB.cubicTo(4,6, 1,0, 5,0);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
static void cubicOp72i(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,5, 5,2, 5,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,5);
pathB.cubicTo(4,5, 1,0, 5,0);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp);
}
static void cubicOp73d(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(3,4, 4,0, 6,4);
path.lineTo(0,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,4);
pathB.cubicTo(4,6, 1,0, 4,3);
pathB.lineTo(0,4);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
static void cubicOp75d(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(0,4, 5,1, 6,4);
path.lineTo(0,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,5);
pathB.cubicTo(4,6, 1,0, 4,0);
pathB.lineTo(1,5);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
static void cubicOp77i(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(0,1);
path.cubicTo(1,3, 2,0, 3,2);
path.lineTo(0,1);
path.close();
pathB.setFillType(SkPath::kEvenOdd_FillType);
pathB.moveTo(0,2);
pathB.cubicTo(2,3, 1,0, 3,1);
pathB.lineTo(0,2);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp);
}
static void cubicOp78u(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(1,6);
path.cubicTo(1,6, 5,0, 6,1);
path.lineTo(1,6);
path.close();
pathB.setFillType(SkPath::kEvenOdd_FillType);
pathB.moveTo(0,5);
pathB.cubicTo(1,6, 6,1, 6,1);
pathB.lineTo(0,5);
pathB.close();
testPathOp(reporter, path, pathB, kUnion_PathOp);
}
static void cubicOp79d(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,2);
path.cubicTo(0,1, 3,-0.1f, 1,0);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,3);
pathB.cubicTo(0,1, 2,0, 1,0);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
static void cubicOp79u(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(1,3, 1,0, 6,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(0,1);
pathB.cubicTo(4,6, 1,0, 3,1);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp);
}
static void cubicOp80i(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(2,3, 2,1, 4,3);
path.lineTo(0,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(1,2);
pathB.cubicTo(3,4, 1,0, 3,2);
pathB.lineTo(1,2);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp);
}
static void cubicOp82i(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kEvenOdd_FillType);
path.moveTo(0,1);
path.cubicTo(2,3, 5,2, 3,0);
path.lineTo(0,1);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(2,5);
pathB.cubicTo(0,3, 1,0, 3,2);
pathB.lineTo(2,5);
pathB.close();
testPathOp(reporter, path, pathB, kIntersect_PathOp);
}
static void cubicOp81d(skiatest::Reporter* reporter) {
SkPath path, pathB;
path.setFillType(SkPath::kWinding_FillType);
path.moveTo(0,1);
path.cubicTo(4,6, 4,3, 5,4);
path.close();
pathB.setFillType(SkPath::kWinding_FillType);
pathB.moveTo(3,4);
pathB.cubicTo(4,5, 1,0, 6,4);
pathB.close();
testPathOp(reporter, path, pathB, kDifference_PathOp);
}
static void (*firstTest)(skiatest::Reporter* ) = cubicOp81d;
static struct TestDesc tests[] = {
TEST(cubicOp82i),
TEST(cubicOp81d),
TEST(cubicOp80i),
TEST(cubicOp79u),
TEST(cubicOp79d),
TEST(cubicOp78u),
TEST(cubicOp77i),
TEST(cubicOp75d),
TEST(cubicOp73d),
TEST(cubicOp72i),
TEST(cubicOp71d),
TEST(skp5),
TEST(skp4),
TEST(skp3),

View File

@ -3772,7 +3772,7 @@ static void testQuad7(skiatest::Reporter* reporter) {
testSimplify(reporter, path);
}
static void (*firstTest)(skiatest::Reporter* ) = 0;
static void (*firstTest)(skiatest::Reporter* ) = testQuadratic85;
static TestDesc tests[] = {
TEST(testQuad7),

View File

@ -470,6 +470,9 @@ static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
case SkPath::kQuad_Verb:
REPORTER_ASSERT(reporter, !"unexpected quad verb");
break;
case SkPath::kConic_Verb:
REPORTER_ASSERT(reporter, !"unexpected conic verb");
break;
case SkPath::kCubic_Verb:
REPORTER_ASSERT(reporter, !"unexpected cubic verb");
break;
@ -1969,6 +1972,19 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
numPoints += 2;
lastWasClose = false;
break;
case SkPath::kConic_Verb:
if (!haveMoveTo) {
expectedPts[numPoints++] = lastMoveToPt;
expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
haveMoveTo = true;
}
expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
rand.nextUScalar1() * 4);
numPoints += 2;
lastWasClose = false;
break;
case SkPath::kCubic_Verb:
if (!haveMoveTo) {
expectedPts[numPoints++] = lastMoveToPt;
@ -1988,7 +2004,8 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
haveMoveTo = false;
lastWasClose = true;
break;
default:;
default:
SkASSERT(!"unexpected verb");
}
expectedVerbs[numIterVerbs++] = nextVerb;
}
@ -2019,6 +2036,7 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
numIterPts += 1;
break;
case SkPath::kQuad_Verb:
case SkPath::kConic_Verb:
REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
REPORTER_ASSERT(reporter, pts[0] == lastPt);
REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
@ -2039,7 +2057,8 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
lastPt = lastMoveTo;
break;
default:;
default:
SkASSERT(!"unexpected verb");
}
}
REPORTER_ASSERT(reporter, numIterPts == numPoints);