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