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