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