| 1 | /* | 
|---|
| 2 | * Copyright 2013 Google Inc. | 
|---|
| 3 | * | 
|---|
| 4 | * Use of this source code is governed by a BSD-style license that can be | 
|---|
| 5 | * found in the LICENSE file. | 
|---|
| 6 | */ | 
|---|
| 7 |  | 
|---|
| 8 | #include "include/private/SkPathRef.h" | 
|---|
| 9 |  | 
|---|
| 10 | #include "include/core/SkPath.h" | 
|---|
| 11 | #include "include/private/SkNx.h" | 
|---|
| 12 | #include "include/private/SkOnce.h" | 
|---|
| 13 | #include "include/private/SkTo.h" | 
|---|
| 14 | #include "src/core/SkBuffer.h" | 
|---|
| 15 | #include "src/core/SkPathPriv.h" | 
|---|
| 16 | #include "src/core/SkSafeMath.h" | 
|---|
| 17 |  | 
|---|
| 18 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
| 19 | SkPathRef::Editor::Editor(sk_sp<SkPathRef>* pathRef, | 
|---|
| 20 | int incReserveVerbs, | 
|---|
| 21 | int incReservePoints) | 
|---|
| 22 | { | 
|---|
| 23 | SkASSERT(incReserveVerbs >= 0); | 
|---|
| 24 | SkASSERT(incReservePoints >= 0); | 
|---|
| 25 |  | 
|---|
| 26 | if ((*pathRef)->unique()) { | 
|---|
| 27 | (*pathRef)->incReserve(incReserveVerbs, incReservePoints); | 
|---|
| 28 | } else { | 
|---|
| 29 | SkPathRef* copy = new SkPathRef; | 
|---|
| 30 | copy->copy(**pathRef, incReserveVerbs, incReservePoints); | 
|---|
| 31 | pathRef->reset(copy); | 
|---|
| 32 | } | 
|---|
| 33 | fPathRef = pathRef->get(); | 
|---|
| 34 | fPathRef->callGenIDChangeListeners(); | 
|---|
| 35 | fPathRef->fGenerationID = 0; | 
|---|
| 36 | fPathRef->fBoundsIsDirty = true; | 
|---|
| 37 | SkDEBUGCODE(fPathRef->fEditorsAttached++;) | 
|---|
| 38 | } | 
|---|
| 39 |  | 
|---|
| 40 | // Sort of like makeSpace(0) but the the additional requirement that we actively shrink the | 
|---|
| 41 | // allocations to just fit the current needs. makeSpace() will only grow, but never shrinks. | 
|---|
| 42 | // | 
|---|
| 43 | void SkPath::shrinkToFit() { | 
|---|
| 44 | fPathRef->fPoints.shrinkToFit(); | 
|---|
| 45 | fPathRef->fVerbs.shrinkToFit(); | 
|---|
| 46 | fPathRef->fConicWeights.shrinkToFit(); | 
|---|
| 47 | SkDEBUGCODE(fPathRef->validate();) | 
|---|
| 48 | } | 
|---|
| 49 |  | 
|---|
| 50 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
| 51 |  | 
|---|
| 52 | SkPathRef::~SkPathRef() { | 
|---|
| 53 | // Deliberately don't validate() this path ref, otherwise there's no way | 
|---|
| 54 | // to read one that's not valid and then free its memory without asserting. | 
|---|
| 55 | SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;) | 
|---|
| 56 | SkDEBUGCODE(fEditorsAttached.store(0x7777777);) | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | static SkPathRef* gEmpty = nullptr; | 
|---|
| 60 |  | 
|---|
| 61 | SkPathRef* SkPathRef::CreateEmpty() { | 
|---|
| 62 | static SkOnce once; | 
|---|
| 63 | once([]{ | 
|---|
| 64 | gEmpty = new SkPathRef; | 
|---|
| 65 | gEmpty->computeBounds();   // Avoids races later to be the first to do this. | 
|---|
| 66 | }); | 
|---|
| 67 | return SkRef(gEmpty); | 
|---|
| 68 | } | 
|---|
| 69 |  | 
|---|
| 70 | static void transform_dir_and_start(const SkMatrix& matrix, bool isRRect, bool* isCCW, | 
|---|
| 71 | unsigned* start) { | 
|---|
| 72 | int inStart = *start; | 
|---|
| 73 | int rm = 0; | 
|---|
| 74 | if (isRRect) { | 
|---|
| 75 | // Degenerate rrect indices to oval indices and remember the remainder. | 
|---|
| 76 | // Ovals have one index per side whereas rrects have two. | 
|---|
| 77 | rm = inStart & 0b1; | 
|---|
| 78 | inStart /= 2; | 
|---|
| 79 | } | 
|---|
| 80 | // Is the antidiagonal non-zero (otherwise the diagonal is zero) | 
|---|
| 81 | int antiDiag; | 
|---|
| 82 | // Is the non-zero value in the top row (either kMScaleX or kMSkewX) negative | 
|---|
| 83 | int topNeg; | 
|---|
| 84 | // Are the two non-zero diagonal or antidiagonal values the same sign. | 
|---|
| 85 | int sameSign; | 
|---|
| 86 | if (matrix.get(SkMatrix::kMScaleX) != 0) { | 
|---|
| 87 | antiDiag = 0b00; | 
|---|
| 88 | if (matrix.get(SkMatrix::kMScaleX) > 0) { | 
|---|
| 89 | topNeg = 0b00; | 
|---|
| 90 | sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b01 : 0b00; | 
|---|
| 91 | } else { | 
|---|
| 92 | topNeg = 0b10; | 
|---|
| 93 | sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b00 : 0b01; | 
|---|
| 94 | } | 
|---|
| 95 | } else { | 
|---|
| 96 | antiDiag = 0b01; | 
|---|
| 97 | if (matrix.get(SkMatrix::kMSkewX) > 0) { | 
|---|
| 98 | topNeg = 0b00; | 
|---|
| 99 | sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b01 : 0b00; | 
|---|
| 100 | } else { | 
|---|
| 101 | topNeg = 0b10; | 
|---|
| 102 | sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b00 : 0b01; | 
|---|
| 103 | } | 
|---|
| 104 | } | 
|---|
| 105 | if (sameSign != antiDiag) { | 
|---|
| 106 | // This is a rotation (and maybe scale). The direction is unchanged. | 
|---|
| 107 | // Trust me on the start computation (or draw yourself some pictures) | 
|---|
| 108 | *start = (inStart + 4 - (topNeg | antiDiag)) % 4; | 
|---|
| 109 | SkASSERT(*start < 4); | 
|---|
| 110 | if (isRRect) { | 
|---|
| 111 | *start = 2 * *start + rm; | 
|---|
| 112 | } | 
|---|
| 113 | } else { | 
|---|
| 114 | // This is a mirror (and maybe scale). The direction is reversed. | 
|---|
| 115 | *isCCW = !*isCCW; | 
|---|
| 116 | // Trust me on the start computation (or draw yourself some pictures) | 
|---|
| 117 | *start = (6 + (topNeg | antiDiag) - inStart) % 4; | 
|---|
| 118 | SkASSERT(*start < 4); | 
|---|
| 119 | if (isRRect) { | 
|---|
| 120 | *start = 2 * *start + (rm ? 0 : 1); | 
|---|
| 121 | } | 
|---|
| 122 | } | 
|---|
| 123 | } | 
|---|
| 124 |  | 
|---|
| 125 | void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst, | 
|---|
| 126 | const SkPathRef& src, | 
|---|
| 127 | const SkMatrix& matrix) { | 
|---|
| 128 | SkDEBUGCODE(src.validate();) | 
|---|
| 129 | if (matrix.isIdentity()) { | 
|---|
| 130 | if (dst->get() != &src) { | 
|---|
| 131 | src.ref(); | 
|---|
| 132 | dst->reset(const_cast<SkPathRef*>(&src)); | 
|---|
| 133 | SkDEBUGCODE((*dst)->validate();) | 
|---|
| 134 | } | 
|---|
| 135 | return; | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | if (!(*dst)->unique()) { | 
|---|
| 139 | dst->reset(new SkPathRef); | 
|---|
| 140 | } | 
|---|
| 141 |  | 
|---|
| 142 | if (dst->get() != &src) { | 
|---|
| 143 | (*dst)->fPoints = src.fPoints; | 
|---|
| 144 | (*dst)->fVerbs = src.fVerbs; | 
|---|
| 145 | (*dst)->fConicWeights = src.fConicWeights; | 
|---|
| 146 | (*dst)->callGenIDChangeListeners(); | 
|---|
| 147 | (*dst)->fGenerationID = 0;  // mark as dirty | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|
| 150 | // Need to check this here in case (&src == dst) | 
|---|
| 151 | bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1; | 
|---|
| 152 |  | 
|---|
| 153 | matrix.mapPoints((*dst)->fPoints.begin(), src.fPoints.begin(), src.fPoints.count()); | 
|---|
| 154 |  | 
|---|
| 155 | /* | 
|---|
| 156 | *  Here we optimize the bounds computation, by noting if the bounds are | 
|---|
| 157 | *  already known, and if so, we just transform those as well and mark | 
|---|
| 158 | *  them as "known", rather than force the transformed path to have to | 
|---|
| 159 | *  recompute them. | 
|---|
| 160 | * | 
|---|
| 161 | *  Special gotchas if the path is effectively empty (<= 1 point) or | 
|---|
| 162 | *  if it is non-finite. In those cases bounds need to stay empty, | 
|---|
| 163 | *  regardless of the matrix. | 
|---|
| 164 | */ | 
|---|
| 165 | if (canXformBounds) { | 
|---|
| 166 | (*dst)->fBoundsIsDirty = false; | 
|---|
| 167 | if (src.fIsFinite) { | 
|---|
| 168 | matrix.mapRect(&(*dst)->fBounds, src.fBounds); | 
|---|
| 169 | if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) { | 
|---|
| 170 | (*dst)->fBounds.setEmpty(); | 
|---|
| 171 | } | 
|---|
| 172 | } else { | 
|---|
| 173 | (*dst)->fIsFinite = false; | 
|---|
| 174 | (*dst)->fBounds.setEmpty(); | 
|---|
| 175 | } | 
|---|
| 176 | } else { | 
|---|
| 177 | (*dst)->fBoundsIsDirty = true; | 
|---|
| 178 | } | 
|---|
| 179 |  | 
|---|
| 180 | (*dst)->fSegmentMask = src.fSegmentMask; | 
|---|
| 181 |  | 
|---|
| 182 | // It's an oval only if it stays a rect. | 
|---|
| 183 | bool rectStaysRect = matrix.rectStaysRect(); | 
|---|
| 184 | (*dst)->fIsOval = src.fIsOval && rectStaysRect; | 
|---|
| 185 | (*dst)->fIsRRect = src.fIsRRect && rectStaysRect; | 
|---|
| 186 | if ((*dst)->fIsOval || (*dst)->fIsRRect) { | 
|---|
| 187 | unsigned start = src.fRRectOrOvalStartIdx; | 
|---|
| 188 | bool isCCW = SkToBool(src.fRRectOrOvalIsCCW); | 
|---|
| 189 | transform_dir_and_start(matrix, (*dst)->fIsRRect, &isCCW, &start); | 
|---|
| 190 | (*dst)->fRRectOrOvalIsCCW = isCCW; | 
|---|
| 191 | (*dst)->fRRectOrOvalStartIdx = start; | 
|---|
| 192 | } | 
|---|
| 193 |  | 
|---|
| 194 | if (dst->get() == &src) { | 
|---|
| 195 | (*dst)->callGenIDChangeListeners(); | 
|---|
| 196 | (*dst)->fGenerationID = 0; | 
|---|
| 197 | } | 
|---|
| 198 |  | 
|---|
| 199 | SkDEBUGCODE((*dst)->validate();) | 
|---|
| 200 | } | 
|---|
| 201 |  | 
|---|
| 202 | void SkPathRef::Rewind(sk_sp<SkPathRef>* pathRef) { | 
|---|
| 203 | if ((*pathRef)->unique()) { | 
|---|
| 204 | SkDEBUGCODE((*pathRef)->validate();) | 
|---|
| 205 | (*pathRef)->callGenIDChangeListeners(); | 
|---|
| 206 | (*pathRef)->fBoundsIsDirty = true;  // this also invalidates fIsFinite | 
|---|
| 207 | (*pathRef)->fGenerationID = 0; | 
|---|
| 208 | (*pathRef)->fPoints.rewind(); | 
|---|
| 209 | (*pathRef)->fVerbs.rewind(); | 
|---|
| 210 | (*pathRef)->fConicWeights.rewind(); | 
|---|
| 211 | (*pathRef)->fSegmentMask = 0; | 
|---|
| 212 | (*pathRef)->fIsOval = false; | 
|---|
| 213 | (*pathRef)->fIsRRect = false; | 
|---|
| 214 | SkDEBUGCODE((*pathRef)->validate();) | 
|---|
| 215 | } else { | 
|---|
| 216 | int oldVCnt = (*pathRef)->countVerbs(); | 
|---|
| 217 | int oldPCnt = (*pathRef)->countPoints(); | 
|---|
| 218 | pathRef->reset(new SkPathRef); | 
|---|
| 219 | (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt); | 
|---|
| 220 | } | 
|---|
| 221 | } | 
|---|
| 222 |  | 
|---|
| 223 | bool SkPathRef::operator== (const SkPathRef& ref) const { | 
|---|
| 224 | SkDEBUGCODE(this->validate();) | 
|---|
| 225 | SkDEBUGCODE(ref.validate();) | 
|---|
| 226 |  | 
|---|
| 227 | // We explicitly check fSegmentMask as a quick-reject. We could skip it, | 
|---|
| 228 | // since it is only a cache of info in the fVerbs, but its a fast way to | 
|---|
| 229 | // notice a difference | 
|---|
| 230 | if (fSegmentMask != ref.fSegmentMask) { | 
|---|
| 231 | return false; | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 | bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; | 
|---|
| 235 | #ifdef SK_RELEASE | 
|---|
| 236 | if (genIDMatch) { | 
|---|
| 237 | return true; | 
|---|
| 238 | } | 
|---|
| 239 | #endif | 
|---|
| 240 | if (fPoints != ref.fPoints || fConicWeights != ref.fConicWeights || fVerbs != ref.fVerbs) { | 
|---|
| 241 | SkASSERT(!genIDMatch); | 
|---|
| 242 | return false; | 
|---|
| 243 | } | 
|---|
| 244 | if (ref.fVerbs.count() == 0) { | 
|---|
| 245 | SkASSERT(ref.fPoints.count() == 0); | 
|---|
| 246 | } | 
|---|
| 247 | return true; | 
|---|
| 248 | } | 
|---|
| 249 |  | 
|---|
| 250 | void SkPathRef::writeToBuffer(SkWBuffer* buffer) const { | 
|---|
| 251 | SkDEBUGCODE(this->validate();) | 
|---|
| 252 | SkDEBUGCODE(size_t beforePos = buffer->pos();) | 
|---|
| 253 |  | 
|---|
| 254 | // Call getBounds() to ensure (as a side-effect) that fBounds | 
|---|
| 255 | // and fIsFinite are computed. | 
|---|
| 256 | const SkRect& bounds = this->getBounds(); | 
|---|
| 257 |  | 
|---|
| 258 | // We store fSegmentMask for older readers, but current readers can't trust it, so they | 
|---|
| 259 | // don't read it. | 
|---|
| 260 | int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | | 
|---|
| 261 | (fSegmentMask << kSegmentMask_SerializationShift); | 
|---|
| 262 | buffer->write32(packed); | 
|---|
| 263 |  | 
|---|
| 264 | // TODO: write gen ID here. Problem: We don't know if we're cross process or not from | 
|---|
| 265 | // SkWBuffer. Until this is fixed we write 0. | 
|---|
| 266 | buffer->write32(0); | 
|---|
| 267 | buffer->write32(fVerbs.count()); | 
|---|
| 268 | buffer->write32(fPoints.count()); | 
|---|
| 269 | buffer->write32(fConicWeights.count()); | 
|---|
| 270 | buffer->write(fVerbs.begin(), fVerbs.bytes()); | 
|---|
| 271 | buffer->write(fPoints.begin(), fVerbs.bytes()); | 
|---|
| 272 | buffer->write(fConicWeights.begin(), fConicWeights.bytes()); | 
|---|
| 273 | buffer->write(&bounds, sizeof(bounds)); | 
|---|
| 274 |  | 
|---|
| 275 | SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize()); | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | uint32_t SkPathRef::writeSize() const { | 
|---|
| 279 | return uint32_t(5 * sizeof(uint32_t) + | 
|---|
| 280 | fVerbs.bytes() + fPoints.bytes() + fConicWeights.bytes() + | 
|---|
| 281 | sizeof(SkRect)); | 
|---|
| 282 | } | 
|---|
| 283 |  | 
|---|
| 284 | void SkPathRef::copy(const SkPathRef& ref, | 
|---|
| 285 | int additionalReserveVerbs, | 
|---|
| 286 | int additionalReservePoints) { | 
|---|
| 287 | SkDEBUGCODE(this->validate();) | 
|---|
| 288 | this->resetToSize(ref.fVerbs.count(), ref.fPoints.count(), ref.fConicWeights.count(), | 
|---|
| 289 | additionalReserveVerbs, additionalReservePoints); | 
|---|
| 290 | fVerbs = ref.fVerbs; | 
|---|
| 291 | fPoints = ref.fPoints; | 
|---|
| 292 | fConicWeights = ref.fConicWeights; | 
|---|
| 293 | fBoundsIsDirty = ref.fBoundsIsDirty; | 
|---|
| 294 | if (!fBoundsIsDirty) { | 
|---|
| 295 | fBounds = ref.fBounds; | 
|---|
| 296 | fIsFinite = ref.fIsFinite; | 
|---|
| 297 | } | 
|---|
| 298 | fSegmentMask = ref.fSegmentMask; | 
|---|
| 299 | fIsOval = ref.fIsOval; | 
|---|
| 300 | fIsRRect = ref.fIsRRect; | 
|---|
| 301 | fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW; | 
|---|
| 302 | fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx; | 
|---|
| 303 | SkDEBUGCODE(this->validate();) | 
|---|
| 304 | } | 
|---|
| 305 |  | 
|---|
| 306 | unsigned SkPathRef::computeSegmentMask() const { | 
|---|
| 307 | const uint8_t* verbs = fVerbs.begin(); | 
|---|
| 308 | unsigned mask = 0; | 
|---|
| 309 | for (int i = 0; i < fVerbs.count(); ++i) { | 
|---|
| 310 | switch (verbs[i]) { | 
|---|
| 311 | case SkPath::kLine_Verb:  mask |= SkPath::kLine_SegmentMask; break; | 
|---|
| 312 | case SkPath::kQuad_Verb:  mask |= SkPath::kQuad_SegmentMask; break; | 
|---|
| 313 | case SkPath::kConic_Verb: mask |= SkPath::kConic_SegmentMask; break; | 
|---|
| 314 | case SkPath::kCubic_Verb: mask |= SkPath::kCubic_SegmentMask; break; | 
|---|
| 315 | default: break; | 
|---|
| 316 | } | 
|---|
| 317 | } | 
|---|
| 318 | return mask; | 
|---|
| 319 | } | 
|---|
| 320 |  | 
|---|
| 321 | void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const { | 
|---|
| 322 | const SkScalar* inValues = &ending.getPoints()->fX; | 
|---|
| 323 | SkScalar* outValues = &out->getWritablePoints()->fX; | 
|---|
| 324 | int count = out->countPoints() * 2; | 
|---|
| 325 | for (int index = 0; index < count; ++index) { | 
|---|
| 326 | outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight); | 
|---|
| 327 | } | 
|---|
| 328 | out->fBoundsIsDirty = true; | 
|---|
| 329 | out->fIsOval = false; | 
|---|
| 330 | out->fIsRRect = false; | 
|---|
| 331 | } | 
|---|
| 332 |  | 
|---|
| 333 | std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& path) { | 
|---|
| 334 | SkDEBUGCODE(this->validate();) | 
|---|
| 335 |  | 
|---|
| 336 | fSegmentMask |= path.fSegmentMask; | 
|---|
| 337 | fBoundsIsDirty = true;  // this also invalidates fIsFinite | 
|---|
| 338 | fIsOval = false; | 
|---|
| 339 | fIsRRect = false; | 
|---|
| 340 |  | 
|---|
| 341 | if (int numVerbs = path.countVerbs()) { | 
|---|
| 342 | memcpy(fVerbs.append(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0])); | 
|---|
| 343 | } | 
|---|
| 344 |  | 
|---|
| 345 | SkPoint* pts = nullptr; | 
|---|
| 346 | if (int numPts = path.countPoints()) { | 
|---|
| 347 | pts = fPoints.append(numPts); | 
|---|
| 348 | } | 
|---|
| 349 |  | 
|---|
| 350 | SkScalar* weights = nullptr; | 
|---|
| 351 | if (int numConics = path.countWeights()) { | 
|---|
| 352 | weights = fConicWeights.append(numConics); | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | SkDEBUGCODE(this->validate();) | 
|---|
| 356 | return {pts, weights}; | 
|---|
| 357 | } | 
|---|
| 358 |  | 
|---|
| 359 | SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb, | 
|---|
| 360 | int numVbs, | 
|---|
| 361 | SkScalar** weights) { | 
|---|
| 362 | SkDEBUGCODE(this->validate();) | 
|---|
| 363 | int pCnt; | 
|---|
| 364 | switch (verb) { | 
|---|
| 365 | case SkPath::kMove_Verb: | 
|---|
| 366 | pCnt = numVbs; | 
|---|
| 367 | break; | 
|---|
| 368 | case SkPath::kLine_Verb: | 
|---|
| 369 | fSegmentMask |= SkPath::kLine_SegmentMask; | 
|---|
| 370 | pCnt = numVbs; | 
|---|
| 371 | break; | 
|---|
| 372 | case SkPath::kQuad_Verb: | 
|---|
| 373 | fSegmentMask |= SkPath::kQuad_SegmentMask; | 
|---|
| 374 | pCnt = 2 * numVbs; | 
|---|
| 375 | break; | 
|---|
| 376 | case SkPath::kConic_Verb: | 
|---|
| 377 | fSegmentMask |= SkPath::kConic_SegmentMask; | 
|---|
| 378 | pCnt = 2 * numVbs; | 
|---|
| 379 | break; | 
|---|
| 380 | case SkPath::kCubic_Verb: | 
|---|
| 381 | fSegmentMask |= SkPath::kCubic_SegmentMask; | 
|---|
| 382 | pCnt = 3 * numVbs; | 
|---|
| 383 | break; | 
|---|
| 384 | case SkPath::kClose_Verb: | 
|---|
| 385 | SkDEBUGFAIL( "growForRepeatedVerb called for kClose_Verb"); | 
|---|
| 386 | pCnt = 0; | 
|---|
| 387 | break; | 
|---|
| 388 | case SkPath::kDone_Verb: | 
|---|
| 389 | SkDEBUGFAIL( "growForRepeatedVerb called for kDone"); | 
|---|
| 390 | // fall through | 
|---|
| 391 | default: | 
|---|
| 392 | SkDEBUGFAIL( "default should not be reached"); | 
|---|
| 393 | pCnt = 0; | 
|---|
| 394 | } | 
|---|
| 395 |  | 
|---|
| 396 | fBoundsIsDirty = true;  // this also invalidates fIsFinite | 
|---|
| 397 | fIsOval = false; | 
|---|
| 398 | fIsRRect = false; | 
|---|
| 399 |  | 
|---|
| 400 | memset(fVerbs.append(numVbs), verb, numVbs); | 
|---|
| 401 | if (SkPath::kConic_Verb == verb) { | 
|---|
| 402 | SkASSERT(weights); | 
|---|
| 403 | *weights = fConicWeights.append(numVbs); | 
|---|
| 404 | } | 
|---|
| 405 | SkPoint* pts = fPoints.append(pCnt); | 
|---|
| 406 |  | 
|---|
| 407 | SkDEBUGCODE(this->validate();) | 
|---|
| 408 | return pts; | 
|---|
| 409 | } | 
|---|
| 410 |  | 
|---|
| 411 | SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) { | 
|---|
| 412 | SkDEBUGCODE(this->validate();) | 
|---|
| 413 | int pCnt; | 
|---|
| 414 | unsigned mask = 0; | 
|---|
| 415 | switch (verb) { | 
|---|
| 416 | case SkPath::kMove_Verb: | 
|---|
| 417 | pCnt = 1; | 
|---|
| 418 | break; | 
|---|
| 419 | case SkPath::kLine_Verb: | 
|---|
| 420 | mask = SkPath::kLine_SegmentMask; | 
|---|
| 421 | pCnt = 1; | 
|---|
| 422 | break; | 
|---|
| 423 | case SkPath::kQuad_Verb: | 
|---|
| 424 | mask = SkPath::kQuad_SegmentMask; | 
|---|
| 425 | pCnt = 2; | 
|---|
| 426 | break; | 
|---|
| 427 | case SkPath::kConic_Verb: | 
|---|
| 428 | mask = SkPath::kConic_SegmentMask; | 
|---|
| 429 | pCnt = 2; | 
|---|
| 430 | break; | 
|---|
| 431 | case SkPath::kCubic_Verb: | 
|---|
| 432 | mask = SkPath::kCubic_SegmentMask; | 
|---|
| 433 | pCnt = 3; | 
|---|
| 434 | break; | 
|---|
| 435 | case SkPath::kClose_Verb: | 
|---|
| 436 | pCnt = 0; | 
|---|
| 437 | break; | 
|---|
| 438 | case SkPath::kDone_Verb: | 
|---|
| 439 | SkDEBUGFAIL( "growForVerb called for kDone"); | 
|---|
| 440 | // fall through | 
|---|
| 441 | default: | 
|---|
| 442 | SkDEBUGFAIL( "default is not reached"); | 
|---|
| 443 | pCnt = 0; | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 | fSegmentMask |= mask; | 
|---|
| 447 | fBoundsIsDirty = true;  // this also invalidates fIsFinite | 
|---|
| 448 | fIsOval = false; | 
|---|
| 449 | fIsRRect = false; | 
|---|
| 450 |  | 
|---|
| 451 | *fVerbs.append() = verb; | 
|---|
| 452 | if (SkPath::kConic_Verb == verb) { | 
|---|
| 453 | *fConicWeights.append() = weight; | 
|---|
| 454 | } | 
|---|
| 455 | SkPoint* pts = fPoints.append(pCnt); | 
|---|
| 456 |  | 
|---|
| 457 | SkDEBUGCODE(this->validate();) | 
|---|
| 458 | return pts; | 
|---|
| 459 | } | 
|---|
| 460 |  | 
|---|
| 461 | uint32_t SkPathRef::genID() const { | 
|---|
| 462 | SkASSERT(fEditorsAttached.load() == 0); | 
|---|
| 463 | static const uint32_t kMask = (static_cast<int64_t>(1) << SkPathPriv::kPathRefGenIDBitCnt) - 1; | 
|---|
| 464 |  | 
|---|
| 465 | if (fGenerationID == 0) { | 
|---|
| 466 | if (fPoints.count() == 0 && fVerbs.count() == 0) { | 
|---|
| 467 | fGenerationID = kEmptyGenID; | 
|---|
| 468 | } else { | 
|---|
| 469 | static std::atomic<uint32_t> nextID{kEmptyGenID + 1}; | 
|---|
| 470 | do { | 
|---|
| 471 | fGenerationID = nextID.fetch_add(1, std::memory_order_relaxed) & kMask; | 
|---|
| 472 | } while (fGenerationID == 0 || fGenerationID == kEmptyGenID); | 
|---|
| 473 | } | 
|---|
| 474 | } | 
|---|
| 475 | return fGenerationID; | 
|---|
| 476 | } | 
|---|
| 477 |  | 
|---|
| 478 | void SkPathRef::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) { | 
|---|
| 479 | if (this == gEmpty) { | 
|---|
| 480 | return; | 
|---|
| 481 | } | 
|---|
| 482 | bool singleThreaded = this->unique(); | 
|---|
| 483 | fGenIDChangeListeners.add(std::move(listener), singleThreaded); | 
|---|
| 484 | } | 
|---|
| 485 |  | 
|---|
| 486 | int SkPathRef::genIDChangeListenerCount() { return fGenIDChangeListeners.count(); } | 
|---|
| 487 |  | 
|---|
| 488 | // we need to be called *before* the genID gets changed or zerod | 
|---|
| 489 | void SkPathRef::callGenIDChangeListeners() { | 
|---|
| 490 | bool singleThreaded = this->unique(); | 
|---|
| 491 | fGenIDChangeListeners.changed(singleThreaded); | 
|---|
| 492 | } | 
|---|
| 493 |  | 
|---|
| 494 | SkRRect SkPathRef::getRRect() const { | 
|---|
| 495 | const SkRect& bounds = this->getBounds(); | 
|---|
| 496 | SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; | 
|---|
| 497 | Iter iter(*this); | 
|---|
| 498 | SkPoint pts[4]; | 
|---|
| 499 | uint8_t verb = iter.next(pts); | 
|---|
| 500 | SkASSERT(SkPath::kMove_Verb == verb); | 
|---|
| 501 | while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { | 
|---|
| 502 | if (SkPath::kConic_Verb == verb) { | 
|---|
| 503 | SkVector v1_0 = pts[1] - pts[0]; | 
|---|
| 504 | SkVector v2_1 = pts[2] - pts[1]; | 
|---|
| 505 | SkVector dxdy; | 
|---|
| 506 | if (v1_0.fX) { | 
|---|
| 507 | SkASSERT(!v2_1.fX && !v1_0.fY); | 
|---|
| 508 | dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY)); | 
|---|
| 509 | } else if (!v1_0.fY) { | 
|---|
| 510 | SkASSERT(!v2_1.fX || !v2_1.fY); | 
|---|
| 511 | dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY)); | 
|---|
| 512 | } else { | 
|---|
| 513 | SkASSERT(!v2_1.fY); | 
|---|
| 514 | dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY)); | 
|---|
| 515 | } | 
|---|
| 516 | SkRRect::Corner corner = | 
|---|
| 517 | pts[1].fX == bounds.fLeft ? | 
|---|
| 518 | pts[1].fY == bounds.fTop ? | 
|---|
| 519 | SkRRect::kUpperLeft_Corner : SkRRect::kLowerLeft_Corner : | 
|---|
| 520 | pts[1].fY == bounds.fTop ? | 
|---|
| 521 | SkRRect::kUpperRight_Corner : SkRRect::kLowerRight_Corner; | 
|---|
| 522 | SkASSERT(!radii[corner].fX && !radii[corner].fY); | 
|---|
| 523 | radii[corner] = dxdy; | 
|---|
| 524 | } else { | 
|---|
| 525 | SkASSERT((verb == SkPath::kLine_Verb | 
|---|
| 526 | && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY))) | 
|---|
| 527 | || verb == SkPath::kClose_Verb); | 
|---|
| 528 | } | 
|---|
| 529 | } | 
|---|
| 530 | SkRRect rrect; | 
|---|
| 531 | rrect.setRectRadii(bounds, radii); | 
|---|
| 532 | return rrect; | 
|---|
| 533 | } | 
|---|
| 534 |  | 
|---|
| 535 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| 536 |  | 
|---|
| 537 | SkPathRef::Iter::Iter() { | 
|---|
| 538 | #ifdef SK_DEBUG | 
|---|
| 539 | fPts = nullptr; | 
|---|
| 540 | fConicWeights = nullptr; | 
|---|
| 541 | #endif | 
|---|
| 542 | // need to init enough to make next() harmlessly return kDone_Verb | 
|---|
| 543 | fVerbs = nullptr; | 
|---|
| 544 | fVerbStop = nullptr; | 
|---|
| 545 | } | 
|---|
| 546 |  | 
|---|
| 547 | SkPathRef::Iter::Iter(const SkPathRef& path) { | 
|---|
| 548 | this->setPathRef(path); | 
|---|
| 549 | } | 
|---|
| 550 |  | 
|---|
| 551 | void SkPathRef::Iter::setPathRef(const SkPathRef& path) { | 
|---|
| 552 | fPts = path.points(); | 
|---|
| 553 | fVerbs = path.verbsBegin(); | 
|---|
| 554 | fVerbStop = path.verbsEnd(); | 
|---|
| 555 | fConicWeights = path.conicWeights(); | 
|---|
| 556 | if (fConicWeights) { | 
|---|
| 557 | fConicWeights -= 1;  // begin one behind | 
|---|
| 558 | } | 
|---|
| 559 |  | 
|---|
| 560 | // Don't allow iteration through non-finite points. | 
|---|
| 561 | if (!path.isFinite()) { | 
|---|
| 562 | fVerbStop = fVerbs; | 
|---|
| 563 | } | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | uint8_t SkPathRef::Iter::next(SkPoint pts[4]) { | 
|---|
| 567 | SkASSERT(pts); | 
|---|
| 568 |  | 
|---|
| 569 | SkDEBUGCODE(unsigned peekResult = this->peek();) | 
|---|
| 570 |  | 
|---|
| 571 | if (fVerbs == fVerbStop) { | 
|---|
| 572 | SkASSERT(peekResult == SkPath::kDone_Verb); | 
|---|
| 573 | return (uint8_t) SkPath::kDone_Verb; | 
|---|
| 574 | } | 
|---|
| 575 |  | 
|---|
| 576 | // fVerbs points one beyond next verb so decrement first. | 
|---|
| 577 | unsigned verb = *fVerbs++; | 
|---|
| 578 | const SkPoint* srcPts = fPts; | 
|---|
| 579 |  | 
|---|
| 580 | switch (verb) { | 
|---|
| 581 | case SkPath::kMove_Verb: | 
|---|
| 582 | pts[0] = srcPts[0]; | 
|---|
| 583 | srcPts += 1; | 
|---|
| 584 | break; | 
|---|
| 585 | case SkPath::kLine_Verb: | 
|---|
| 586 | pts[0] = srcPts[-1]; | 
|---|
| 587 | pts[1] = srcPts[0]; | 
|---|
| 588 | srcPts += 1; | 
|---|
| 589 | break; | 
|---|
| 590 | case SkPath::kConic_Verb: | 
|---|
| 591 | fConicWeights += 1; | 
|---|
| 592 | // fall-through | 
|---|
| 593 | case SkPath::kQuad_Verb: | 
|---|
| 594 | pts[0] = srcPts[-1]; | 
|---|
| 595 | pts[1] = srcPts[0]; | 
|---|
| 596 | pts[2] = srcPts[1]; | 
|---|
| 597 | srcPts += 2; | 
|---|
| 598 | break; | 
|---|
| 599 | case SkPath::kCubic_Verb: | 
|---|
| 600 | pts[0] = srcPts[-1]; | 
|---|
| 601 | pts[1] = srcPts[0]; | 
|---|
| 602 | pts[2] = srcPts[1]; | 
|---|
| 603 | pts[3] = srcPts[2]; | 
|---|
| 604 | srcPts += 3; | 
|---|
| 605 | break; | 
|---|
| 606 | case SkPath::kClose_Verb: | 
|---|
| 607 | break; | 
|---|
| 608 | case SkPath::kDone_Verb: | 
|---|
| 609 | SkASSERT(fVerbs == fVerbStop); | 
|---|
| 610 | break; | 
|---|
| 611 | } | 
|---|
| 612 | fPts = srcPts; | 
|---|
| 613 | SkASSERT(peekResult == verb); | 
|---|
| 614 | return (uint8_t) verb; | 
|---|
| 615 | } | 
|---|
| 616 |  | 
|---|
| 617 | uint8_t SkPathRef::Iter::peek() const { | 
|---|
| 618 | return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb; | 
|---|
| 619 | } | 
|---|
| 620 |  | 
|---|
| 621 |  | 
|---|
| 622 | bool SkPathRef::isValid() const { | 
|---|
| 623 | if (fIsOval || fIsRRect) { | 
|---|
| 624 | // Currently we don't allow both of these to be set, even though ovals are ro | 
|---|
| 625 | if (fIsOval == fIsRRect) { | 
|---|
| 626 | return false; | 
|---|
| 627 | } | 
|---|
| 628 | if (fIsOval) { | 
|---|
| 629 | if (fRRectOrOvalStartIdx >= 4) { | 
|---|
| 630 | return false; | 
|---|
| 631 | } | 
|---|
| 632 | } else { | 
|---|
| 633 | if (fRRectOrOvalStartIdx >= 8) { | 
|---|
| 634 | return false; | 
|---|
| 635 | } | 
|---|
| 636 | } | 
|---|
| 637 | } | 
|---|
| 638 |  | 
|---|
| 639 | if (!fBoundsIsDirty && !fBounds.isEmpty()) { | 
|---|
| 640 | bool isFinite = true; | 
|---|
| 641 | Sk2s leftTop = Sk2s(fBounds.fLeft, fBounds.fTop); | 
|---|
| 642 | Sk2s rightBot = Sk2s(fBounds.fRight, fBounds.fBottom); | 
|---|
| 643 | for (int i = 0; i < fPoints.count(); ++i) { | 
|---|
| 644 | Sk2s point = Sk2s(fPoints[i].fX, fPoints[i].fY); | 
|---|
| 645 | #ifdef SK_DEBUG | 
|---|
| 646 | if (fPoints[i].isFinite() && | 
|---|
| 647 | ((point < leftTop).anyTrue() || (point > rightBot).anyTrue())) { | 
|---|
| 648 | SkDebugf( "bad SkPathRef bounds: %g %g %g %g\n", | 
|---|
| 649 | fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom); | 
|---|
| 650 | for (int j = 0; j < fPoints.count(); ++j) { | 
|---|
| 651 | if (i == j) { | 
|---|
| 652 | SkDebugf( "*** bounds do not contain: "); | 
|---|
| 653 | } | 
|---|
| 654 | SkDebugf( "%g %g\n", fPoints[j].fX, fPoints[j].fY); | 
|---|
| 655 | } | 
|---|
| 656 | return false; | 
|---|
| 657 | } | 
|---|
| 658 | #endif | 
|---|
| 659 |  | 
|---|
| 660 | if (fPoints[i].isFinite() && (point < leftTop).anyTrue() && !(point > rightBot).anyTrue()) | 
|---|
| 661 | return false; | 
|---|
| 662 | if (!fPoints[i].isFinite()) { | 
|---|
| 663 | isFinite = false; | 
|---|
| 664 | } | 
|---|
| 665 | } | 
|---|
| 666 | if (SkToBool(fIsFinite) != isFinite) { | 
|---|
| 667 | return false; | 
|---|
| 668 | } | 
|---|
| 669 | } | 
|---|
| 670 | return true; | 
|---|
| 671 | } | 
|---|
| 672 |  | 
|---|
| 673 | ////////////////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| 674 |  | 
|---|
| 675 | SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) { | 
|---|
| 676 | fMoveToPtr = fPts = path.fPathRef->points(); | 
|---|
| 677 | fVerbs = path.fPathRef->verbsBegin(); | 
|---|
| 678 | fVerbsStop = path.fPathRef->verbsEnd(); | 
|---|
| 679 | fConicWeights = path.fPathRef->conicWeights(); | 
|---|
| 680 | if (fConicWeights) { | 
|---|
| 681 | fConicWeights -= 1;  // begin one behind | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | fNeedsCloseLine = false; | 
|---|
| 685 | fNextIsNewContour = false; | 
|---|
| 686 | SkDEBUGCODE(fIsConic = false;) | 
|---|
| 687 | } | 
|---|
| 688 |  | 
|---|