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 SkPoint* SkPathRef::Editor::growForVerb(int /*SkPath::Verb*/ verb) { | 14 ////////////////////////////////////////////////////////////////////////////// |
15 fPathRef->validate(); | 15 SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef, |
16 return fPathRef->growForVerb(verb); | 16 int incReserveVerbs, |
| 17 int incReservePoints) |
| 18 { |
| 19 if ((*pathRef)->unique()) { |
| 20 (*pathRef)->incReserve(incReserveVerbs, incReservePoints); |
| 21 } else { |
| 22 SkPathRef* copy = SkNEW(SkPathRef); |
| 23 copy->copy(**pathRef, incReserveVerbs, incReservePoints); |
| 24 pathRef->reset(copy); |
| 25 } |
| 26 fPathRef = *pathRef; |
| 27 fPathRef->fGenerationID = 0; |
| 28 SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) |
17 } | 29 } |
18 | 30 |
19 SkPoint* SkPathRef::Editor::growForConic(SkScalar w) { | 31 SkPoint* SkPathRef::Editor::growForConic(SkScalar w) { |
20 fPathRef->validate(); | 32 fPathRef->validate(); |
21 SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb); | 33 SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb); |
22 *fPathRef->fConicWeights.append() = w; | 34 *fPathRef->fConicWeights.append() = w; |
23 return pts; | 35 return pts; |
24 } | 36 } |
25 | 37 |
26 SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { | 38 void SkPathRef::Editor::grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint*
* pts) { |
27 this->validate(); | 39 SkASSERT(NULL != verbs); |
28 int pCnt; | 40 SkASSERT(NULL != pts); |
29 switch (verb) { | 41 fPathRef->validate(); |
30 case SkPath::kMove_Verb: | 42 int oldVerbCnt = fPathRef->fVerbCnt; |
31 pCnt = 1; | 43 int oldPointCnt = fPathRef->fPointCnt; |
32 break; | 44 SkASSERT(verbs && pts); |
33 case SkPath::kLine_Verb: | 45 fPathRef->grow(newVerbs, newPts); |
34 pCnt = 1; | 46 *verbs = fPathRef->fVerbs - oldVerbCnt; |
35 break; | 47 *pts = fPathRef->fPoints + oldPointCnt; |
36 case SkPath::kQuad_Verb: | 48 fPathRef->validate(); |
37 // fall through | 49 } |
38 case SkPath::kConic_Verb: | 50 |
39 pCnt = 2; | 51 ////////////////////////////////////////////////////////////////////////////// |
40 break; | 52 void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, |
41 case SkPath::kCubic_Verb: | 53 const SkPathRef& src, |
42 pCnt = 3; | 54 const SkMatrix& matrix) { |
43 break; | 55 src.validate(); |
44 case SkPath::kClose_Verb: | 56 if (matrix.isIdentity()) { |
45 pCnt = 0; | 57 if (*dst != &src) { |
46 break; | 58 src.ref(); |
47 case SkPath::kDone_Verb: | 59 dst->reset(const_cast<SkPathRef*>(&src)); |
48 SkDEBUGFAIL("growForVerb called for kDone"); | 60 (*dst)->validate(); |
49 // fall through | 61 } |
50 default: | 62 return; |
51 SkDEBUGFAIL("default is not reached"); | |
52 pCnt = 0; | |
53 } | 63 } |
54 size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint); | 64 |
55 this->makeSpace(space); | 65 bool dstUnique = (*dst)->unique(); |
56 this->fVerbs[~fVerbCnt] = verb; | 66 if (!dstUnique) { |
57 SkPoint* ret = fPoints + fPointCnt; | 67 dst->reset(SkNEW(SkPathRef)); |
58 fVerbCnt += 1; | 68 (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count
()); |
59 fPointCnt += pCnt; | 69 memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * s
izeof(uint8_t)); |
60 fFreeSpace -= space; | 70 (*dst)->fConicWeights = src.fConicWeights; |
61 fBoundsIsDirty = true; // this also invalidates fIsFinite | 71 } |
62 this->validate(); | 72 |
63 return ret; | 73 // Need to check this here in case (&src == dst) |
| 74 bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.c
ountPoints() > 1; |
| 75 |
| 76 matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt); |
| 77 |
| 78 /* |
| 79 * Here we optimize the bounds computation, by noting if the bounds are |
| 80 * already known, and if so, we just transform those as well and mark |
| 81 * them as "known", rather than force the transformed path to have to |
| 82 * recompute them. |
| 83 * |
| 84 * Special gotchas if the path is effectively empty (<= 1 point) or |
| 85 * if it is non-finite. In those cases bounds need to stay empty, |
| 86 * regardless of the matrix. |
| 87 */ |
| 88 if (canXformBounds) { |
| 89 (*dst)->fBoundsIsDirty = false; |
| 90 if (src.fIsFinite) { |
| 91 matrix.mapRect(&(*dst)->fBounds, src.fBounds); |
| 92 if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) { |
| 93 (*dst)->fBounds.setEmpty(); |
| 94 } |
| 95 } else { |
| 96 (*dst)->fIsFinite = false; |
| 97 (*dst)->fBounds.setEmpty(); |
| 98 } |
| 99 } else { |
| 100 (*dst)->fBoundsIsDirty = true; |
| 101 } |
| 102 |
| 103 (*dst)->validate(); |
64 } | 104 } |
65 | 105 |
66 SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer | 106 SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer |
67 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 107 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
68 , bool newFormat, int32_t oldPacked | 108 , bool newFormat, int32_t oldPacked |
69 #endif | 109 #endif |
70 ) { | 110 ) { |
71 SkPathRef* ref = SkNEW(SkPathRef); | 111 SkPathRef* ref = SkNEW(SkPathRef); |
72 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 112 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
73 if (newFormat) { | 113 if (newFormat) { |
(...skipping 17 matching lines...) Expand all Loading... |
91 SkASSERT(pointCount == ref->countPoints()); | 131 SkASSERT(pointCount == ref->countPoints()); |
92 SkASSERT(conicCount == ref->fConicWeights.count()); | 132 SkASSERT(conicCount == ref->fConicWeights.count()); |
93 buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)); | 133 buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)); |
94 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)); | 134 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)); |
95 buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)); | 135 buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)); |
96 buffer->read(&ref->fBounds, sizeof(SkRect)); | 136 buffer->read(&ref->fBounds, sizeof(SkRect)); |
97 ref->fBoundsIsDirty = false; | 137 ref->fBoundsIsDirty = false; |
98 return ref; | 138 return ref; |
99 } | 139 } |
100 | 140 |
101 /** | 141 void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) { |
102 * Writes the path points and verbs to a buffer. | 142 if ((*pathRef)->unique()) { |
103 */ | 143 (*pathRef)->validate(); |
| 144 (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite |
| 145 (*pathRef)->fVerbCnt = 0; |
| 146 (*pathRef)->fPointCnt = 0; |
| 147 (*pathRef)->fFreeSpace = (*pathRef)->currSize(); |
| 148 (*pathRef)->fGenerationID = 0; |
| 149 (*pathRef)->fConicWeights.rewind(); |
| 150 (*pathRef)->validate(); |
| 151 } else { |
| 152 int oldVCnt = (*pathRef)->countVerbs(); |
| 153 int oldPCnt = (*pathRef)->countPoints(); |
| 154 pathRef->reset(SkNEW(SkPathRef)); |
| 155 (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt); |
| 156 } |
| 157 } |
| 158 |
| 159 bool SkPathRef::operator== (const SkPathRef& ref) const { |
| 160 this->validate(); |
| 161 ref.validate(); |
| 162 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; |
| 163 #ifdef SK_RELEASE |
| 164 if (genIDMatch) { |
| 165 return true; |
| 166 } |
| 167 #endif |
| 168 if (fPointCnt != ref.fPointCnt || |
| 169 fVerbCnt != ref.fVerbCnt) { |
| 170 SkASSERT(!genIDMatch); |
| 171 return false; |
| 172 } |
| 173 if (0 != memcmp(this->verbsMemBegin(), |
| 174 ref.verbsMemBegin(), |
| 175 ref.fVerbCnt * sizeof(uint8_t))) { |
| 176 SkASSERT(!genIDMatch); |
| 177 return false; |
| 178 } |
| 179 if (0 != memcmp(this->points(), |
| 180 ref.points(), |
| 181 ref.fPointCnt * sizeof(SkPoint))) { |
| 182 SkASSERT(!genIDMatch); |
| 183 return false; |
| 184 } |
| 185 if (fConicWeights != ref.fConicWeights) { |
| 186 SkASSERT(!genIDMatch); |
| 187 return false; |
| 188 } |
| 189 // We've done the work to determine that these are equal. If either has a ze
ro genID, copy |
| 190 // the other's. If both are 0 then genID() will compute the next ID. |
| 191 if (0 == fGenerationID) { |
| 192 fGenerationID = ref.genID(); |
| 193 } else if (0 == ref.fGenerationID) { |
| 194 ref.fGenerationID = this->genID(); |
| 195 } |
| 196 return true; |
| 197 } |
| 198 |
104 void SkPathRef::writeToBuffer(SkWBuffer* buffer) { | 199 void SkPathRef::writeToBuffer(SkWBuffer* buffer) { |
105 this->validate(); | 200 this->validate(); |
106 SkDEBUGCODE(size_t beforePos = buffer->pos();) | 201 SkDEBUGCODE(size_t beforePos = buffer->pos();) |
107 | 202 |
108 // Call getBounds() to ensure (as a side-effect) that fBounds | 203 // Call getBounds() to ensure (as a side-effect) that fBounds |
109 // and fIsFinite are computed. | 204 // and fIsFinite are computed. |
110 const SkRect& bounds = this->getBounds(); | 205 const SkRect& bounds = this->getBounds(); |
111 | 206 |
112 int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift); | 207 int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift); |
113 buffer->write32(packed); | 208 buffer->write32(packed); |
114 | 209 |
115 // TODO: write gen ID here. Problem: We don't know if we're cross process or
not from | 210 // TODO: write gen ID here. Problem: We don't know if we're cross process or
not from |
116 // SkWBuffer. Until this is fixed we write 0. | 211 // SkWBuffer. Until this is fixed we write 0. |
117 buffer->write32(0); | 212 buffer->write32(0); |
118 buffer->write32(fVerbCnt); | 213 buffer->write32(fVerbCnt); |
119 buffer->write32(fPointCnt); | 214 buffer->write32(fPointCnt); |
120 buffer->write32(fConicWeights.count()); | 215 buffer->write32(fConicWeights.count()); |
121 buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t)); | 216 buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t)); |
122 buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); | 217 buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); |
123 buffer->write(fConicWeights.begin(), fConicWeights.bytes()); | 218 buffer->write(fConicWeights.begin(), fConicWeights.bytes()); |
124 buffer->write(&bounds, sizeof(bounds)); | 219 buffer->write(&bounds, sizeof(bounds)); |
125 | 220 |
126 SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize()); | 221 SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize()); |
127 } | 222 } |
| 223 |
| 224 uint32_t SkPathRef::writeSize() { |
| 225 return uint32_t(5 * sizeof(uint32_t) + |
| 226 fVerbCnt * sizeof(uint8_t) + |
| 227 fPointCnt * sizeof(SkPoint) + |
| 228 fConicWeights.bytes() + |
| 229 sizeof(SkRect)); |
| 230 } |
| 231 |
| 232 void SkPathRef::copy(const SkPathRef& ref, |
| 233 int additionalReserveVerbs, |
| 234 int additionalReservePoints) { |
| 235 this->validate(); |
| 236 this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(), |
| 237 additionalReserveVerbs, additionalReservePoints); |
| 238 memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(
uint8_t)); |
| 239 memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); |
| 240 fConicWeights = ref.fConicWeights; |
| 241 // We could call genID() here to force a real ID (instead of 0). However, if
we're making |
| 242 // a copy then presumably we intend to make a modification immediately after
wards. |
| 243 fGenerationID = ref.fGenerationID; |
| 244 fBoundsIsDirty = ref.fBoundsIsDirty; |
| 245 if (!fBoundsIsDirty) { |
| 246 fBounds = ref.fBounds; |
| 247 fIsFinite = ref.fIsFinite; |
| 248 } |
| 249 this->validate(); |
| 250 } |
| 251 |
| 252 void SkPathRef::resetToSize(int verbCount, int pointCount, int conicCount, |
| 253 int reserveVerbs, int reservePoints) { |
| 254 this->validate(); |
| 255 fBoundsIsDirty = true; // this also invalidates fIsFinite |
| 256 fGenerationID = 0; |
| 257 |
| 258 size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; |
| 259 size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reser
vePoints; |
| 260 size_t minSize = newSize + newReserve; |
| 261 |
| 262 ptrdiff_t sizeDelta = this->currSize() - minSize; |
| 263 |
| 264 if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) { |
| 265 sk_free(fPoints); |
| 266 fPoints = NULL; |
| 267 fVerbs = NULL; |
| 268 fFreeSpace = 0; |
| 269 fVerbCnt = 0; |
| 270 fPointCnt = 0; |
| 271 this->makeSpace(minSize); |
| 272 fVerbCnt = verbCount; |
| 273 fPointCnt = pointCount; |
| 274 fFreeSpace -= newSize; |
| 275 } else { |
| 276 fPointCnt = pointCount; |
| 277 fVerbCnt = verbCount; |
| 278 fFreeSpace = this->currSize() - minSize; |
| 279 } |
| 280 fConicWeights.setCount(conicCount); |
| 281 this->validate(); |
| 282 } |
| 283 |
| 284 void SkPathRef::grow(int newVerbs, int newPoints) { |
| 285 this->validate(); |
| 286 size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint); |
| 287 this->makeSpace(space); |
| 288 fVerbCnt += newVerbs; |
| 289 fPointCnt += newPoints; |
| 290 fFreeSpace -= space; |
| 291 fBoundsIsDirty = true; // this also invalidates fIsFinite |
| 292 this->validate(); |
| 293 } |
| 294 |
| 295 SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { |
| 296 this->validate(); |
| 297 int pCnt; |
| 298 switch (verb) { |
| 299 case SkPath::kMove_Verb: |
| 300 pCnt = 1; |
| 301 break; |
| 302 case SkPath::kLine_Verb: |
| 303 pCnt = 1; |
| 304 break; |
| 305 case SkPath::kQuad_Verb: |
| 306 // fall through |
| 307 case SkPath::kConic_Verb: |
| 308 pCnt = 2; |
| 309 break; |
| 310 case SkPath::kCubic_Verb: |
| 311 pCnt = 3; |
| 312 break; |
| 313 case SkPath::kClose_Verb: |
| 314 pCnt = 0; |
| 315 break; |
| 316 case SkPath::kDone_Verb: |
| 317 SkDEBUGFAIL("growForVerb called for kDone"); |
| 318 // fall through |
| 319 default: |
| 320 SkDEBUGFAIL("default is not reached"); |
| 321 pCnt = 0; |
| 322 } |
| 323 size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint); |
| 324 this->makeSpace(space); |
| 325 this->fVerbs[~fVerbCnt] = verb; |
| 326 SkPoint* ret = fPoints + fPointCnt; |
| 327 fVerbCnt += 1; |
| 328 fPointCnt += pCnt; |
| 329 fFreeSpace -= space; |
| 330 fBoundsIsDirty = true; // this also invalidates fIsFinite |
| 331 this->validate(); |
| 332 return ret; |
| 333 } |
| 334 |
| 335 void SkPathRef::makeSpace(size_t size) { |
| 336 this->validate(); |
| 337 ptrdiff_t growSize = size - fFreeSpace; |
| 338 if (growSize <= 0) { |
| 339 return; |
| 340 } |
| 341 size_t oldSize = this->currSize(); |
| 342 // round to next multiple of 8 bytes |
| 343 growSize = (growSize + 7) & ~static_cast<size_t>(7); |
| 344 // we always at least double the allocation |
| 345 if (static_cast<size_t>(growSize) < oldSize) { |
| 346 growSize = oldSize; |
| 347 } |
| 348 if (growSize < kMinSize) { |
| 349 growSize = kMinSize; |
| 350 } |
| 351 size_t newSize = oldSize + growSize; |
| 352 // Note that realloc could memcpy more than we need. It seems to be a win an
yway. TODO: |
| 353 // encapsulate this. |
| 354 fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize)); |
| 355 size_t oldVerbSize = fVerbCnt * sizeof(uint8_t); |
| 356 void* newVerbsDst = reinterpret_cast<void*>( |
| 357 reinterpret_cast<intptr_t>(fPoints) + newSize - oldV
erbSize); |
| 358 void* oldVerbsSrc = reinterpret_cast<void*>( |
| 359 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldV
erbSize); |
| 360 memmove(newVerbsDst, oldVerbsSrc, oldVerbSize); |
| 361 fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + ne
wSize); |
| 362 fFreeSpace += growSize; |
| 363 this->validate(); |
| 364 } |
| 365 |
| 366 int32_t SkPathRef::genID() const { |
| 367 SkASSERT(!fEditorsAttached); |
| 368 if (!fGenerationID) { |
| 369 if (0 == fPointCnt && 0 == fVerbCnt) { |
| 370 fGenerationID = kEmptyGenID; |
| 371 } else { |
| 372 static int32_t gPathRefGenerationID; |
| 373 // do a loop in case our global wraps around, as we never want to re
turn a 0 or the |
| 374 // empty ID |
| 375 do { |
| 376 fGenerationID = sk_atomic_inc(&gPathRefGenerationID) + 1; |
| 377 } while (fGenerationID <= kEmptyGenID); |
| 378 } |
| 379 } |
| 380 return fGenerationID; |
| 381 } |
| 382 |
| 383 void SkPathRef::validate() const { |
| 384 SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0); |
| 385 SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPo
ints) >= 0); |
| 386 SkASSERT((NULL == fPoints) == (NULL == fVerbs)); |
| 387 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); |
| 388 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); |
| 389 SkASSERT(!(NULL == fPoints && fPointCnt)); |
| 390 SkASSERT(!(NULL == fVerbs && fVerbCnt)); |
| 391 SkASSERT(this->currSize() == |
| 392 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVe
rbCnt); |
| 393 |
| 394 #ifdef SK_DEBUG |
| 395 if (!fBoundsIsDirty && !fBounds.isEmpty()) { |
| 396 bool isFinite = true; |
| 397 for (int i = 0; i < fPointCnt; ++i) { |
| 398 SkASSERT(fPoints[i].fX >= fBounds.fLeft && fPoints[i].fX <= fBounds.
fRight && |
| 399 fPoints[i].fY >= fBounds.fTop && fPoints[i].fY <= fBound
s.fBottom); |
| 400 if (!fPoints[i].isFinite()) { |
| 401 isFinite = false; |
| 402 } |
| 403 } |
| 404 SkASSERT(SkToBool(fIsFinite) == isFinite); |
| 405 } |
| 406 #endif |
| 407 } |
OLD | NEW |