1 /* 2 * Copyright 2016 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 "GrShape.h" 9 10 #include <utility> 11 12 GrShape& GrShape::operator=(const GrShape& that) { 13 fStyle = that.fStyle; 14 this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr); 15 switch (fType) { 16 case Type::kEmpty: 17 break; 18 case Type::kInvertedEmpty: 19 break; 20 case Type::kRRect: 21 fRRectData = that.fRRectData; 22 break; 23 case Type::kArc: 24 fArcData = that.fArcData; 25 break; 26 case Type::kLine: 27 fLineData = that.fLineData; 28 break; 29 case Type::kPath: 30 fPathData.fGenID = that.fPathData.fGenID; 31 break; 32 } 33 fInheritedKey.reset(that.fInheritedKey.count()); 34 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 35 sizeof(uint32_t) * fInheritedKey.count()); 36 if (that.fInheritedPathForListeners.isValid()) { 37 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get()); 38 } else { 39 fInheritedPathForListeners.reset(); 40 } 41 return *this; 42 } 43 44 static bool flip_inversion(bool originalIsInverted, GrShape::FillInversion inversion) { 45 switch (inversion) { 46 case GrShape::FillInversion::kPreserve: 47 return false; 48 case GrShape::FillInversion::kFlip: 49 return true; 50 case GrShape::FillInversion::kForceInverted: 51 return !originalIsInverted; 52 case GrShape::FillInversion::kForceNoninverted: 53 return originalIsInverted; 54 } 55 return false; 56 } 57 58 static bool is_inverted(bool originalIsInverted, GrShape::FillInversion inversion) { 59 switch (inversion) { 60 case GrShape::FillInversion::kPreserve: 61 return originalIsInverted; 62 case GrShape::FillInversion::kFlip: 63 return !originalIsInverted; 64 case GrShape::FillInversion::kForceInverted: 65 return true; 66 case GrShape::FillInversion::kForceNoninverted: 67 return false; 68 } 69 return false; 70 } 71 72 GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) { 73 if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) { 74 // By returning the original rather than falling through we can preserve any inherited style 75 // key. Otherwise, we wipe it out below since the style change invalidates it. 76 return original; 77 } 78 GrShape result; 79 if (original.fInheritedPathForListeners.isValid()) { 80 result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get()); 81 } 82 switch (original.fType) { 83 case Type::kRRect: 84 result.fType = original.fType; 85 result.fRRectData.fRRect = original.fRRectData.fRRect; 86 result.fRRectData.fDir = kDefaultRRectDir; 87 result.fRRectData.fStart = kDefaultRRectStart; 88 result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion); 89 break; 90 case Type::kArc: 91 result.fType = original.fType; 92 result.fArcData.fOval = original.fArcData.fOval; 93 result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees; 94 result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees; 95 result.fArcData.fUseCenter = original.fArcData.fUseCenter; 96 result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion); 97 break; 98 case Type::kLine: 99 // Lines don't fill. 100 if (is_inverted(original.fLineData.fInverted, inversion)) { 101 result.fType = Type::kInvertedEmpty; 102 } else { 103 result.fType = Type::kEmpty; 104 } 105 break; 106 case Type::kEmpty: 107 result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty : Type::kEmpty; 108 break; 109 case Type::kInvertedEmpty: 110 result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty : Type::kEmpty; 111 break; 112 case Type::kPath: 113 result.initType(Type::kPath, &original.fPathData.fPath); 114 result.fPathData.fGenID = original.fPathData.fGenID; 115 if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) { 116 result.fPathData.fPath.toggleInverseFillType(); 117 } 118 if (!original.style().isSimpleFill()) { 119 // Going from a non-filled style to fill may allow additional simplifications (e.g. 120 // closing an open rect that wasn't closed in the original shape because it had 121 // stroke style). 122 result.attemptToSimplifyPath(); 123 } 124 break; 125 } 126 // We don't copy the inherited key since it can contain path effect information that we just 127 // stripped. 128 return result; 129 } 130 131 SkRect GrShape::bounds() const { 132 // Bounds where left == bottom or top == right can indicate a line or point shape. We return 133 // inverted bounds for a truly empty shape. 134 static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1); 135 switch (fType) { 136 case Type::kEmpty: 137 return kInverted; 138 case Type::kInvertedEmpty: 139 return kInverted; 140 case Type::kLine: { 141 SkRect bounds; 142 if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) { 143 bounds.fLeft = fLineData.fPts[0].fX; 144 bounds.fRight = fLineData.fPts[1].fX; 145 } else { 146 bounds.fLeft = fLineData.fPts[1].fX; 147 bounds.fRight = fLineData.fPts[0].fX; 148 } 149 if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) { 150 bounds.fTop = fLineData.fPts[0].fY; 151 bounds.fBottom = fLineData.fPts[1].fY; 152 } else { 153 bounds.fTop = fLineData.fPts[1].fY; 154 bounds.fBottom = fLineData.fPts[0].fY; 155 } 156 return bounds; 157 } 158 case Type::kRRect: 159 return fRRectData.fRRect.getBounds(); 160 case Type::kArc: 161 // Could make this less conservative by looking at angles. 162 return fArcData.fOval; 163 case Type::kPath: 164 return this->path().getBounds(); 165 } 166 SK_ABORT("Unknown shape type"); 167 return kInverted; 168 } 169 170 SkRect GrShape::styledBounds() const { 171 if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) { 172 return SkRect::MakeEmpty(); 173 } 174 175 SkRect bounds; 176 fStyle.adjustBounds(&bounds, this->bounds()); 177 return bounds; 178 } 179 180 // If the path is small enough to be keyed from its data this returns key length, otherwise -1. 181 static int path_key_from_data_size(const SkPath& path) { 182 const int verbCnt = path.countVerbs(); 183 if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) { 184 return -1; 185 } 186 const int pointCnt = path.countPoints(); 187 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 188 189 GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 190 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t)); 191 // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to 192 // a uint32_t length. 193 return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt; 194 } 195 196 // Writes the path data key into the passed pointer. 197 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) { 198 uint32_t* key = origKey; 199 // The check below should take care of negative values casted positive. 200 const int verbCnt = path.countVerbs(); 201 const int pointCnt = path.countPoints(); 202 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 203 SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt); 204 SkASSERT(pointCnt && verbCnt); 205 *key++ = path.getFillType(); 206 *key++ = verbCnt; 207 memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t)); 208 int verbKeySize = SkAlign4(verbCnt); 209 // pad out to uint32_t alignment using value that will stand out when debugging. 210 uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt; 211 memset(pad, 0xDE, verbKeySize - verbCnt); 212 key += verbKeySize >> 2; 213 214 memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt); 215 GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 216 key += 2 * pointCnt; 217 sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt); 218 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t)); 219 SkDEBUGCODE(key += conicWeightCnt); 220 SkASSERT(key - origKey == path_key_from_data_size(path)); 221 } 222 223 int GrShape::unstyledKeySize() const { 224 if (fInheritedKey.count()) { 225 return fInheritedKey.count(); 226 } 227 switch (fType) { 228 case Type::kEmpty: 229 return 1; 230 case Type::kInvertedEmpty: 231 return 1; 232 case Type::kRRect: 233 SkASSERT(!fInheritedKey.count()); 234 GR_STATIC_ASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); 235 // + 1 for the direction, start index, and inverseness. 236 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1; 237 case Type::kArc: 238 SkASSERT(!fInheritedKey.count()); 239 GR_STATIC_ASSERT(0 == sizeof(fArcData) % sizeof(uint32_t)); 240 return sizeof(fArcData) / sizeof(uint32_t); 241 case Type::kLine: 242 GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint)); 243 // 4 for the end points and 1 for the inverseness 244 return 5; 245 case Type::kPath: { 246 if (0 == fPathData.fGenID) { 247 return -1; 248 } 249 int dataKeySize = path_key_from_data_size(fPathData.fPath); 250 if (dataKeySize >= 0) { 251 return dataKeySize; 252 } 253 // The key is the path ID and fill type. 254 return 2; 255 } 256 } 257 SK_ABORT("Should never get here."); 258 return 0; 259 } 260 261 void GrShape::writeUnstyledKey(uint32_t* key) const { 262 SkASSERT(this->unstyledKeySize()); 263 SkDEBUGCODE(uint32_t* origKey = key;) 264 if (fInheritedKey.count()) { 265 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count()); 266 SkDEBUGCODE(key += fInheritedKey.count();) 267 } else { 268 switch (fType) { 269 case Type::kEmpty: 270 *key++ = 1; 271 break; 272 case Type::kInvertedEmpty: 273 *key++ = 2; 274 break; 275 case Type::kRRect: 276 fRRectData.fRRect.writeToMemory(key); 277 key += SkRRect::kSizeInMemory / sizeof(uint32_t); 278 *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0; 279 *key |= fRRectData.fInverted ? (1 << 30) : 0; 280 *key++ |= fRRectData.fStart; 281 SkASSERT(fRRectData.fStart < 8); 282 break; 283 case Type::kArc: 284 memcpy(key, &fArcData, sizeof(fArcData)); 285 key += sizeof(fArcData) / sizeof(uint32_t); 286 break; 287 case Type::kLine: 288 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint)); 289 key += 4; 290 *key++ = fLineData.fInverted ? 1 : 0; 291 break; 292 case Type::kPath: { 293 SkASSERT(fPathData.fGenID); 294 int dataKeySize = path_key_from_data_size(fPathData.fPath); 295 if (dataKeySize >= 0) { 296 write_path_key_from_data(fPathData.fPath, key); 297 return; 298 } 299 *key++ = fPathData.fGenID; 300 // We could canonicalize the fill rule for paths that don't differentiate between 301 // even/odd or winding fill (e.g. convex). 302 *key++ = this->path().getFillType(); 303 break; 304 } 305 } 306 } 307 SkASSERT(key - origKey == this->unstyledKeySize()); 308 } 309 310 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) { 311 SkASSERT(!fInheritedKey.count()); 312 // If the output shape turns out to be simple, then we will just use its geometric key 313 if (Type::kPath == fType) { 314 // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as 315 // ApplyFullStyle(shape). 316 // The full key is structured as (geo,path_effect,stroke). 317 // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then 318 // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key 319 // and then append the style key (which should now be stroke only) at the end. 320 int parentCnt = parent.fInheritedKey.count(); 321 bool useParentGeoKey = !parentCnt; 322 if (useParentGeoKey) { 323 parentCnt = parent.unstyledKeySize(); 324 if (parentCnt < 0) { 325 // The parent's geometry has no key so we will have no key. 326 fPathData.fGenID = 0; 327 return; 328 } 329 } 330 uint32_t styleKeyFlags = 0; 331 if (parent.knownToBeClosed()) { 332 styleKeyFlags |= GrStyle::kClosed_KeyFlag; 333 } 334 if (parent.asLine(nullptr, nullptr)) { 335 styleKeyFlags |= GrStyle::kNoJoins_KeyFlag; 336 } 337 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); 338 if (styleCnt < 0) { 339 // The style doesn't allow a key, set the path gen ID to 0 so that we fail when 340 // we try to get a key for the shape. 341 fPathData.fGenID = 0; 342 return; 343 } 344 fInheritedKey.reset(parentCnt + styleCnt); 345 if (useParentGeoKey) { 346 // This will be the geo key. 347 parent.writeUnstyledKey(fInheritedKey.get()); 348 } else { 349 // This should be (geo,path_effect). 350 memcpy(fInheritedKey.get(), parent.fInheritedKey.get(), 351 parentCnt * sizeof(uint32_t)); 352 } 353 // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke) 354 GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale, 355 styleKeyFlags); 356 } 357 } 358 359 const SkPath* GrShape::originalPathForListeners() const { 360 if (fInheritedPathForListeners.isValid()) { 361 return fInheritedPathForListeners.get(); 362 } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) { 363 return &fPathData.fPath; 364 } 365 return nullptr; 366 } 367 368 void GrShape::addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener> listener) const { 369 if (const auto* lp = this->originalPathForListeners()) { 370 SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener)); 371 } 372 } 373 374 GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees, 375 bool useCenter, const GrStyle& style) { 376 GrShape result; 377 result.changeType(Type::kArc); 378 result.fArcData.fOval = oval; 379 result.fArcData.fStartAngleDegrees = startAngleDegrees; 380 result.fArcData.fSweepAngleDegrees = sweepAngleDegrees; 381 result.fArcData.fUseCenter = useCenter; 382 result.fArcData.fInverted = false; 383 result.fStyle = style; 384 result.attemptToSimplifyArc(); 385 return result; 386 } 387 388 GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) { 389 const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr; 390 this->initType(that.fType, thatPath); 391 switch (fType) { 392 case Type::kEmpty: 393 break; 394 case Type::kInvertedEmpty: 395 break; 396 case Type::kRRect: 397 fRRectData = that.fRRectData; 398 break; 399 case Type::kArc: 400 fArcData = that.fArcData; 401 break; 402 case Type::kLine: 403 fLineData = that.fLineData; 404 break; 405 case Type::kPath: 406 fPathData.fGenID = that.fPathData.fGenID; 407 break; 408 } 409 fInheritedKey.reset(that.fInheritedKey.count()); 410 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 411 sizeof(uint32_t) * fInheritedKey.count()); 412 if (that.fInheritedPathForListeners.isValid()) { 413 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get()); 414 } 415 } 416 417 GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) { 418 // TODO: Add some quantization of scale for better cache performance here or leave that up 419 // to caller? 420 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel 421 // stroke of a rect). 422 if (!parent.style().applies() || 423 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) { 424 this->initType(Type::kEmpty); 425 *this = parent; 426 return; 427 } 428 429 SkPathEffect* pe = parent.fStyle.pathEffect(); 430 SkTLazy<SkPath> tmpPath; 431 const GrShape* parentForKey = &parent; 432 SkTLazy<GrShape> tmpParent; 433 this->initType(Type::kPath); 434 fPathData.fGenID = 0; 435 if (pe) { 436 const SkPath* srcForPathEffect; 437 if (parent.fType == Type::kPath) { 438 srcForPathEffect = &parent.path(); 439 } else { 440 srcForPathEffect = tmpPath.init(); 441 parent.asPath(tmpPath.get()); 442 } 443 // Should we consider bounds? Would have to include in key, but it'd be nice to know 444 // if the bounds actually modified anything before including in key. 445 SkStrokeRec strokeRec = parent.fStyle.strokeRec(); 446 if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect, 447 scale)) { 448 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr)); 449 *this = tmpParent.get()->applyStyle(apply, scale); 450 return; 451 } 452 // A path effect has access to change the res scale but we aren't expecting it to and it 453 // would mess up our key computation. 454 SkASSERT(scale == strokeRec.getResScale()); 455 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) { 456 // The intermediate shape may not be a general path. If we we're just applying 457 // the path effect then attemptToReduceFromPath would catch it. This means that 458 // when we subsequently applied the remaining strokeRec we would have a non-path 459 // parent shape that would be used to determine the the stroked path's key. 460 // We detect that case here and change parentForKey to a temporary that represents 461 // the simpler shape so that applying both path effect and the strokerec all at 462 // once produces the same key. 463 tmpParent.init(this->path(), GrStyle(strokeRec, nullptr)); 464 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale); 465 if (!tmpPath.isValid()) { 466 tmpPath.init(); 467 } 468 tmpParent.get()->asPath(tmpPath.get()); 469 SkStrokeRec::InitStyle fillOrHairline; 470 // The parent shape may have simplified away the strokeRec, check for that here. 471 if (tmpParent.get()->style().applies()) { 472 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline, 473 *tmpPath.get(), scale)); 474 } else if (tmpParent.get()->style().isSimpleFill()) { 475 fillOrHairline = SkStrokeRec::kFill_InitStyle; 476 } else { 477 SkASSERT(tmpParent.get()->style().isSimpleHairline()); 478 fillOrHairline = SkStrokeRec::kHairline_InitStyle; 479 } 480 fStyle.resetToInitStyle(fillOrHairline); 481 parentForKey = tmpParent.get(); 482 } else { 483 fStyle = GrStyle(strokeRec, nullptr); 484 } 485 } else { 486 const SkPath* srcForParentStyle; 487 if (parent.fType == Type::kPath) { 488 srcForParentStyle = &parent.path(); 489 } else { 490 srcForParentStyle = tmpPath.init(); 491 parent.asPath(tmpPath.get()); 492 } 493 SkStrokeRec::InitStyle fillOrHairline; 494 SkASSERT(parent.fStyle.applies()); 495 SkASSERT(!parent.fStyle.pathEffect()); 496 SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle, 497 scale)); 498 fStyle.resetToInitStyle(fillOrHairline); 499 } 500 if (parent.fInheritedPathForListeners.isValid()) { 501 fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get()); 502 } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) { 503 fInheritedPathForListeners.set(parent.fPathData.fPath); 504 } 505 this->attemptToSimplifyPath(); 506 this->setInheritedKey(*parentForKey, apply, scale); 507 } 508 509 void GrShape::attemptToSimplifyPath() { 510 SkRect rect; 511 SkRRect rrect; 512 SkPath::Direction rrectDir; 513 unsigned rrectStart; 514 bool inverted = this->path().isInverseFillType(); 515 SkPoint pts[2]; 516 if (this->path().isEmpty()) { 517 // Dashing ignores inverseness skbug.com/5421. 518 this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty 519 : Type::kEmpty); 520 } else if (this->path().isLine(pts)) { 521 this->changeType(Type::kLine); 522 fLineData.fPts[0] = pts[0]; 523 fLineData.fPts[1] = pts[1]; 524 fLineData.fInverted = inverted; 525 } else if (SkPathPriv::IsRRect(this->path(), &rrect, &rrectDir, &rrectStart)) { 526 this->changeType(Type::kRRect); 527 fRRectData.fRRect = rrect; 528 fRRectData.fDir = rrectDir; 529 fRRectData.fStart = rrectStart; 530 fRRectData.fInverted = inverted; 531 SkASSERT(!fRRectData.fRRect.isEmpty()); 532 } else if (SkPathPriv::IsOval(this->path(), &rect, &rrectDir, &rrectStart)) { 533 this->changeType(Type::kRRect); 534 fRRectData.fRRect.setOval(rect); 535 fRRectData.fDir = rrectDir; 536 fRRectData.fInverted = inverted; 537 // convert from oval indexing to rrect indexiing. 538 fRRectData.fStart = 2 * rrectStart; 539 } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) { 540 this->changeType(Type::kRRect); 541 // When there is a path effect we restrict rect detection to the narrower API that 542 // gives us the starting position. Otherwise, we will retry with the more aggressive 543 // isRect(). 544 fRRectData.fRRect.setRect(rect); 545 fRRectData.fInverted = inverted; 546 fRRectData.fDir = rrectDir; 547 // convert from rect indexing to rrect indexiing. 548 fRRectData.fStart = 2 * rrectStart; 549 } else if (!this->style().hasPathEffect()) { 550 bool closed; 551 if (this->path().isRect(&rect, &closed, nullptr)) { 552 if (closed || this->style().isSimpleFill()) { 553 this->changeType(Type::kRRect); 554 fRRectData.fRRect.setRect(rect); 555 // Since there is no path effect the dir and start index is immaterial. 556 fRRectData.fDir = kDefaultRRectDir; 557 fRRectData.fStart = kDefaultRRectStart; 558 // There isn't dashing so we will have to preserver inverseness. 559 fRRectData.fInverted = inverted; 560 } 561 } 562 } 563 if (Type::kPath != fType) { 564 fInheritedKey.reset(0); 565 // Whenever we simplify to a non-path, break the chain so we no longer refer to the 566 // original path. This prevents attaching genID listeners to temporary paths created when 567 // drawing simple shapes. 568 fInheritedPathForListeners.reset(); 569 if (Type::kRRect == fType) { 570 this->attemptToSimplifyRRect(); 571 } else if (Type::kLine == fType) { 572 this->attemptToSimplifyLine(); 573 } 574 } else { 575 if (fInheritedKey.count() || this->path().isVolatile()) { 576 fPathData.fGenID = 0; 577 } else { 578 fPathData.fGenID = this->path().getGenerationID(); 579 } 580 if (!this->style().hasNonDashPathEffect()) { 581 if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style || 582 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) { 583 // Stroke styles don't differentiate between winding and even/odd. 584 // Moreover, dashing ignores inverseness (skbug.com/5421) 585 bool inverse = !this->style().isDashed() && this->path().isInverseFillType(); 586 if (inverse) { 587 this->path().setFillType(kDefaultPathInverseFillType); 588 } else { 589 this->path().setFillType(kDefaultPathFillType); 590 } 591 } else if (this->path().isConvex()) { 592 // There is no distinction between even/odd and non-zero winding count for convex 593 // paths. 594 if (this->path().isInverseFillType()) { 595 this->path().setFillType(kDefaultPathInverseFillType); 596 } else { 597 this->path().setFillType(kDefaultPathFillType); 598 } 599 } 600 } 601 } 602 } 603 604 void GrShape::attemptToSimplifyRRect() { 605 SkASSERT(Type::kRRect == fType); 606 SkASSERT(!fInheritedKey.count()); 607 if (fRRectData.fRRect.isEmpty()) { 608 // An empty filled rrect is equivalent to a filled empty path with inversion preserved. 609 if (fStyle.isSimpleFill()) { 610 fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty; 611 fStyle = GrStyle::SimpleFill(); 612 return; 613 } 614 // Dashing a rrect with no width or height is equivalent to filling an emtpy path. 615 // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length 616 // line will produce cap geometry if the effect begins in an "on" interval. 617 if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) { 618 // Dashing ignores the inverseness (currently). skbug.com/5421. 619 fType = Type::kEmpty; 620 fStyle = GrStyle::SimpleFill(); 621 return; 622 } 623 } 624 if (!this->style().hasPathEffect()) { 625 fRRectData.fDir = kDefaultRRectDir; 626 fRRectData.fStart = kDefaultRRectStart; 627 } else if (fStyle.isDashed()) { 628 // Dashing ignores the inverseness (currently). skbug.com/5421 629 fRRectData.fInverted = false; 630 // Possible TODO here: Check whether the dash results in a single arc or line. 631 } 632 // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts. 633 if (!fStyle.hasPathEffect() && 634 fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style && 635 fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join && 636 fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 && 637 fRRectData.fRRect.isRect()) { 638 SkScalar r = fStyle.strokeRec().getWidth() / 2; 639 fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r)); 640 fStyle = GrStyle::SimpleFill(); 641 } 642 } 643 644 void GrShape::attemptToSimplifyLine() { 645 SkASSERT(Type::kLine == fType); 646 SkASSERT(!fInheritedKey.count()); 647 if (fStyle.isDashed()) { 648 bool allOffsZero = true; 649 for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) { 650 allOffsZero = !fStyle.dashIntervals()[i]; 651 } 652 if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) { 653 return; 654 } 655 // Dashing ignores inverseness. 656 fLineData.fInverted = false; 657 return; 658 } else if (fStyle.hasPathEffect()) { 659 return; 660 } 661 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 662 // Make stroke + fill be stroke since the fill is empty. 663 SkStrokeRec rec = fStyle.strokeRec(); 664 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); 665 fStyle = GrStyle(rec, nullptr); 666 } 667 if (fStyle.isSimpleFill()) { 668 this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty); 669 return; 670 } 671 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style && 672 this->attemptToSimplifyStrokedLineToRRect()) { 673 return; 674 } 675 // Only path effects could care about the order of the points. Otherwise canonicalize 676 // the point order. 677 SkPoint* pts = fLineData.fPts; 678 if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) { 679 using std::swap; 680 swap(pts[0], pts[1]); 681 } 682 } 683 684 void GrShape::attemptToSimplifyArc() { 685 SkASSERT(fType == Type::kArc); 686 SkASSERT(!fArcData.fInverted); 687 if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) { 688 this->changeType(Type::kEmpty); 689 return; 690 } 691 692 // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses 693 // the full circle and doesn't use the center point is an oval. Unless it has square or round 694 // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're 695 // ignoring that for now. 696 if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter && 697 fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) { 698 if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) { 699 auto oval = fArcData.fOval; 700 this->changeType(Type::kRRect); 701 this->fRRectData.fRRect.setOval(oval); 702 this->fRRectData.fDir = kDefaultRRectDir; 703 this->fRRectData.fStart = kDefaultRRectStart; 704 this->fRRectData.fInverted = false; 705 return; 706 } 707 } 708 if (!fStyle.pathEffect()) { 709 // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always 710 // positive. 711 if (fArcData.fSweepAngleDegrees < 0) { 712 fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees; 713 fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees; 714 } 715 } 716 if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) { 717 this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f); 718 } 719 // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to 720 // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke 721 // could as well if the stroke fills the center. 722 } 723 724 bool GrShape::attemptToSimplifyStrokedLineToRRect() { 725 SkASSERT(Type::kLine == fType); 726 SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style); 727 728 SkRect rect; 729 SkVector outset; 730 // If we allowed a rotation angle for rrects we could capture all cases here. 731 if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) { 732 rect.fLeft = SkTMin(fLineData.fPts[0].fX, fLineData.fPts[1].fX); 733 rect.fRight = SkTMax(fLineData.fPts[0].fX, fLineData.fPts[1].fX); 734 rect.fTop = rect.fBottom = fLineData.fPts[0].fY; 735 outset.fY = fStyle.strokeRec().getWidth() / 2.f; 736 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY; 737 } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) { 738 rect.fTop = SkTMin(fLineData.fPts[0].fY, fLineData.fPts[1].fY); 739 rect.fBottom = SkTMax(fLineData.fPts[0].fY, fLineData.fPts[1].fY); 740 rect.fLeft = rect.fRight = fLineData.fPts[0].fX; 741 outset.fX = fStyle.strokeRec().getWidth() / 2.f; 742 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX; 743 } else { 744 return false; 745 } 746 rect.outset(outset.fX, outset.fY); 747 if (rect.isEmpty()) { 748 this->changeType(Type::kEmpty); 749 fStyle = GrStyle::SimpleFill(); 750 return true; 751 } 752 SkRRect rrect; 753 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { 754 SkASSERT(outset.fX == outset.fY); 755 rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY); 756 } else { 757 rrect = SkRRect::MakeRect(rect); 758 } 759 bool inverted = fLineData.fInverted && !fStyle.hasPathEffect(); 760 this->changeType(Type::kRRect); 761 fRRectData.fRRect = rrect; 762 fRRectData.fInverted = inverted; 763 fRRectData.fDir = kDefaultRRectDir; 764 fRRectData.fStart = kDefaultRRectStart; 765 fStyle = GrStyle::SimpleFill(); 766 return true; 767 } 768