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 "src/gpu/geometry/GrStyledShape.h" 9 10#include "include/private/SkIDChangeListener.h" 11 12#include <utility> 13 14GrStyledShape& GrStyledShape::operator=(const GrStyledShape& that) { 15 fShape = that.fShape; 16 fStyle = that.fStyle; 17 fGenID = that.fGenID; 18 fSimplified = that.fSimplified; 19 20 fInheritedKey.reset(that.fInheritedKey.count()); 21 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 22 sizeof(uint32_t) * fInheritedKey.count()); 23 if (that.fInheritedPathForListeners.isValid()) { 24 fInheritedPathForListeners.set(*that.fInheritedPathForListeners); 25 } else { 26 fInheritedPathForListeners.reset(); 27 } 28 return *this; 29} 30 31static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion) { 32 switch (inversion) { 33 case GrStyledShape::FillInversion::kPreserve: 34 return originalIsInverted; 35 case GrStyledShape::FillInversion::kFlip: 36 return !originalIsInverted; 37 case GrStyledShape::FillInversion::kForceInverted: 38 return true; 39 case GrStyledShape::FillInversion::kForceNoninverted: 40 return false; 41 } 42 return false; 43} 44 45GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion) { 46 bool newIsInverted = is_inverted(original.fShape.inverted(), inversion); 47 if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) { 48 // By returning the original rather than falling through we can preserve any inherited style 49 // key. Otherwise, we wipe it out below since the style change invalidates it. 50 return original; 51 } 52 GrStyledShape result; 53 SkASSERT(result.fStyle.isSimpleFill()); 54 if (original.fInheritedPathForListeners.isValid()) { 55 result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners); 56 } 57 58 result.fShape = original.fShape; 59 result.fGenID = original.fGenID; 60 result.fShape.setInverted(newIsInverted); 61 62 if (!original.style().isSimpleFill()) { 63 // Going from a non-filled style to fill may allow additional simplifications (e.g. 64 // closing an open rect that wasn't closed in the original shape because it had 65 // stroke style). 66 result.simplify(); 67 // The above simplify() call only sets simplified to true if its geometry was changed, 68 // since it already sees its style as a simple fill. Since the original style was not a 69 // simple fill, MakeFilled always simplifies. 70 result.fSimplified = true; 71 } 72 73 // Verify that lines/points were converted to empty by the style change 74 SkASSERT((!original.fShape.isLine() && !original.fShape.isPoint()) || result.fShape.isEmpty()); 75 76 // We don't copy the inherited key since it can contain path effect information that we just 77 // stripped. 78 return result; 79} 80 81SkRect GrStyledShape::styledBounds() const { 82 if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) { 83 return SkRect::MakeEmpty(); 84 } 85 86 SkRect bounds; 87 fStyle.adjustBounds(&bounds, this->bounds()); 88 return bounds; 89} 90 91// If the path is small enough to be keyed from its data this returns key length, otherwise -1. 92static int path_key_from_data_size(const SkPath& path) { 93 const int verbCnt = path.countVerbs(); 94 if (verbCnt > GrStyledShape::kMaxKeyFromDataVerbCnt) { 95 return -1; 96 } 97 const int pointCnt = path.countPoints(); 98 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 99 100 static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 101 static_assert(sizeof(SkScalar) == sizeof(uint32_t)); 102 // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to 103 // a uint32_t length. 104 return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt; 105} 106 107// Writes the path data key into the passed pointer. 108static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) { 109 uint32_t* key = origKey; 110 // The check below should take care of negative values casted positive. 111 const int verbCnt = path.countVerbs(); 112 const int pointCnt = path.countPoints(); 113 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 114 SkASSERT(verbCnt <= GrStyledShape::kMaxKeyFromDataVerbCnt); 115 SkASSERT(pointCnt && verbCnt); 116 *key++ = verbCnt; 117 memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t)); 118 int verbKeySize = SkAlign4(verbCnt); 119 // pad out to uint32_t alignment using value that will stand out when debugging. 120 uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt; 121 memset(pad, 0xDE, verbKeySize - verbCnt); 122 key += verbKeySize >> 2; 123 124 memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt); 125 static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 126 key += 2 * pointCnt; 127 sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt); 128 static_assert(sizeof(SkScalar) == sizeof(uint32_t)); 129 SkDEBUGCODE(key += conicWeightCnt); 130 SkASSERT(key - origKey == path_key_from_data_size(path)); 131} 132 133int GrStyledShape::unstyledKeySize() const { 134 if (fInheritedKey.count()) { 135 return fInheritedKey.count(); 136 } 137 138 int count = 1; // Every key has the state flags from the GrShape 139 switch(fShape.type()) { 140 case GrShape::Type::kPoint: 141 static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t)); 142 count += sizeof(SkPoint) / sizeof(uint32_t); 143 break; 144 case GrShape::Type::kRect: 145 static_assert(0 == sizeof(SkRect) % sizeof(uint32_t)); 146 count += sizeof(SkRect) / sizeof(uint32_t); 147 break; 148 case GrShape::Type::kRRect: 149 static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); 150 count += SkRRect::kSizeInMemory / sizeof(uint32_t); 151 break; 152 case GrShape::Type::kArc: 153 static_assert(0 == sizeof(GrArc) % sizeof(uint32_t)); 154 count += sizeof(GrArc) / sizeof(uint32_t); 155 break; 156 case GrShape::Type::kLine: 157 static_assert(0 == sizeof(GrLineSegment) % sizeof(uint32_t)); 158 count += sizeof(GrLineSegment) / sizeof(uint32_t); 159 break; 160 case GrShape::Type::kPath: { 161 if (0 == fGenID) { 162 return -1; // volatile, so won't be keyed 163 } 164 int dataKeySize = path_key_from_data_size(fShape.path()); 165 if (dataKeySize >= 0) { 166 count += dataKeySize; 167 } else { 168 count++; // Just adds the gen ID. 169 } 170 break; } 171 default: 172 // else it's empty, which just needs the state flags for its key 173 SkASSERT(fShape.isEmpty()); 174 } 175 return count; 176} 177 178void GrStyledShape::writeUnstyledKey(uint32_t* key) const { 179 SkASSERT(this->unstyledKeySize()); 180 SkDEBUGCODE(uint32_t* origKey = key;) 181 if (fInheritedKey.count()) { 182 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count()); 183 SkDEBUGCODE(key += fInheritedKey.count();) 184 } else { 185 // Dir and start are only used for rect and rrect shapes, so are not included in other 186 // shape type keys. Make sure that they are the defaults for other shapes so it doesn't 187 // matter that we universally include them in the flag key value. 188 SkASSERT((fShape.isRect() || fShape.isRRect()) || 189 (fShape.dir() == GrShape::kDefaultDir && 190 fShape.startIndex() == GrShape::kDefaultStart)); 191 192 // Every key starts with the state from the GrShape (this includes path fill type, 193 // and any tracked winding, start, inversion, as well as the class of geometry). 194 *key++ = fShape.stateKey(); 195 196 switch(fShape.type()) { 197 case GrShape::Type::kPath: { 198 SkASSERT(fGenID != 0); 199 // Ensure that the path's inversion matches our state so that the path's key suffices. 200 SkASSERT(fShape.inverted() == fShape.path().isInverseFillType()); 201 202 int dataKeySize = path_key_from_data_size(fShape.path()); 203 if (dataKeySize >= 0) { 204 write_path_key_from_data(fShape.path(), key); 205 return; 206 } else { 207 *key++ = fGenID; 208 } 209 break; } 210 case GrShape::Type::kPoint: 211 memcpy(key, &fShape.point(), sizeof(SkPoint)); 212 key += sizeof(SkPoint) / sizeof(uint32_t); 213 break; 214 case GrShape::Type::kRect: 215 memcpy(key, &fShape.rect(), sizeof(SkRect)); 216 key += sizeof(SkRect) / sizeof(uint32_t); 217 break; 218 case GrShape::Type::kRRect: 219 fShape.rrect().writeToMemory(key); 220 key += SkRRect::kSizeInMemory / sizeof(uint32_t); 221 break; 222 case GrShape::Type::kArc: 223 // Write dense floats first 224 memcpy(key, &fShape.arc(), sizeof(SkRect) + 2 * sizeof(float)); 225 key += (sizeof(GrArc) / sizeof(uint32_t) - 1); 226 // Then write the final bool as an int, to make sure upper bits are set 227 *key++ = fShape.arc().fUseCenter ? 1 : 0; 228 break; 229 case GrShape::Type::kLine: 230 memcpy(key, &fShape.line(), sizeof(GrLineSegment)); 231 key += sizeof(GrLineSegment) / sizeof(uint32_t); 232 break; 233 default: 234 // Nothing other than the flag state is needed in the key for an empty shape 235 SkASSERT(fShape.isEmpty()); 236 } 237 } 238 SkASSERT(key - origKey == this->unstyledKeySize()); 239} 240 241void GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply apply, 242 SkScalar scale) { 243 SkASSERT(!fInheritedKey.count()); 244 // If the output shape turns out to be simple, then we will just use its geometric key 245 if (fShape.isPath()) { 246 // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as 247 // ApplyFullStyle(shape). 248 // The full key is structured as (geo,path_effect,stroke). 249 // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then 250 // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key 251 // and then append the style key (which should now be stroke only) at the end. 252 int parentCnt = parent.fInheritedKey.count(); 253 bool useParentGeoKey = !parentCnt; 254 if (useParentGeoKey) { 255 parentCnt = parent.unstyledKeySize(); 256 if (parentCnt < 0) { 257 // The parent's geometry has no key so we will have no key. 258 fGenID = 0; 259 return; 260 } 261 } 262 uint32_t styleKeyFlags = 0; 263 if (parent.knownToBeClosed()) { 264 styleKeyFlags |= GrStyle::kClosed_KeyFlag; 265 } 266 if (parent.asLine(nullptr, nullptr)) { 267 styleKeyFlags |= GrStyle::kNoJoins_KeyFlag; 268 } 269 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); 270 if (styleCnt < 0) { 271 // The style doesn't allow a key, set the path gen ID to 0 so that we fail when 272 // we try to get a key for the shape. 273 fGenID = 0; 274 return; 275 } 276 fInheritedKey.reset(parentCnt + styleCnt); 277 if (useParentGeoKey) { 278 // This will be the geo key. 279 parent.writeUnstyledKey(fInheritedKey.get()); 280 } else { 281 // This should be (geo,path_effect). 282 memcpy(fInheritedKey.get(), parent.fInheritedKey.get(), 283 parentCnt * sizeof(uint32_t)); 284 } 285 // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke) 286 GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale, 287 styleKeyFlags); 288 } 289} 290 291const SkPath* GrStyledShape::originalPathForListeners() const { 292 if (fInheritedPathForListeners.isValid()) { 293 return fInheritedPathForListeners.get(); 294 } else if (fShape.isPath() && !fShape.path().isVolatile()) { 295 return &fShape.path(); 296 } 297 return nullptr; 298} 299 300void GrStyledShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const { 301 if (const auto* lp = this->originalPathForListeners()) { 302 SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener)); 303 } 304} 305 306GrStyledShape GrStyledShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, 307 SkScalar sweepAngleDegrees, bool useCenter, 308 const GrStyle& style, DoSimplify doSimplify) { 309 GrStyledShape result; 310 result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter}); 311 result.fStyle = style; 312 if (doSimplify == DoSimplify::kYes) { 313 result.simplify(); 314 } 315 return result; 316} 317 318GrStyledShape::GrStyledShape(const GrStyledShape& that) 319 : fShape(that.fShape) 320 , fStyle(that.fStyle) 321 , fGenID(that.fGenID) 322 , fSimplified(that.fSimplified) { 323 fInheritedKey.reset(that.fInheritedKey.count()); 324 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 325 sizeof(uint32_t) * fInheritedKey.count()); 326 if (that.fInheritedPathForListeners.isValid()) { 327 fInheritedPathForListeners.set(*that.fInheritedPathForListeners); 328 } 329} 330 331GrStyledShape::GrStyledShape(const GrStyledShape& parent, GrStyle::Apply apply, SkScalar scale) { 332 // TODO: Add some quantization of scale for better cache performance here or leave that up 333 // to caller? 334 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel 335 // stroke of a rect). 336 if (!parent.style().applies() || 337 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) { 338 *this = parent; 339 return; 340 } 341 342 SkPathEffect* pe = parent.fStyle.pathEffect(); 343 SkTLazy<SkPath> tmpPath; 344 const GrStyledShape* parentForKey = &parent; 345 SkTLazy<GrStyledShape> tmpParent; 346 347 // Start out as an empty path that is filled in by the applied style 348 fShape.setPath(SkPath()); 349 350 if (pe) { 351 const SkPath* srcForPathEffect; 352 if (parent.fShape.isPath()) { 353 srcForPathEffect = &parent.fShape.path(); 354 } else { 355 srcForPathEffect = tmpPath.init(); 356 parent.asPath(tmpPath.get()); 357 } 358 // Should we consider bounds? Would have to include in key, but it'd be nice to know 359 // if the bounds actually modified anything before including in key. 360 SkStrokeRec strokeRec = parent.fStyle.strokeRec(); 361 if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect, 362 scale)) { 363 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr)); 364 *this = tmpParent->applyStyle(apply, scale); 365 return; 366 } 367 // A path effect has access to change the res scale but we aren't expecting it to and it 368 // would mess up our key computation. 369 SkASSERT(scale == strokeRec.getResScale()); 370 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) { 371 // The intermediate shape may not be a general path. If we we're just applying 372 // the path effect then attemptToReduceFromPath would catch it. This means that 373 // when we subsequently applied the remaining strokeRec we would have a non-path 374 // parent shape that would be used to determine the the stroked path's key. 375 // We detect that case here and change parentForKey to a temporary that represents 376 // the simpler shape so that applying both path effect and the strokerec all at 377 // once produces the same key. 378 tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr)); 379 tmpParent->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale); 380 if (!tmpPath.isValid()) { 381 tmpPath.init(); 382 } 383 tmpParent->asPath(tmpPath.get()); 384 SkStrokeRec::InitStyle fillOrHairline; 385 // The parent shape may have simplified away the strokeRec, check for that here. 386 if (tmpParent->style().applies()) { 387 SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline, 388 *tmpPath.get(), scale)); 389 } else if (tmpParent->style().isSimpleFill()) { 390 fillOrHairline = SkStrokeRec::kFill_InitStyle; 391 } else { 392 SkASSERT(tmpParent.get()->style().isSimpleHairline()); 393 fillOrHairline = SkStrokeRec::kHairline_InitStyle; 394 } 395 fStyle.resetToInitStyle(fillOrHairline); 396 parentForKey = tmpParent.get(); 397 } else { 398 fStyle = GrStyle(strokeRec, nullptr); 399 } 400 } else { 401 const SkPath* srcForParentStyle; 402 if (parent.fShape.isPath()) { 403 srcForParentStyle = &parent.fShape.path(); 404 } else { 405 srcForParentStyle = tmpPath.init(); 406 parent.asPath(tmpPath.get()); 407 } 408 SkStrokeRec::InitStyle fillOrHairline; 409 SkASSERT(parent.fStyle.applies()); 410 SkASSERT(!parent.fStyle.pathEffect()); 411 SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline, 412 *srcForParentStyle, scale)); 413 fStyle.resetToInitStyle(fillOrHairline); 414 } 415 416 if (parent.fInheritedPathForListeners.isValid()) { 417 fInheritedPathForListeners.set(*parent.fInheritedPathForListeners); 418 } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) { 419 fInheritedPathForListeners.set(parent.fShape.path()); 420 } 421 this->simplify(); 422 this->setInheritedKey(*parentForKey, apply, scale); 423} 424 425bool GrStyledShape::asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, 426 bool* inverted) const { 427 if (!fShape.isRRect() && !fShape.isRect()) { 428 return false; 429 } 430 431 // Validity check here, if we don't have a path effect on the style, we should have passed 432 // appropriate flags to GrShape::simplify() to have reset these parameters. 433 SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir && 434 fShape.startIndex() == GrShape::kDefaultStart)); 435 436 // If the shape is a regular rect, map to round rect winding parameters, including accounting 437 // for the automatic sorting of edges that SkRRect::MakeRect() performs. 438 if (fShape.isRect()) { 439 if (rrect) { 440 *rrect = SkRRect::MakeRect(fShape.rect()); 441 } 442 // Don't bother mapping these if we don't have a path effect, however. 443 if (!fStyle.hasPathEffect()) { 444 if (dir) { 445 *dir = GrShape::kDefaultDir; 446 } 447 if (start) { 448 *start = GrShape::kDefaultStart; 449 } 450 } else { 451 // In SkPath a rect starts at index 0 by default. This is the top left corner. However, 452 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the 453 // rect edges. Thus, we may need to modify the rrect's start index and direction. 454 SkPathDirection rectDir = fShape.dir(); 455 unsigned rectStart = fShape.startIndex(); 456 457 if (fShape.rect().fLeft > fShape.rect().fRight) { 458 // Toggle direction, and modify index by mapping through the array 459 static const unsigned kMapping[] = {1, 0, 3, 2}; 460 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW 461 : SkPathDirection::kCCW; 462 rectStart = kMapping[rectStart]; 463 } 464 if (fShape.rect().fTop > fShape.rect().fBottom) { 465 // Toggle direction and map index by 3 - start 466 // NOTE: if we earlier flipped for X as well, this results in no net direction 467 // change and effectively flipping the start index to the diagonal corners of the 468 // rect (matching what we'd expect for a rect with both X and Y flipped). 469 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW 470 : SkPathDirection::kCCW; 471 rectStart = 3 - rectStart; 472 } 473 474 if (dir) { 475 *dir = rectDir; 476 } 477 if (start) { 478 // Convert to round rect indexing 479 *start = 2 * rectStart; 480 } 481 } 482 } else { 483 // Straight forward export 484 if (rrect) { 485 *rrect = fShape.rrect(); 486 } 487 if (dir) { 488 *dir = fShape.dir(); 489 } 490 if (start) { 491 *start = fShape.startIndex(); 492 // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special 493 // but we do for dashing placement 494 if (fShape.rrect().isOval()) { 495 *start &= 0b110; 496 } 497 } 498 } 499 500 if (inverted) { 501 *inverted = fShape.inverted(); 502 } 503 504 return true; 505} 506 507bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const { 508 if (!fShape.isLine()) { 509 return false; 510 } 511 512 if (pts) { 513 pts[0] = fShape.line().fP1; 514 pts[1] = fShape.line().fP2; 515 } 516 if (inverted) { 517 *inverted = fShape.inverted(); 518 } 519 return true; 520} 521 522bool GrStyledShape::asNestedRects(SkRect rects[2]) const { 523 if (!fShape.isPath()) { 524 return false; 525 } 526 527 // TODO: it would be better two store DRRects natively in the shape rather than converting 528 // them to a path and then reextracting the nested rects 529 if (fShape.path().isInverseFillType()) { 530 return false; 531 } 532 533 SkPathDirection dirs[2]; 534 if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) { 535 return false; 536 } 537 538 if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) { 539 // The two rects need to be wound opposite to each other 540 return false; 541 } 542 543 // Right now, nested rects where the margin is not the same width 544 // all around do not render correctly 545 const SkScalar* outer = rects[0].asScalars(); 546 const SkScalar* inner = rects[1].asScalars(); 547 548 bool allEq = true; 549 550 SkScalar margin = SkScalarAbs(outer[0] - inner[0]); 551 bool allGoE1 = margin >= SK_Scalar1; 552 553 for (int i = 1; i < 4; ++i) { 554 SkScalar temp = SkScalarAbs(outer[i] - inner[i]); 555 if (temp < SK_Scalar1) { 556 allGoE1 = false; 557 } 558 if (!SkScalarNearlyEqual(margin, temp)) { 559 allEq = false; 560 } 561 } 562 563 return allEq || allGoE1; 564} 565 566class AutoRestoreInverseness { 567public: 568 AutoRestoreInverseness(GrShape* shape, const GrStyle& style) 569 // Dashing ignores inverseness skbug.com/5421. 570 : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {} 571 572 ~AutoRestoreInverseness() { 573 // Restore invertedness after any modifications were made to the shape type 574 fShape->setInverted(fInverted); 575 SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType()); 576 } 577 578private: 579 GrShape* fShape; 580 bool fInverted; 581}; 582 583void GrStyledShape::simplify() { 584 AutoRestoreInverseness ari(&fShape, fStyle); 585 586 unsigned simplifyFlags = 0; 587 if (fStyle.isSimpleFill()) { 588 simplifyFlags = GrShape::kAll_Flags; 589 } else if (!fStyle.hasPathEffect()) { 590 // Everything but arcs with caps that might extend beyond the oval edge can ignore winding 591 if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) { 592 simplifyFlags |= GrShape::kIgnoreWinding_Flag; 593 } 594 simplifyFlags |= GrShape::kMakeCanonical_Flag; 595 } // else if there's a path effect, every destructive simplification is disabledd 596 597 // Remember if the original shape was closed; in the event we simplify to a point or line 598 // because of degenerate geometry, we need to update joins and caps. 599 GrShape::Type oldType = fShape.type(); 600 fClosed = fShape.simplify(simplifyFlags); 601 fSimplified = oldType != fShape.type(); 602 603 if (fShape.isPath()) { 604 // The shape remains a path, so configure the gen ID and canonicalize fill type if possible 605 if (fInheritedKey.count() || fShape.path().isVolatile()) { 606 fGenID = 0; 607 } else { 608 fGenID = fShape.path().getGenerationID(); 609 } 610 if (!fStyle.hasNonDashPathEffect() && 611 (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style || 612 fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style || 613 fShape.path().isConvex())) { 614 // Stroke styles don't differentiate between winding and even/odd. There is no 615 // distinction between even/odd and non-zero winding count for convex paths. 616 // Moreover, dashing ignores inverseness (skbug.com/5421) 617 fShape.path().setFillType(GrShape::kDefaultFillType); 618 } 619 } else { 620 fInheritedKey.reset(0); 621 // Whenever we simplify to a non-path, break the chain so we no longer refer to the 622 // original path. This prevents attaching genID listeners to temporary paths created when 623 // drawing simple shapes. 624 fInheritedPathForListeners.reset(); 625 // Further simplifications to the shape based on the style 626 this->simplifyStroke(); 627 } 628} 629 630void GrStyledShape::simplifyStroke() { 631 AutoRestoreInverseness ari(&fShape, fStyle); 632 633 // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape 634 // becomes a round rect. 635 if (!fStyle.hasPathEffect() && fShape.isRect() && 636 fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 637 if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join || 638 (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join && 639 fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) { 640 // Bevel-stroked rect needs path rendering 641 return; 642 } 643 644 SkScalar r = fStyle.strokeRec().getWidth() / 2; 645 fShape.rect().outset(r, r); 646 if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) { 647 // There's no dashing to worry about if we got here, so it's okay that this resets 648 // winding parameters 649 fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r)); 650 } 651 fStyle = GrStyle::SimpleFill(); 652 fSimplified = true; 653 return; 654 } 655 656 // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the 657 // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce. 658 if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() || 659 fStyle.strokeRec().isHairlineStyle()) { 660 return; 661 } 662 663 // Tracks style simplifications, even if the geometry can't be further simplified. 664 bool styleSimplified = false; 665 if (fStyle.isDashed()) { 666 // For dashing a point, if the first interval is on, we can drop the dash and just draw 667 // the caps. For dashing a line, if every off interval is 0 length, its a stroke. 668 bool dropDash = false; 669 if (fShape.isPoint()) { 670 dropDash = fStyle.dashIntervalCnt() > 0 && 671 SkToBool(fStyle.dashIntervals()[0]); 672 } else { 673 dropDash = true; 674 for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) { 675 if (SkToBool(fStyle.dashIntervals()[i])) { 676 // An off interval has non-zero length so this won't convert to a simple line 677 dropDash = false; 678 break; 679 } 680 } 681 } 682 683 if (!dropDash) { 684 return; 685 } 686 // Fall through to modifying the shape to respect the new stroke geometry 687 fStyle = GrStyle(fStyle.strokeRec(), nullptr); 688 // Since the reduced the line or point after dashing is dependent on the caps of the dashes, 689 // we reset to be unclosed so we don't override the style based on joins later. 690 fClosed = false; 691 styleSimplified = true; 692 } 693 694 // At this point, we're a line or point with no path effects. Any fill portion of the style 695 // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke. 696 if (fStyle.isSimpleFill()) { 697 fShape.reset(); 698 fSimplified = true; 699 return; 700 } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 701 // Stroke only 702 SkStrokeRec rec = fStyle.strokeRec(); 703 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); 704 fStyle = GrStyle(rec, nullptr); 705 styleSimplified = true; 706 } 707 708 // A point or line that was formed by a degenerate closed shape needs its style updated to 709 // reflect the fact that it doesn't actually produce caps. 710 if (fClosed) { 711 SkPaint::Cap cap; 712 if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) { 713 // As a closed shape, the line moves from a to b and back to a, producing a 180 degree 714 // turn. With round joins, this would make a semi-circle at each end, which is visually 715 // identical to a round cap on the reduced line geometry. 716 cap = SkPaint::kRound_Cap; 717 } else { 718 // If this were a closed line, the 180 degree turn either is a miter join that exceeds 719 // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape 720 // of a 180 degreen corner is equivalent to a butt cap. 721 // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so 722 // it fits this closed line description (it is not two 90 degree turns that could 723 // produce miter geometry). 724 cap = SkPaint::kButt_Cap; 725 } 726 727 if (cap != fStyle.strokeRec().getCap() || 728 SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) { 729 SkStrokeRec rec = fStyle.strokeRec(); 730 rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter()); 731 fStyle = GrStyle(rec, nullptr); 732 styleSimplified = true; 733 } 734 } 735 736 if (fShape.isPoint()) { 737 // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point 738 // doesn't draw anything, a round cap is an oval and a square cap is a square. 739 if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) { 740 fShape.reset(); 741 } else { 742 SkScalar w = fStyle.strokeRec().getWidth() / 2.f; 743 SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY}; 744 r.outset(w, w); 745 746 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { 747 fShape.setRRect(SkRRect::MakeOval(r)); 748 } else { 749 fShape.setRect(r); 750 } 751 } 752 } else { 753 // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we 754 // allowed rotation angle, this would work for any lines. 755 SkRect rect; 756 SkVector outset; 757 if (fShape.line().fP1.fY == fShape.line().fP2.fY) { 758 rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX); 759 rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX); 760 rect.fTop = rect.fBottom = fShape.line().fP1.fY; 761 outset.fY = fStyle.strokeRec().getWidth() / 2.f; 762 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY; 763 } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) { 764 rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY); 765 rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY); 766 rect.fLeft = rect.fRight = fShape.line().fP1.fX; 767 outset.fX = fStyle.strokeRec().getWidth() / 2.f; 768 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX; 769 } else { 770 // Geometrically can't apply the style and turn into a fill, but might still be simpler 771 // than before based solely on changes to fStyle. 772 fSimplified |= styleSimplified; 773 return; 774 } 775 rect.outset(outset.fX, outset.fY); 776 if (rect.isEmpty()) { 777 fShape.reset(); 778 } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { 779 SkASSERT(outset.fX == outset.fY); 780 fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY)); 781 } else { 782 fShape.setRect(rect); 783 } 784 } 785 // If we made it here, the stroke was fully applied to the new shape so we can become a fill. 786 fStyle = GrStyle::SimpleFill(); 787 fSimplified = true; 788 return; 789} 790