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 #ifndef GrShape_DEFINED 9 #define GrShape_DEFINED 10 11 #include "include/core/SkPath.h" 12 #include "include/core/SkRRect.h" 13 #include "include/private/SkTemplates.h" 14 #include "src/core/SkPathPriv.h" 15 #include "src/core/SkTLazy.h" 16 #include "src/gpu/GrStyle.h" 17 #include <new> 18 19 class SkIDChangeListener; 20 21 /** 22 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with. 23 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry 24 * reflects the styling information (e.g. is stroked). It is also possible to apply just the 25 * path effect from the style. In this case the resulting shape will include any remaining 26 * stroking information that is to be applied after the path effect. 27 * 28 * Shapes can produce keys that represent only the geometry information, not the style. Note that 29 * when styling information is applied to produce a new shape then the style has been converted 30 * to geometric information and is included in the new shape's key. When the same style is applied 31 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes 32 * will be the same. 33 * 34 * Currently this can only be constructed from a path, rect, or rrect though it can become a path 35 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries 36 * that have fast paths in the GPU backend. 37 */ 38 class GrShape { 39 public: 40 // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed 41 // to have to worry about this. This value is exposed for unit tests. 42 static constexpr int kMaxKeyFromDataVerbCnt = 10; 43 GrShape()44 GrShape() { this->initType(Type::kEmpty); } 45 GrShape(const SkPath & path)46 explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {} 47 GrShape(const SkRRect & rrect)48 explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {} 49 GrShape(const SkRect & rect)50 explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {} 51 GrShape(const SkPath & path,const GrStyle & style)52 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) { 53 this->initType(Type::kPath, &path); 54 this->attemptToSimplifyPath(); 55 } 56 GrShape(const SkRRect & rrect,const GrStyle & style)57 GrShape(const SkRRect& rrect, const GrStyle& style) : fStyle(style) { 58 this->initType(Type::kRRect); 59 fRRectData.fRRect = rrect; 60 fRRectData.fInverted = false; 61 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(), 62 &fRRectData.fDir); 63 this->attemptToSimplifyRRect(); 64 } 65 GrShape(const SkRRect & rrect,SkPathDirection dir,unsigned start,bool inverted,const GrStyle & style)66 GrShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted, 67 const GrStyle& style) 68 : fStyle(style) { 69 this->initType(Type::kRRect); 70 fRRectData.fRRect = rrect; 71 fRRectData.fInverted = inverted; 72 if (style.pathEffect()) { 73 fRRectData.fDir = dir; 74 fRRectData.fStart = start; 75 if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) { 76 fRRectData.fStart = (fRRectData.fStart + 1) & 0b110; 77 } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) { 78 fRRectData.fStart &= 0b110; 79 } 80 } else { 81 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir); 82 } 83 this->attemptToSimplifyRRect(); 84 } 85 GrShape(const SkRect & rect,const GrStyle & style)86 GrShape(const SkRect& rect, const GrStyle& style) : fStyle(style) { 87 this->initType(Type::kRRect); 88 fRRectData.fRRect = SkRRect::MakeRect(rect); 89 fRRectData.fInverted = false; 90 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(), 91 &fRRectData.fDir); 92 this->attemptToSimplifyRRect(); 93 } 94 GrShape(const SkPath & path,const SkPaint & paint)95 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) { 96 this->initType(Type::kPath, &path); 97 this->attemptToSimplifyPath(); 98 } 99 GrShape(const SkRRect & rrect,const SkPaint & paint)100 GrShape(const SkRRect& rrect, const SkPaint& paint) : fStyle(paint) { 101 this->initType(Type::kRRect); 102 fRRectData.fRRect = rrect; 103 fRRectData.fInverted = false; 104 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(), 105 &fRRectData.fDir); 106 this->attemptToSimplifyRRect(); 107 } 108 GrShape(const SkRect & rect,const SkPaint & paint)109 GrShape(const SkRect& rect, const SkPaint& paint) : fStyle(paint) { 110 this->initType(Type::kRRect); 111 fRRectData.fRRect = SkRRect::MakeRect(rect); 112 fRRectData.fInverted = false; 113 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(), 114 &fRRectData.fDir); 115 this->attemptToSimplifyRRect(); 116 } 117 118 static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees, 119 SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style); 120 121 GrShape(const GrShape&); 122 GrShape& operator=(const GrShape& that); 123 ~GrShape()124 ~GrShape() { this->changeType(Type::kEmpty); } 125 126 /** 127 * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled 128 * version of the shape. 129 */ 130 enum class FillInversion { 131 kPreserve, 132 kFlip, 133 kForceNoninverted, 134 kForceInverted 135 }; 136 /** 137 * Makes a filled shape from the pre-styled original shape and optionally modifies whether 138 * the fill is inverted or not. It's important to note that the original shape's geometry 139 * may already have been modified if doing so was neutral with respect to its style 140 * (e.g. filled paths are always closed when stored in a shape and dashed paths are always 141 * made non-inverted since dashing ignores inverseness). 142 */ 143 static GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve); 144 style()145 const GrStyle& style() const { return fStyle; } 146 147 /** 148 * Returns a shape that has either applied the path effect or path effect and stroking 149 * information from this shape's style to its geometry. Scale is used when approximating the 150 * output geometry and typically is computed from the view matrix 151 */ applyStyle(GrStyle::Apply apply,SkScalar scale)152 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { 153 return GrShape(*this, apply, scale); 154 } 155 isRect()156 bool isRect() const { 157 if (Type::kRRect != fType) { 158 return false; 159 } 160 161 return fRRectData.fRRect.isRect(); 162 } 163 164 /** Returns the unstyled geometry as a rrect if possible. */ asRRect(SkRRect * rrect,SkPathDirection * dir,unsigned * start,bool * inverted)165 bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const { 166 if (Type::kRRect != fType) { 167 return false; 168 } 169 if (rrect) { 170 *rrect = fRRectData.fRRect; 171 } 172 if (dir) { 173 *dir = fRRectData.fDir; 174 } 175 if (start) { 176 *start = fRRectData.fStart; 177 } 178 if (inverted) { 179 *inverted = fRRectData.fInverted; 180 } 181 return true; 182 } 183 184 /** 185 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. 186 * An inverse filled line path is still considered a line. 187 */ asLine(SkPoint pts[2],bool * inverted)188 bool asLine(SkPoint pts[2], bool* inverted) const { 189 if (fType != Type::kLine) { 190 return false; 191 } 192 if (pts) { 193 pts[0] = fLineData.fPts[0]; 194 pts[1] = fLineData.fPts[1]; 195 } 196 if (inverted) { 197 *inverted = fLineData.fInverted; 198 } 199 return true; 200 } 201 202 /** Returns the unstyled geometry as a path. */ asPath(SkPath * out)203 void asPath(SkPath* out) const { 204 switch (fType) { 205 case Type::kEmpty: 206 out->reset(); 207 break; 208 case Type::kInvertedEmpty: 209 out->reset(); 210 out->setFillType(kDefaultPathInverseFillType); 211 break; 212 case Type::kRRect: 213 out->reset(); 214 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart); 215 // Below matches the fill type that attemptToSimplifyPath uses. 216 if (fRRectData.fInverted) { 217 out->setFillType(kDefaultPathInverseFillType); 218 } else { 219 out->setFillType(kDefaultPathFillType); 220 } 221 break; 222 case Type::kArc: 223 SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees, 224 fArcData.fSweepAngleDegrees, fArcData.fUseCenter, 225 fStyle.isSimpleFill()); 226 if (fArcData.fInverted) { 227 out->setFillType(kDefaultPathInverseFillType); 228 } else { 229 out->setFillType(kDefaultPathFillType); 230 } 231 break; 232 case Type::kLine: 233 out->reset(); 234 out->moveTo(fLineData.fPts[0]); 235 out->lineTo(fLineData.fPts[1]); 236 if (fLineData.fInverted) { 237 out->setFillType(kDefaultPathInverseFillType); 238 } else { 239 out->setFillType(kDefaultPathFillType); 240 } 241 break; 242 case Type::kPath: 243 *out = this->path(); 244 break; 245 } 246 } 247 248 // Can this shape be drawn as a pair of filled nested rectangles? asNestedRects(SkRect rects[2])249 bool asNestedRects(SkRect rects[2]) const { 250 if (Type::kPath != fType) { 251 return false; 252 } 253 254 // TODO: it would be better two store DRRects natively in the shape rather than converting 255 // them to a path and then reextracting the nested rects 256 if (this->path().isInverseFillType()) { 257 return false; 258 } 259 260 SkPathDirection dirs[2]; 261 if (!SkPathPriv::IsNestedFillRects(this->path(), rects, dirs)) { 262 return false; 263 } 264 265 if (SkPathFillType::kWinding == this->path().getFillType() && dirs[0] == dirs[1]) { 266 // The two rects need to be wound opposite to each other 267 return false; 268 } 269 270 // Right now, nested rects where the margin is not the same width 271 // all around do not render correctly 272 const SkScalar* outer = rects[0].asScalars(); 273 const SkScalar* inner = rects[1].asScalars(); 274 275 bool allEq = true; 276 277 SkScalar margin = SkScalarAbs(outer[0] - inner[0]); 278 bool allGoE1 = margin >= SK_Scalar1; 279 280 for (int i = 1; i < 4; ++i) { 281 SkScalar temp = SkScalarAbs(outer[i] - inner[i]); 282 if (temp < SK_Scalar1) { 283 allGoE1 = false; 284 } 285 if (!SkScalarNearlyEqual(margin, temp)) { 286 allEq = false; 287 } 288 } 289 290 return allEq || allGoE1; 291 } 292 293 /** 294 * Returns whether the geometry is empty. Note that applying the style could produce a 295 * non-empty shape. It also may have an inverse fill. 296 */ isEmpty()297 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; } 298 299 /** 300 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores 301 * the inverse fill nature of the geometry. 302 */ 303 SkRect bounds() const; 304 305 /** 306 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill 307 * status). 308 */ 309 SkRect styledBounds() const; 310 311 /** 312 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise 313 * convex path is considered to be closed if they styling reflects a fill and not otherwise. 314 * This is because filling closes all contours in the path. 315 */ knownToBeConvex()316 bool knownToBeConvex() const { 317 switch (fType) { 318 case Type::kEmpty: 319 return true; 320 case Type::kInvertedEmpty: 321 return true; 322 case Type::kRRect: 323 return true; 324 case Type::kArc: 325 return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees, 326 SkToBool(fArcData.fUseCenter), 327 fStyle.isSimpleFill()); 328 case Type::kLine: 329 return true; 330 case Type::kPath: 331 // SkPath.isConvex() really means "is this path convex were it to be closed" and 332 // thus doesn't give the correct answer for stroked paths, hence we also check 333 // whether the path is either filled or closed. Convex paths may only have one 334 // contour hence isLastContourClosed() is a sufficient for a convex path. 335 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) && 336 this->path().isConvex(); 337 } 338 return false; 339 } 340 341 /** 342 * Does the shape have a known winding direction. Some degenerate convex shapes may not have 343 * a computable direction, but this is not always a requirement for path renderers so it is 344 * kept separate from knownToBeConvex(). 345 */ knownDirection()346 bool knownDirection() const { 347 switch (fType) { 348 case Type::kEmpty: 349 return true; 350 case Type::kInvertedEmpty: 351 return true; 352 case Type::kRRect: 353 return true; 354 case Type::kArc: 355 return true; 356 case Type::kLine: 357 return true; 358 case Type::kPath: 359 // Assuming this is called after knownToBeConvex(), this should just be relying on 360 // cached convexity and direction and will be cheap. 361 return !SkPathPriv::CheapIsFirstDirection(this->path(), 362 SkPathPriv::kUnknown_FirstDirection); 363 } 364 return false; 365 } 366 367 /** Is the pre-styled geometry inverse filled? */ inverseFilled()368 bool inverseFilled() const { 369 bool ret = false; 370 switch (fType) { 371 case Type::kEmpty: 372 ret = false; 373 break; 374 case Type::kInvertedEmpty: 375 ret = true; 376 break; 377 case Type::kRRect: 378 ret = fRRectData.fInverted; 379 break; 380 case Type::kArc: 381 ret = fArcData.fInverted; 382 break; 383 case Type::kLine: 384 ret = fLineData.fInverted; 385 break; 386 case Type::kPath: 387 ret = this->path().isInverseFillType(); 388 break; 389 } 390 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 391 SkASSERT(!(ret && this->style().isDashed())); 392 return ret; 393 } 394 395 /** 396 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in 397 * because an arbitrary path effect could produce an inverse filled path. In other cases this 398 * can be thought of as "inverseFilledAfterStyling()". 399 */ mayBeInverseFilledAfterStyling()400 bool mayBeInverseFilledAfterStyling() const { 401 // An arbitrary path effect can produce an arbitrary output path, which may be inverse 402 // filled. 403 if (this->style().hasNonDashPathEffect()) { 404 return true; 405 } 406 return this->inverseFilled(); 407 } 408 409 /** 410 * Is it known that the unstyled geometry has no unclosed contours. This means that it will 411 * not have any caps if stroked (modulo the effect of any path effect). 412 */ knownToBeClosed()413 bool knownToBeClosed() const { 414 switch (fType) { 415 case Type::kEmpty: 416 return true; 417 case Type::kInvertedEmpty: 418 return true; 419 case Type::kRRect: 420 return true; 421 case Type::kArc: 422 return fArcData.fUseCenter; 423 case Type::kLine: 424 return false; 425 case Type::kPath: 426 // SkPath doesn't keep track of the closed status of each contour. 427 return SkPathPriv::IsClosedSingleContour(this->path()); 428 } 429 return false; 430 } 431 segmentMask()432 uint32_t segmentMask() const { 433 switch (fType) { 434 case Type::kEmpty: 435 return 0; 436 case Type::kInvertedEmpty: 437 return 0; 438 case Type::kRRect: 439 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) { 440 return SkPath::kConic_SegmentMask; 441 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type || 442 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) { 443 return SkPath::kLine_SegmentMask; 444 } 445 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask; 446 case Type::kArc: 447 if (fArcData.fUseCenter) { 448 return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask; 449 } 450 return SkPath::kConic_SegmentMask; 451 case Type::kLine: 452 return SkPath::kLine_SegmentMask; 453 case Type::kPath: 454 return this->path().getSegmentMasks(); 455 } 456 return 0; 457 } 458 459 /** 460 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling). 461 * A negative value is returned if the shape has no key (shouldn't be cached). 462 */ 463 int unstyledKeySize() const; 464 hasUnstyledKey()465 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } 466 467 /** 468 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough 469 * space allocated for the key and that unstyledKeySize() does not return a negative value 470 * for this shape. 471 */ 472 void writeUnstyledKey(uint32_t* key) const; 473 474 /** 475 * Adds a listener to the *original* path. Typically used to invalidate cached resources when 476 * a path is no longer in-use. If the shape started out as something other than a path, this 477 * does nothing. 478 */ 479 void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const; 480 481 /** 482 * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get 483 * the generation ID of the *original* path. This is the path that will receive 484 * GenIDChangeListeners added to this shape. 485 */ 486 uint32_t testingOnly_getOriginalGenerationID() const; 487 bool testingOnly_isPath() const; 488 bool testingOnly_isNonVolatilePath() const; 489 490 private: 491 enum class Type { 492 kEmpty, 493 kInvertedEmpty, 494 kRRect, 495 kArc, 496 kLine, 497 kPath, 498 }; 499 500 void initType(Type type, const SkPath* path = nullptr) { 501 fType = Type::kEmpty; 502 this->changeType(type, path); 503 } 504 505 void changeType(Type type, const SkPath* path = nullptr) { 506 bool wasPath = Type::kPath == fType; 507 fType = type; 508 bool isPath = Type::kPath == type; 509 SkASSERT(!path || isPath); 510 if (wasPath && !isPath) { 511 fPathData.fPath.~SkPath(); 512 } else if (!wasPath && isPath) { 513 if (path) { 514 new (&fPathData.fPath) SkPath(*path); 515 } else { 516 new (&fPathData.fPath) SkPath(); 517 } 518 } else if (isPath && path) { 519 fPathData.fPath = *path; 520 } 521 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath. 522 fPathData.fGenID = 0; 523 } 524 path()525 SkPath& path() { 526 SkASSERT(Type::kPath == fType); 527 return fPathData.fPath; 528 } 529 path()530 const SkPath& path() const { 531 SkASSERT(Type::kPath == fType); 532 return fPathData.fPath; 533 } 534 535 /** Constructor used by the applyStyle() function */ 536 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 537 538 /** 539 * Determines the key we should inherit from the input shape's geometry and style when 540 * we are applying the style to create a new shape. 541 */ 542 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 543 544 void attemptToSimplifyPath(); 545 void attemptToSimplifyRRect(); 546 void attemptToSimplifyLine(); 547 void attemptToSimplifyArc(); 548 549 bool attemptToSimplifyStrokedLineToRRect(); 550 551 /** Gets the path that gen id listeners should be added to. */ 552 const SkPath* originalPathForListeners() const; 553 554 // Defaults to use when there is no distinction between even/odd and winding fills. 555 static constexpr SkPathFillType kDefaultPathFillType = SkPathFillType::kEvenOdd; 556 static constexpr SkPathFillType kDefaultPathInverseFillType = SkPathFillType::kInverseEvenOdd; 557 558 static constexpr SkPathDirection kDefaultRRectDir = SkPathDirection::kCW; 559 static constexpr unsigned kDefaultRRectStart = 0; 560 DefaultRectDirAndStartIndex(const SkRect & rect,bool hasPathEffect,SkPathDirection * dir)561 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect, 562 SkPathDirection* dir) { 563 *dir = kDefaultRRectDir; 564 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise 565 // beginning at index 0 (which happens to correspond to rrect index 0 or 7). 566 if (!hasPathEffect) { 567 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 568 return kDefaultRRectStart; 569 } 570 // In SkPath a rect starts at index 0 by default. This is the top left corner. However, 571 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the 572 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort. 573 bool swapX = rect.fLeft > rect.fRight; 574 bool swapY = rect.fTop > rect.fBottom; 575 if (swapX && swapY) { 576 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices. 577 return 2 * 2; 578 } else if (swapX) { 579 *dir = SkPathDirection::kCCW; 580 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices. 581 return 2 * 1; 582 } else if (swapY) { 583 *dir = SkPathDirection::kCCW; 584 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices. 585 return 2 * 3; 586 } 587 return 0; 588 } 589 DefaultRRectDirAndStartIndex(const SkRRect & rrect,bool hasPathEffect,SkPathDirection * dir)590 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect, 591 SkPathDirection* dir) { 592 // This comes from SkPath's interface. The default for adding a SkRRect to a path is 593 // clockwise beginning at starting index 6. 594 static constexpr unsigned kPathRRectStartIdx = 6; 595 *dir = kDefaultRRectDir; 596 if (!hasPathEffect) { 597 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 598 return kDefaultRRectStart; 599 } 600 return kPathRRectStartIdx; 601 } 602 603 union { 604 struct { 605 SkRRect fRRect; 606 SkPathDirection fDir; 607 unsigned fStart; 608 bool fInverted; 609 } fRRectData; 610 struct { 611 SkRect fOval; 612 SkScalar fStartAngleDegrees; 613 SkScalar fSweepAngleDegrees; 614 int16_t fUseCenter; 615 int16_t fInverted; 616 } fArcData; 617 struct { 618 SkPath fPath; 619 // Gen ID of the original path (fPath may be modified) 620 int32_t fGenID; 621 } fPathData; 622 struct { 623 SkPoint fPts[2]; 624 bool fInverted; 625 } fLineData; 626 }; 627 GrStyle fStyle; 628 SkTLazy<SkPath> fInheritedPathForListeners; 629 SkAutoSTArray<8, uint32_t> fInheritedKey; 630 Type fType; 631 }; 632 #endif 633