OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkBuffer.h" | 8 #include "SkBuffer.h" |
9 #include "SkPath.h" | 9 #include "SkPath.h" |
10 #include "SkPathRef.h" | 10 #include "SkPathRef.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 } else { | 21 } else { |
22 SkPathRef* copy = SkNEW(SkPathRef); | 22 SkPathRef* copy = SkNEW(SkPathRef); |
23 copy->copy(**pathRef, incReserveVerbs, incReservePoints); | 23 copy->copy(**pathRef, incReserveVerbs, incReservePoints); |
24 pathRef->reset(copy); | 24 pathRef->reset(copy); |
25 } | 25 } |
26 fPathRef = *pathRef; | 26 fPathRef = *pathRef; |
27 fPathRef->fGenerationID = 0; | 27 fPathRef->fGenerationID = 0; |
28 SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) | 28 SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) |
29 } | 29 } |
30 | 30 |
31 SkPoint* SkPathRef::Editor::growForConic(SkScalar w) { | |
32 SkDEBUGCODE(fPathRef->validate();) | |
33 SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb); | |
34 *fPathRef->fConicWeights.append() = w; | |
35 return pts; | |
36 } | |
37 | |
38 ////////////////////////////////////////////////////////////////////////////// | 31 ////////////////////////////////////////////////////////////////////////////// |
39 void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, | 32 void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, |
40 const SkPathRef& src, | 33 const SkPathRef& src, |
41 const SkMatrix& matrix) { | 34 const SkMatrix& matrix) { |
42 SkDEBUGCODE(src.validate();) | 35 SkDEBUGCODE(src.validate();) |
43 if (matrix.isIdentity()) { | 36 if (matrix.isIdentity()) { |
44 if (*dst != &src) { | 37 if (*dst != &src) { |
45 src.ref(); | 38 src.ref(); |
46 dst->reset(const_cast<SkPathRef*>(&src)); | 39 dst->reset(const_cast<SkPathRef*>(&src)); |
47 SkDEBUGCODE((*dst)->validate();) | 40 SkDEBUGCODE((*dst)->validate();) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 (*dst)->fBounds.setEmpty(); | 73 (*dst)->fBounds.setEmpty(); |
81 } | 74 } |
82 } else { | 75 } else { |
83 (*dst)->fIsFinite = false; | 76 (*dst)->fIsFinite = false; |
84 (*dst)->fBounds.setEmpty(); | 77 (*dst)->fBounds.setEmpty(); |
85 } | 78 } |
86 } else { | 79 } else { |
87 (*dst)->fBoundsIsDirty = true; | 80 (*dst)->fBoundsIsDirty = true; |
88 } | 81 } |
89 | 82 |
| 83 (*dst)->fLastMoveToIndex = src.fLastMoveToIndex; |
| 84 (*dst)->fSegmentMask = src.fSegmentMask; |
| 85 (*dst)->fConvexity = src.fConvexity; |
| 86 |
| 87 if (SkPathRef::kUnknown_Direction == src.fDirection) { |
| 88 (*dst)->fDirection = SkPathRef::kUnknown_Direction; |
| 89 } else { |
| 90 SkScalar det2x2 = |
| 91 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMS
caleY)) - |
| 92 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSk
ewY)); |
| 93 if (det2x2 < 0) { |
| 94 (*dst)->fDirection = SkPathRef::OppositeDirection(src.getDirection()
); |
| 95 } else if (det2x2 > 0) { |
| 96 (*dst)->fDirection = src.fDirection; |
| 97 } else { |
| 98 (*dst)->fDirection = SkPathRef::kUnknown_Direction; |
| 99 } |
| 100 } |
| 101 |
| 102 // It's an oval only if it stays a rect. |
| 103 (*dst)->fIsOval = src.fIsOval && matrix.rectStaysRect(); |
| 104 |
90 SkDEBUGCODE((*dst)->validate();) | 105 SkDEBUGCODE((*dst)->validate();) |
91 } | 106 } |
92 | 107 |
93 SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer | 108 SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer |
94 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 109 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
95 , bool newFormat, int32_t oldPacked | 110 , bool newFormat, int32_t oldPacked |
96 #endif | 111 #endif |
97 ) { | 112 ) { |
98 SkPathRef* ref = SkNEW(SkPathRef); | 113 SkPathRef* ref = SkNEW(SkPathRef); |
| 114 int32_t packed; |
99 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 115 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
100 if (newFormat) { | 116 if (newFormat) { |
101 #endif | 117 #endif |
102 int32_t packed = buffer->readU32(); | 118 packed = buffer->readU32(); |
103 | |
104 ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1; | |
105 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 119 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
106 } else { | |
107 ref->fIsFinite = (oldPacked >> SkPath::kOldIsFinite_SerializationShift)
& 1; | |
108 } | 120 } |
109 #endif | 121 #endif |
110 | 122 |
111 ref->fGenerationID = buffer->readU32(); | 123 ref->fGenerationID = buffer->readU32(); |
112 int32_t verbCount = buffer->readS32(); | 124 int32_t verbCount = buffer->readS32(); |
113 int32_t pointCount = buffer->readS32(); | 125 int32_t pointCount = buffer->readS32(); |
114 int32_t conicCount = buffer->readS32(); | 126 int32_t conicCount = buffer->readS32(); |
115 ref->resetToSize(verbCount, pointCount, conicCount); | 127 ref->resetToSize(verbCount, pointCount, conicCount); |
116 | 128 |
| 129 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
| 130 if (newFormat) { |
| 131 #endif |
| 132 ref->fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; |
| 133 ref->fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; |
| 134 ref->fIsOval = (packed >> kIsOval_SerializationShift) & 1; |
| 135 ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1; |
| 136 ref->fDirection = (packed >> kDirection_SerializationShift) & 0x3; |
| 137 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
| 138 } else { |
| 139 ref->fSegmentMask = (oldPacked >> SkPath::kOldSegmentMask_SerializationS
hift) & 0xF; |
| 140 ref->fConvexity = (oldPacked >> SkPath::kOldConvexity_SerializationShift
) & 0xFF; |
| 141 ref->fIsOval = (oldPacked >> SkPath::kOldIsOval_SerializationShift) & 1; |
| 142 ref->fIsFinite = (oldPacked >> SkPath::kOldIsFinite_SerializationShift)
& 1; |
| 143 ref->fDirection = (oldPacked >> SkPath::kOldDirection_SerializationShift
) & 0x3; |
| 144 } |
| 145 #endif |
| 146 |
117 SkASSERT(verbCount == ref->countVerbs()); | 147 SkASSERT(verbCount == ref->countVerbs()); |
118 SkASSERT(pointCount == ref->countPoints()); | 148 SkASSERT(pointCount == ref->countPoints()); |
119 SkASSERT(conicCount == ref->fConicWeights.count()); | 149 SkASSERT(conicCount == ref->fConicWeights.count()); |
120 buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)); | 150 buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)); |
121 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)); | 151 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)); |
122 buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)); | 152 buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)); |
123 buffer->read(&ref->fBounds, sizeof(SkRect)); | 153 buffer->read(&ref->fBounds, sizeof(SkRect)); |
124 ref->fBoundsIsDirty = false; | 154 ref->fBoundsIsDirty = false; |
| 155 |
| 156 #ifdef SK_DEBUG_PATH |
| 157 int lastMoveToIndex = kINITIAL_LASTMOVETOINDEX_VALUE; |
| 158 int pointCnt = 0; |
| 159 |
| 160 for (int i = 0; i < ref->fVerbCnt; ++i) { |
| 161 switch (ref->fVerbs[~i]) { |
| 162 case kMove_Verb: |
| 163 lastMoveToIndex = pointCnt; |
| 164 ++pointCnt; |
| 165 break; |
| 166 case kLine_Verb: |
| 167 ++pointCnt; |
| 168 break; |
| 169 case kQuad_Verb: |
| 170 pointCnt += 2; |
| 171 break; |
| 172 case kConic_Verb: |
| 173 pointCnt += 2; |
| 174 break; |
| 175 case kCubic_Verb: |
| 176 pointCnt += 3; |
| 177 break; |
| 178 case kClose_Verb: |
| 179 if (lastMoveToIndex >= 0) { |
| 180 lastMoveToIndex = ~lastMoveToIndex; |
| 181 } |
| 182 break; |
| 183 case kDone_Verb: |
| 184 // fall through |
| 185 default: |
| 186 SkDEBUGFAIL("default is not reached"); |
| 187 } |
| 188 } |
| 189 ref->fLastMoveToIndex = lastMoveToIndex; |
| 190 #endif |
| 191 |
125 return ref; | 192 return ref; |
126 } | 193 } |
127 | 194 |
128 void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) { | 195 void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) { |
129 if ((*pathRef)->unique()) { | 196 if ((*pathRef)->unique()) { |
130 SkDEBUGCODE((*pathRef)->validate();) | 197 SkDEBUGCODE((*pathRef)->validate();) |
131 (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite | 198 (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite |
132 (*pathRef)->fVerbCnt = 0; | 199 (*pathRef)->fVerbCnt = 0; |
133 (*pathRef)->fPointCnt = 0; | 200 (*pathRef)->fPointCnt = 0; |
134 (*pathRef)->fFreeSpace = (*pathRef)->currSize(); | 201 (*pathRef)->fFreeSpace = (*pathRef)->currSize(); |
135 (*pathRef)->fGenerationID = 0; | 202 (*pathRef)->fGenerationID = 0; |
136 (*pathRef)->fConicWeights.rewind(); | 203 (*pathRef)->fConicWeights.rewind(); |
| 204 (*pathRef)->fLastMoveToIndex = kINITIAL_LASTMOVETOINDEX_VALUE; |
| 205 (*pathRef)->fSegmentMask = 0; |
| 206 (*pathRef)->fConvexity = kUnknown_Convexity; |
| 207 (*pathRef)->fDirection = kUnknown_Direction; |
| 208 (*pathRef)->fIsOval = false; |
137 SkDEBUGCODE((*pathRef)->validate();) | 209 SkDEBUGCODE((*pathRef)->validate();) |
138 } else { | 210 } else { |
139 int oldVCnt = (*pathRef)->countVerbs(); | 211 int oldVCnt = (*pathRef)->countVerbs(); |
140 int oldPCnt = (*pathRef)->countPoints(); | 212 int oldPCnt = (*pathRef)->countPoints(); |
141 pathRef->reset(SkNEW(SkPathRef)); | 213 pathRef->reset(SkNEW(SkPathRef)); |
142 (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt); | 214 (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt); |
143 } | 215 } |
144 } | 216 } |
145 | 217 |
146 bool SkPathRef::operator== (const SkPathRef& ref) const { | 218 bool SkPathRef::operator== (const SkPathRef& ref) const { |
147 SkDEBUGCODE(this->validate();) | 219 SkDEBUGCODE(this->validate();) |
148 SkDEBUGCODE(ref.validate();) | 220 SkDEBUGCODE(ref.validate();) |
| 221 // note: don't need to look at isConvex or bounds, since just comparing the |
| 222 // raw data is sufficient. |
| 223 |
| 224 // We explicitly check fSegmentMask as a quick-reject. We could skip it, |
| 225 // since it is only a cache of info in the fVerbs, but its a fast way to |
| 226 // notice a difference |
| 227 if (fSegmentMask != ref.fSegmentMask) { |
| 228 return false; |
| 229 } |
149 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; | 230 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; |
150 #ifdef SK_RELEASE | 231 #ifdef SK_RELEASE |
151 if (genIDMatch) { | 232 if (genIDMatch) { |
152 return true; | 233 return true; |
153 } | 234 } |
154 #endif | 235 #endif |
155 if (fPointCnt != ref.fPointCnt || | 236 if (fPointCnt != ref.fPointCnt || |
156 fVerbCnt != ref.fVerbCnt) { | 237 fVerbCnt != ref.fVerbCnt) { |
157 SkASSERT(!genIDMatch); | 238 SkASSERT(!genIDMatch); |
158 return false; | 239 return false; |
(...skipping 25 matching lines...) Expand all Loading... |
184 } | 265 } |
185 | 266 |
186 void SkPathRef::writeToBuffer(SkWBuffer* buffer) { | 267 void SkPathRef::writeToBuffer(SkWBuffer* buffer) { |
187 SkDEBUGCODE(this->validate();) | 268 SkDEBUGCODE(this->validate();) |
188 SkDEBUGCODE(size_t beforePos = buffer->pos();) | 269 SkDEBUGCODE(size_t beforePos = buffer->pos();) |
189 | 270 |
190 // Call getBounds() to ensure (as a side-effect) that fBounds | 271 // Call getBounds() to ensure (as a side-effect) that fBounds |
191 // and fIsFinite are computed. | 272 // and fIsFinite are computed. |
192 const SkRect& bounds = this->getBounds(); | 273 const SkRect& bounds = this->getBounds(); |
193 | 274 |
194 int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift); | 275 int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | |
| 276 ((fIsOval & 1) << kIsOval_SerializationShift) |» |
| 277 (fConvexity << kConvexity_SerializationShift) |» |
| 278 (fSegmentMask << kSegmentMask_SerializationShift) |» |
| 279 (fDirection << kDirection_SerializationShift); |
195 buffer->write32(packed); | 280 buffer->write32(packed); |
196 | 281 |
197 // TODO: write gen ID here. Problem: We don't know if we're cross process or
not from | 282 // TODO: write gen ID here. Problem: We don't know if we're cross process or
not from |
198 // SkWBuffer. Until this is fixed we write 0. | 283 // SkWBuffer. Until this is fixed we write 0. |
199 buffer->write32(0); | 284 buffer->write32(0); |
200 buffer->write32(fVerbCnt); | 285 buffer->write32(fVerbCnt); |
201 buffer->write32(fPointCnt); | 286 buffer->write32(fPointCnt); |
202 buffer->write32(fConicWeights.count()); | 287 buffer->write32(fConicWeights.count()); |
203 buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t)); | 288 buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t)); |
204 buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); | 289 buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); |
(...skipping 21 matching lines...) Expand all Loading... |
226 memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); | 311 memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); |
227 fConicWeights = ref.fConicWeights; | 312 fConicWeights = ref.fConicWeights; |
228 // We could call genID() here to force a real ID (instead of 0). However, if
we're making | 313 // We could call genID() here to force a real ID (instead of 0). However, if
we're making |
229 // a copy then presumably we intend to make a modification immediately after
wards. | 314 // a copy then presumably we intend to make a modification immediately after
wards. |
230 fGenerationID = ref.fGenerationID; | 315 fGenerationID = ref.fGenerationID; |
231 fBoundsIsDirty = ref.fBoundsIsDirty; | 316 fBoundsIsDirty = ref.fBoundsIsDirty; |
232 if (!fBoundsIsDirty) { | 317 if (!fBoundsIsDirty) { |
233 fBounds = ref.fBounds; | 318 fBounds = ref.fBounds; |
234 fIsFinite = ref.fIsFinite; | 319 fIsFinite = ref.fIsFinite; |
235 } | 320 } |
| 321 fLastMoveToIndex = ref.fLastMoveToIndex; |
| 322 fSegmentMask = ref.fSegmentMask; |
| 323 fConvexity = ref.fConvexity; |
| 324 fDirection = ref.fDirection; |
| 325 fIsOval = ref.fIsOval; |
236 SkDEBUGCODE(this->validate();) | 326 SkDEBUGCODE(this->validate();) |
237 } | 327 } |
238 | 328 |
239 SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { | 329 // In some cases we need to inject a leading moveTo before we add points |
| 330 // for lineTo, quadTo, conicTo, cubicTo |
| 331 // |
| 332 // SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0) |
| 333 // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous mo
veTo) |
| 334 SkPoint* SkPathRef::growForVerb(Verb verb) { |
240 SkDEBUGCODE(this->validate();) | 335 SkDEBUGCODE(this->validate();) |
241 int pCnt; | 336 int pCnt; |
| 337 bool dirtyAfterEdit = true; |
| 338 bool moveInjectionNeeded = false; |
| 339 uint8_t newMask = 0; |
| 340 int newLastMoveToIndex = fLastMoveToIndex; |
| 341 |
242 switch (verb) { | 342 switch (verb) { |
243 case SkPath::kMove_Verb: | 343 case kMove_Verb: |
| 344 // remember the index |
| 345 newLastMoveToIndex = this->countPoints(); |
| 346 pCnt = 1; |
| 347 dirtyAfterEdit = false; |
| 348 break; |
| 349 case kLine_Verb: |
| 350 moveInjectionNeeded = fLastMoveToIndex < 0; |
| 351 newMask = kLine_SegmentMask; |
244 pCnt = 1; | 352 pCnt = 1; |
245 break; | 353 break; |
246 case SkPath::kLine_Verb: | 354 case kQuad_Verb: |
247 pCnt = 1; | 355 moveInjectionNeeded = fLastMoveToIndex < 0; |
248 break; | 356 newMask = kQuad_SegmentMask; |
249 case SkPath::kQuad_Verb: | |
250 // fall through | |
251 case SkPath::kConic_Verb: | |
252 pCnt = 2; | 357 pCnt = 2; |
253 break; | 358 break; |
254 case SkPath::kCubic_Verb: | 359 case kConic_Verb: |
| 360 moveInjectionNeeded = fLastMoveToIndex < 0; |
| 361 newMask = kConic_SegmentMask; |
| 362 pCnt = 2; |
| 363 break; |
| 364 case kCubic_Verb: |
| 365 moveInjectionNeeded = fLastMoveToIndex < 0; |
| 366 newMask = kCubic_SegmentMask; |
255 pCnt = 3; | 367 pCnt = 3; |
256 break; | 368 break; |
257 case SkPath::kClose_Verb: | 369 case kClose_Verb: |
| 370 // signal that we need a moveTo to follow us (unless we're done) |
| 371 newLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIn
dex) - 1); |
258 pCnt = 0; | 372 pCnt = 0; |
| 373 dirtyAfterEdit = false; |
259 break; | 374 break; |
260 case SkPath::kDone_Verb: | 375 case kDone_Verb: |
261 SkDEBUGFAIL("growForVerb called for kDone"); | 376 SkDEBUGFAIL("growForVerb called for kDone"); |
262 // fall through | 377 // fall through |
263 default: | 378 default: |
264 SkDEBUGFAIL("default is not reached"); | 379 SkDEBUGFAIL("default is not reached"); |
| 380 dirtyAfterEdit = false; |
265 pCnt = 0; | 381 pCnt = 0; |
266 } | 382 } |
267 size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint); | 383 size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint); |
| 384 if (moveInjectionNeeded) { |
| 385 space += sizeof(uint8_t) + sizeof(SkPoint); |
| 386 } |
268 this->makeSpace(space); | 387 this->makeSpace(space); |
269 this->fVerbs[~fVerbCnt] = verb; | 388 fLastMoveToIndex = newLastMoveToIndex; |
| 389 if (moveInjectionNeeded) { |
| 390 SkASSERT(dirtyAfterEdit); |
| 391 this->injectMove(); |
| 392 } else { |
| 393 fLastMoveToIndex = newLastMoveToIndex; |
| 394 } |
| 395 fVerbs[~fVerbCnt] = verb; |
| 396 fSegmentMask |= newMask; |
270 SkPoint* ret = fPoints + fPointCnt; | 397 SkPoint* ret = fPoints + fPointCnt; |
271 fVerbCnt += 1; | 398 fVerbCnt += 1; |
272 fPointCnt += pCnt; | 399 fPointCnt += pCnt; |
273 fFreeSpace -= space; | 400 fFreeSpace -= space; |
274 fBoundsIsDirty = true; // this also invalidates fIsFinite | 401 fBoundsIsDirty = true; // this also invalidates fIsFinite |
| 402 if (dirtyAfterEdit) { |
| 403 fConvexity = kUnknown_Convexity; |
| 404 fDirection = kUnknown_Direction; |
| 405 fIsOval = false; |
| 406 } |
| 407 |
275 SkDEBUGCODE(this->validate();) | 408 SkDEBUGCODE(this->validate();) |
276 return ret; | 409 return ret; |
277 } | 410 } |
278 | 411 |
279 int32_t SkPathRef::genID() const { | 412 int32_t SkPathRef::genID() const { |
280 SkASSERT(!fEditorsAttached); | 413 SkASSERT(!fEditorsAttached); |
281 if (!fGenerationID) { | 414 if (!fGenerationID) { |
282 if (0 == fPointCnt && 0 == fVerbCnt) { | 415 if (0 == fPointCnt && 0 == fVerbCnt) { |
283 fGenerationID = kEmptyGenID; | 416 fGenerationID = kEmptyGenID; |
284 } else { | 417 } else { |
(...skipping 14 matching lines...) Expand all Loading... |
299 SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0); | 432 SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0); |
300 SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPo
ints) >= 0); | 433 SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPo
ints) >= 0); |
301 SkASSERT((NULL == fPoints) == (NULL == fVerbs)); | 434 SkASSERT((NULL == fPoints) == (NULL == fVerbs)); |
302 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); | 435 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); |
303 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); | 436 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); |
304 SkASSERT(!(NULL == fPoints && fPointCnt)); | 437 SkASSERT(!(NULL == fPoints && fPointCnt)); |
305 SkASSERT(!(NULL == fVerbs && fVerbCnt)); | 438 SkASSERT(!(NULL == fVerbs && fVerbCnt)); |
306 SkASSERT(this->currSize() == | 439 SkASSERT(this->currSize() == |
307 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVe
rbCnt); | 440 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVe
rbCnt); |
308 | 441 |
309 #ifdef SK_DEBUG | |
310 if (!fBoundsIsDirty && !fBounds.isEmpty()) { | 442 if (!fBoundsIsDirty && !fBounds.isEmpty()) { |
311 bool isFinite = true; | 443 bool isFinite = true; |
312 for (int i = 0; i < fPointCnt; ++i) { | 444 for (int i = 0; i < fPointCnt; ++i) { |
313 SkASSERT(fPoints[i].fX >= fBounds.fLeft && fPoints[i].fX <= fBounds.
fRight && | 445 SkASSERT(fPoints[i].fX >= fBounds.fLeft && fPoints[i].fX <= fBounds.
fRight && |
314 fPoints[i].fY >= fBounds.fTop && fPoints[i].fY <= fBound
s.fBottom); | 446 fPoints[i].fY >= fBounds.fTop && fPoints[i].fY <= fBound
s.fBottom); |
315 if (!fPoints[i].isFinite()) { | 447 if (!fPoints[i].isFinite()) { |
316 isFinite = false; | 448 isFinite = false; |
317 } | 449 } |
318 } | 450 } |
319 SkASSERT(SkToBool(fIsFinite) == isFinite); | 451 SkASSERT(SkToBool(fIsFinite) == isFinite); |
320 } | 452 } |
| 453 |
| 454 #ifdef SK_DEBUG_PATH |
| 455 if (!fBoundsIsDirty) { |
| 456 SkRect bounds; |
| 457 |
| 458 bool isFinite = ComputePtBounds(&bounds, fPoints, fPointCnt); |
| 459 SkASSERT(SkToBool(fIsFinite) == isFinite); |
| 460 |
| 461 if (fPointCnt <= 1) { |
| 462 // if we're empty, fBounds may be empty but translated, so we can't |
| 463 // necessarily compare to bounds directly |
| 464 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will |
| 465 // be [2, 2, 2, 2] |
| 466 SkASSERT(bounds.isEmpty()); |
| 467 SkASSERT(fBounds.isEmpty()); |
| 468 } else { |
| 469 if (bounds.isEmpty()) { |
| 470 SkASSERT(fBounds.isEmpty()); |
| 471 } else { |
| 472 if (!fBounds.isEmpty()) { |
| 473 SkASSERT(fBounds.contains(bounds)); |
| 474 } |
| 475 } |
| 476 } |
| 477 } |
| 478 |
| 479 uint32_t mask = 0; |
| 480 int pointCnt = 0; |
| 481 int lastMoveToIndex = ~0; |
| 482 for (int i = 0; i < fVerbCnt; i++) { |
| 483 switch (fVerbs[~i]) { |
| 484 case kMove_Verb: |
| 485 lastMoveToIndex = pointCnt; |
| 486 ++pointCnt; |
| 487 break; |
| 488 case kLine_Verb: |
| 489 mask |= kLine_SegmentMask; |
| 490 ++pointCnt; |
| 491 break; |
| 492 case kQuad_Verb: |
| 493 mask |= kQuad_SegmentMask; |
| 494 pointCnt += 2; |
| 495 break; |
| 496 case kConic_Verb: |
| 497 mask |= kConic_SegmentMask; |
| 498 pointCnt += 2; |
| 499 break; |
| 500 case kCubic_Verb: |
| 501 mask |= kCubic_SegmentMask; |
| 502 pointCnt += 3; |
| 503 break; |
| 504 case kClose_Verb: |
| 505 if (lastMoveToIndex >= 0) { |
| 506 lastMoveToIndex = ~lastMoveToIndex; |
| 507 } |
| 508 break; |
| 509 case kDone_Verb: |
| 510 SkDEBUGFAIL("Done verb shouldn't be recorded."); |
| 511 break; |
| 512 default: |
| 513 SkDEBUGFAIL("Unknown Verb"); |
| 514 break; |
| 515 } |
| 516 } |
| 517 SkASSERT(mask == fSegmentMask); |
| 518 SkASSERT(lastMoveToIndex == fLastMoveToIndex); |
| 519 #endif // SK_DEBUG_PATH |
| 520 } |
321 #endif | 521 #endif |
| 522 |
| 523 /////////////////////////////////////////////////////////////////////////////// |
| 524 |
| 525 static int sign(SkScalar x) { return x < 0; } |
| 526 #define kValueNeverReturnedBySign 2 |
| 527 |
| 528 static int CrossProductSign(const SkVector& a, const SkVector& b) { |
| 529 return SkScalarSignAsInt(SkPoint::CrossProduct(a, b)); |
| 530 } |
| 531 |
| 532 // only valid for a single contour |
| 533 struct Convexicator { |
| 534 Convexicator() |
| 535 : fPtCount(0) |
| 536 , fConvexity(SkPathRef::kConvex_Convexity) |
| 537 , fDirection(SkPathRef::kUnknown_Direction) { |
| 538 fSign = 0; |
| 539 // warnings |
| 540 fCurrPt.set(0, 0); |
| 541 fVec0.set(0, 0); |
| 542 fVec1.set(0, 0); |
| 543 fFirstVec.set(0, 0); |
| 544 |
| 545 fDx = fDy = 0; |
| 546 fSx = fSy = kValueNeverReturnedBySign; |
| 547 } |
| 548 |
| 549 SkPathRef::Convexity getConvexity() const { return fConvexity; } |
| 550 |
| 551 /** The direction returned is only valid if the path is determined convex */ |
| 552 SkPathRef::Direction getDirection() const { return fDirection; } |
| 553 |
| 554 void addPt(const SkPoint& pt) { |
| 555 if (SkPathRef::kConcave_Convexity == fConvexity) { |
| 556 return; |
| 557 } |
| 558 |
| 559 if (0 == fPtCount) { |
| 560 fCurrPt = pt; |
| 561 ++fPtCount; |
| 562 } else { |
| 563 SkVector vec = pt - fCurrPt; |
| 564 if (vec.fX || vec.fY) { |
| 565 fCurrPt = pt; |
| 566 if (++fPtCount == 2) { |
| 567 fFirstVec = fVec1 = vec; |
| 568 } else { |
| 569 SkASSERT(fPtCount > 2); |
| 570 this->addVec(vec); |
| 571 } |
| 572 |
| 573 int sx = sign(vec.fX); |
| 574 int sy = sign(vec.fY); |
| 575 fDx += (sx != fSx); |
| 576 fDy += (sy != fSy); |
| 577 fSx = sx; |
| 578 fSy = sy; |
| 579 |
| 580 if (fDx > 3 || fDy > 3) { |
| 581 fConvexity = SkPathRef::kConcave_Convexity; |
| 582 } |
| 583 } |
| 584 } |
| 585 } |
| 586 |
| 587 void close() { |
| 588 if (fPtCount > 2) { |
| 589 this->addVec(fFirstVec); |
| 590 } |
| 591 } |
| 592 |
| 593 private: |
| 594 void addVec(const SkVector& vec) { |
| 595 SkASSERT(vec.fX || vec.fY); |
| 596 fVec0 = fVec1; |
| 597 fVec1 = vec; |
| 598 int sign = CrossProductSign(fVec0, fVec1); |
| 599 if (0 == fSign) { |
| 600 fSign = sign; |
| 601 if (1 == sign) { |
| 602 fDirection = SkPathRef::kCW_Direction; |
| 603 } else if (-1 == sign) { |
| 604 fDirection = SkPathRef::kCCW_Direction; |
| 605 } |
| 606 } else if (sign) { |
| 607 if (fSign != sign) { |
| 608 fConvexity = SkPathRef::kConcave_Convexity; |
| 609 fDirection = SkPathRef::kUnknown_Direction; |
| 610 } |
| 611 } |
| 612 } |
| 613 |
| 614 SkPoint fCurrPt; |
| 615 SkVector fVec0, fVec1, fFirstVec; |
| 616 int fPtCount; // non-degenerate points |
| 617 int fSign; |
| 618 SkPathRef::Convexity fConvexity; |
| 619 SkPathRef::Direction fDirection; |
| 620 int fDx, fDy, fSx, fSy; |
| 621 }; |
| 622 |
| 623 SkPathRef::Convexity SkPathRef::internalGetConvexity() const { |
| 624 SkASSERT(kUnknown_Convexity == fConvexity); |
| 625 SkPoint pts[4]; |
| 626 SkPath::Verb verb; |
| 627 SkPath::Iter iter(this, true); |
| 628 |
| 629 int contourCount = 0; |
| 630 int count; |
| 631 Convexicator state; |
| 632 |
| 633 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { |
| 634 switch (verb) { |
| 635 case SkPath::kMove_Verb: |
| 636 if (++contourCount > 1) { |
| 637 fConvexity = kConcave_Convexity; |
| 638 return kConcave_Convexity; |
| 639 } |
| 640 pts[1] = pts[0]; |
| 641 count = 1; |
| 642 break; |
| 643 case SkPath::kLine_Verb: count = 1; break; |
| 644 case SkPath::kQuad_Verb: count = 2; break; |
| 645 case SkPath::kConic_Verb: count = 2; break; |
| 646 case SkPath::kCubic_Verb: count = 3; break; |
| 647 case SkPath::kClose_Verb: |
| 648 state.close(); |
| 649 count = 0; |
| 650 break; |
| 651 default: |
| 652 SkDEBUGFAIL("bad verb"); |
| 653 fConvexity = kConcave_Convexity; |
| 654 return kConcave_Convexity; |
| 655 } |
| 656 |
| 657 for (int i = 1; i <= count; i++) { |
| 658 state.addPt(pts[i]); |
| 659 } |
| 660 // early exit |
| 661 if (kConcave_Convexity == state.getConvexity()) { |
| 662 fConvexity = kConcave_Convexity; |
| 663 return kConcave_Convexity; |
| 664 } |
| 665 } |
| 666 fConvexity = state.getConvexity(); |
| 667 if (kConvex_Convexity == fConvexity && kUnknown_Direction == fDirection) { |
| 668 fDirection = state.getDirection(); |
| 669 } |
| 670 return static_cast<Convexity>(fConvexity); |
| 671 } |
| 672 |
| 673 class ContourIter { |
| 674 public: |
| 675 ContourIter(const SkPathRef& pathRef); |
| 676 |
| 677 bool done() const { return fDone; } |
| 678 // if !done() then these may be called |
| 679 int count() const { return fCurrPtCount; } |
| 680 const SkPoint* pts() const { return fCurrPt; } |
| 681 void next(); |
| 682 |
| 683 private: |
| 684 int fCurrPtCount; |
| 685 const SkPoint* fCurrPt; |
| 686 const uint8_t* fCurrVerb; |
| 687 const uint8_t* fStopVerbs; |
| 688 const SkScalar* fCurrConicWeight; |
| 689 bool fDone; |
| 690 SkDEBUGCODE(int fContourCounter;) |
| 691 }; |
| 692 |
| 693 ContourIter::ContourIter(const SkPathRef& pathRef) { |
| 694 fStopVerbs = pathRef.verbsMemBegin(); |
| 695 fDone = false; |
| 696 fCurrPt = pathRef.points(); |
| 697 fCurrVerb = pathRef.verbs(); |
| 698 fCurrConicWeight = pathRef.conicWeights(); |
| 699 fCurrPtCount = 0; |
| 700 SkDEBUGCODE(fContourCounter = 0;) |
| 701 this->next(); |
| 702 } |
| 703 |
| 704 void ContourIter::next() { |
| 705 if (fCurrVerb <= fStopVerbs) { |
| 706 fDone = true; |
| 707 } |
| 708 if (fDone) { |
| 709 return; |
| 710 } |
| 711 |
| 712 // skip pts of prev contour |
| 713 fCurrPt += fCurrPtCount; |
| 714 |
| 715 SkASSERT(SkPathRef::kMove_Verb == fCurrVerb[~0]); |
| 716 int ptCount = 1; // moveTo |
| 717 const uint8_t* verbs = fCurrVerb; |
| 718 |
| 719 for (--verbs; verbs > fStopVerbs; --verbs) { |
| 720 switch (verbs[~0]) { |
| 721 case SkPathRef::kMove_Verb: |
| 722 goto CONTOUR_END; |
| 723 case SkPathRef::kLine_Verb: |
| 724 ptCount += 1; |
| 725 break; |
| 726 case SkPathRef::kConic_Verb: |
| 727 fCurrConicWeight += 1; |
| 728 // fall-through |
| 729 case SkPathRef::kQuad_Verb: |
| 730 ptCount += 2; |
| 731 break; |
| 732 case SkPathRef::kCubic_Verb: |
| 733 ptCount += 3; |
| 734 break; |
| 735 case SkPathRef::kClose_Verb: |
| 736 break; |
| 737 default: |
| 738 SkDEBUGFAIL("unexpected verb"); |
| 739 break; |
| 740 } |
| 741 } |
| 742 CONTOUR_END: |
| 743 fCurrPtCount = ptCount; |
| 744 fCurrVerb = verbs; |
| 745 SkDEBUGCODE(++fContourCounter;) |
| 746 } |
| 747 |
| 748 // returns cross product of (p1 - p0) and (p2 - p0) |
| 749 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint&
p2) { |
| 750 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); |
| 751 // We may get 0 when the above subtracts underflow. We expect this to be |
| 752 // very rare and lazily promote to double. |
| 753 if (0 == cross) { |
| 754 double p0x = SkScalarToDouble(p0.fX); |
| 755 double p0y = SkScalarToDouble(p0.fY); |
| 756 |
| 757 double p1x = SkScalarToDouble(p1.fX); |
| 758 double p1y = SkScalarToDouble(p1.fY); |
| 759 |
| 760 double p2x = SkScalarToDouble(p2.fX); |
| 761 double p2y = SkScalarToDouble(p2.fY); |
| 762 |
| 763 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - |
| 764 (p1y - p0y) * (p2x - p0x)); |
| 765 |
| 766 } |
| 767 return cross; |
| 768 } |
| 769 |
| 770 // Returns the first pt with the maximum Y coordinate |
| 771 static int find_max_y(const SkPoint pts[], int count) { |
| 772 SkASSERT(count > 0); |
| 773 SkScalar max = pts[0].fY; |
| 774 int firstIndex = 0; |
| 775 for (int i = 1; i < count; ++i) { |
| 776 SkScalar y = pts[i].fY; |
| 777 if (y > max) { |
| 778 max = y; |
| 779 firstIndex = i; |
| 780 } |
| 781 } |
| 782 return firstIndex; |
| 783 } |
| 784 |
| 785 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) { |
| 786 int i = index; |
| 787 for (;;) { |
| 788 i = (i + inc) % n; |
| 789 if (i == index) { // we wrapped around, so abort |
| 790 break; |
| 791 } |
| 792 if (pts[index] != pts[i]) { // found a different point, success! |
| 793 break; |
| 794 } |
| 795 } |
| 796 return i; |
| 797 } |
| 798 |
| 799 /** |
| 800 * Starting at index, and moving forward (incrementing), find the xmin and |
| 801 * xmax of the contiguous points that have the same Y. |
| 802 */ |
| 803 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n, |
| 804 int* maxIndexPtr) { |
| 805 const SkScalar y = pts[index].fY; |
| 806 SkScalar min = pts[index].fX; |
| 807 SkScalar max = min; |
| 808 int minIndex = index; |
| 809 int maxIndex = index; |
| 810 for (int i = index + 1; i < n; ++i) { |
| 811 if (pts[i].fY != y) { |
| 812 break; |
| 813 } |
| 814 SkScalar x = pts[i].fX; |
| 815 if (x < min) { |
| 816 min = x; |
| 817 minIndex = i; |
| 818 } else if (x > max) { |
| 819 max = x; |
| 820 maxIndex = i; |
| 821 } |
| 822 } |
| 823 *maxIndexPtr = maxIndex; |
| 824 return minIndex; |
| 825 } |
| 826 |
| 827 static void crossToDir(SkScalar cross, SkPathRef::Direction* dir) { |
| 828 if (dir) { |
| 829 *dir = cross > 0 ? SkPathRef::kCW_Direction : SkPathRef::kCCW_Direction; |
| 830 } |
| 831 } |
| 832 |
| 833 #if 0 |
| 834 #include "SkString.h" |
| 835 #include "../utils/SkParsePath.h" |
| 836 static void dumpPath(const SkPath& path) { |
| 837 SkString str; |
| 838 SkParsePath::ToSVGString(path, &str); |
| 839 SkDebugf("%s\n", str.c_str()); |
322 } | 840 } |
323 #endif | 841 #endif |
| 842 |
| 843 namespace { |
| 844 // for use with convex_dir_test |
| 845 double mul(double a, double b) { return a * b; } |
| 846 SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); } |
| 847 double toDouble(SkScalar a) { return SkScalarToDouble(a); } |
| 848 SkScalar toScalar(SkScalar a) { return a; } |
| 849 |
| 850 // determines the winding direction of a convex polygon with the precision |
| 851 // of T. CAST_SCALAR casts an SkScalar to T. |
| 852 template <typename T, T (CAST_SCALAR)(SkScalar)> |
| 853 bool convex_dir_test(int n, const SkPoint pts[], SkPathRef::Direction* dir) { |
| 854 // we find the first three points that form a non-degenerate |
| 855 // triangle. If there are no such points then the path is |
| 856 // degenerate. The first is always point 0. Now we find the second |
| 857 // point. |
| 858 int i = 0; |
| 859 enum { kX = 0, kY = 1 }; |
| 860 T v0[2]; |
| 861 while (1) { |
| 862 v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX); |
| 863 v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY); |
| 864 if (v0[kX] || v0[kY]) { |
| 865 break; |
| 866 } |
| 867 if (++i == n - 1) { |
| 868 return false; |
| 869 } |
| 870 } |
| 871 // now find a third point that is not colinear with the first two |
| 872 // points and check the orientation of the triangle (which will be |
| 873 // the same as the orientation of the path). |
| 874 for (++i; i < n; ++i) { |
| 875 T v1[2]; |
| 876 v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX); |
| 877 v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY); |
| 878 T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]); |
| 879 if (0 != cross) { |
| 880 *dir = cross > 0 ? SkPathRef::kCW_Direction : SkPathRef::kCCW_Direct
ion; |
| 881 return true; |
| 882 } |
| 883 } |
| 884 return false; |
| 885 } |
| 886 } |
| 887 |
| 888 /* |
| 889 * We loop through all contours, and keep the computed cross-product of the |
| 890 * contour that contained the global y-max. If we just look at the first |
| 891 * contour, we may find one that is wound the opposite way (correctly) since |
| 892 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour |
| 893 * that is outer most (or at least has the global y-max) before we can consider |
| 894 * its cross product. |
| 895 */ |
| 896 bool SkPathRef::cheapComputeDirection(Direction* dir) const { |
| 897 // dumpPath(*this); |
| 898 // don't want to pay the cost for computing this if it |
| 899 // is unknown, so we don't call isConvex() |
| 900 |
| 901 if (kUnknown_Direction != fDirection) { |
| 902 *dir = static_cast<Direction>(fDirection); |
| 903 return true; |
| 904 } |
| 905 const SkPathRef::Convexity conv = this->getConvexityOrUnknown(); |
| 906 |
| 907 ContourIter iter(*this); |
| 908 |
| 909 // initialize with our logical y-min |
| 910 SkScalar ymax = this->getBounds().fTop; |
| 911 SkScalar ymaxCross = 0; |
| 912 |
| 913 for (; !iter.done(); iter.next()) { |
| 914 int n = iter.count(); |
| 915 if (n < 3) { |
| 916 continue; |
| 917 } |
| 918 |
| 919 const SkPoint* pts = iter.pts(); |
| 920 SkScalar cross = 0; |
| 921 if (SkPathRef::kConvex_Convexity == conv) { |
| 922 // We try first at scalar precision, and then again at double |
| 923 // precision. This is because the vectors computed between distant |
| 924 // points may lose too much precision. |
| 925 if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) { |
| 926 fDirection = *dir; |
| 927 return true; |
| 928 } |
| 929 if (convex_dir_test<double, toDouble>(n, pts, dir)) { |
| 930 fDirection = *dir; |
| 931 return true; |
| 932 } else { |
| 933 return false; |
| 934 } |
| 935 } else { |
| 936 int index = find_max_y(pts, n); |
| 937 if (pts[index].fY < ymax) { |
| 938 continue; |
| 939 } |
| 940 |
| 941 // If there is more than 1 distinct point at the y-max, we take the |
| 942 // x-min and x-max of them and just subtract to compute the dir. |
| 943 if (pts[(index + 1) % n].fY == pts[index].fY) { |
| 944 int maxIndex; |
| 945 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); |
| 946 if (minIndex == maxIndex) { |
| 947 goto TRY_CROSSPROD; |
| 948 } |
| 949 SkASSERT(pts[minIndex].fY == pts[index].fY); |
| 950 SkASSERT(pts[maxIndex].fY == pts[index].fY); |
| 951 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); |
| 952 // we just subtract the indices, and let that auto-convert to |
| 953 // SkScalar, since we just want - or + to signal the direction. |
| 954 cross = minIndex - maxIndex; |
| 955 } else { |
| 956 TRY_CROSSPROD: |
| 957 // Find a next and prev index to use for the cross-product test, |
| 958 // but we try to find pts that form non-zero vectors from pts[in
dex] |
| 959 // |
| 960 // Its possible that we can't find two non-degenerate vectors, s
o |
| 961 // we have to guard our search (e.g. all the pts could be in the |
| 962 // same place). |
| 963 |
| 964 // we pass n - 1 instead of -1 so we don't foul up % operator by |
| 965 // passing it a negative LH argument. |
| 966 int prev = find_diff_pt(pts, index, n, n - 1); |
| 967 if (prev == index) { |
| 968 // completely degenerate, skip to next contour |
| 969 continue; |
| 970 } |
| 971 int next = find_diff_pt(pts, index, n, 1); |
| 972 SkASSERT(next != index); |
| 973 cross = cross_prod(pts[prev], pts[index], pts[next]); |
| 974 // if we get a zero and the points are horizontal, then we look
at the spread in |
| 975 // x-direction. We really should continue to walk away from the
degeneracy until |
| 976 // there is a divergence. |
| 977 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY
== pts[index].fY) { |
| 978 // construct the subtract so we get the correct Direction be
low |
| 979 cross = pts[index].fX - pts[next].fX; |
| 980 } |
| 981 } |
| 982 |
| 983 if (cross) { |
| 984 // record our best guess so far |
| 985 ymax = pts[index].fY; |
| 986 ymaxCross = cross; |
| 987 } |
| 988 } |
| 989 } |
| 990 if (ymaxCross) { |
| 991 crossToDir(ymaxCross, dir); |
| 992 fDirection = *dir; |
| 993 return true; |
| 994 } else { |
| 995 return false; |
| 996 } |
| 997 } |
| 998 |
OLD | NEW |