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