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 18/** 19 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with. 20 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry 21 * reflects the styling information (e.g. is stroked). It is also possible to apply just the 22 * path effect from the style. In this case the resulting shape will include any remaining 23 * stroking information that is to be applied after the path effect. 24 * 25 * Shapes can produce keys that represent only the geometry information, not the style. Note that 26 * when styling information is applied to produce a new shape then the style has been converted 27 * to geometric information and is included in the new shape's key. When the same style is applied 28 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes 29 * will be the same. 30 * 31 * Currently this can only be constructed from a path, rect, or rrect though it can become a path 32 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries 33 * that have fast paths in the GPU backend. 34 */ 35class GrShape { 36public: 37 // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed 38 // to have to worry about this. This value is exposed for unit tests. 39 static constexpr int kMaxKeyFromDataVerbCnt = 10; 40 41 GrShape() { this->initType(Type::kEmpty); } 42 43 explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {} 44 45 explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {} 46 47 explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {} 48 49 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) { 50 this->initType(Type::kPath, &path); 51 this->attemptToSimplifyPath(); 52 } 53 54 GrShape(const SkRRect& rrect, const GrStyle& style) 55 : 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 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 84 GrShape(const SkRect& rect, const GrStyle& style) 85 : fStyle(style) { 86 this->initType(Type::kRRect); 87 fRRectData.fRRect = SkRRect::MakeRect(rect); 88 fRRectData.fInverted = false; 89 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(), 90 &fRRectData.fDir); 91 this->attemptToSimplifyRRect(); 92 } 93 94 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) { 95 this->initType(Type::kPath, &path); 96 this->attemptToSimplifyPath(); 97 } 98 99 GrShape(const SkRRect& rrect, const SkPaint& paint) 100 : 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 109 GrShape(const SkRect& rect, const SkPaint& paint) 110 : fStyle(paint) { 111 this->initType(Type::kRRect); 112 fRRectData.fRRect = SkRRect::MakeRect(rect); 113 fRRectData.fInverted = false; 114 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(), 115 &fRRectData.fDir); 116 this->attemptToSimplifyRRect(); 117 } 118 119 GrShape(const GrShape&); 120 GrShape& operator=(const GrShape& that); 121 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 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 */ 150 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { 151 return GrShape(*this, apply, scale); 152 } 153 154 /** Returns the unstyled geometry as a rrect if possible. */ 155 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const { 156 if (Type::kRRect != fType) { 157 return false; 158 } 159 if (rrect) { 160 *rrect = fRRectData.fRRect; 161 } 162 if (dir) { 163 *dir = fRRectData.fDir; 164 } 165 if (start) { 166 *start = fRRectData.fStart; 167 } 168 if (inverted) { 169 *inverted = fRRectData.fInverted; 170 } 171 return true; 172 } 173 174 /** 175 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. 176 * An inverse filled line path is still considered a line. 177 */ 178 bool asLine(SkPoint pts[2], bool* inverted) const { 179 if (fType != Type::kLine) { 180 return false; 181 } 182 if (pts) { 183 pts[0] = fLineData.fPts[0]; 184 pts[1] = fLineData.fPts[1]; 185 } 186 if (inverted) { 187 *inverted = fLineData.fInverted; 188 } 189 return true; 190 } 191 192 /** Returns the unstyled geometry as a path. */ 193 void asPath(SkPath* out) const { 194 switch (fType) { 195 case Type::kEmpty: 196 out->reset(); 197 break; 198 case Type::kInvertedEmpty: 199 out->reset(); 200 out->setFillType(kDefaultPathInverseFillType); 201 break; 202 case Type::kRRect: 203 out->reset(); 204 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart); 205 // Below matches the fill type that attemptToSimplifyPath uses. 206 if (fRRectData.fInverted) { 207 out->setFillType(kDefaultPathInverseFillType); 208 } else { 209 out->setFillType(kDefaultPathFillType); 210 } 211 break; 212 case Type::kLine: 213 out->reset(); 214 out->moveTo(fLineData.fPts[0]); 215 out->lineTo(fLineData.fPts[1]); 216 if (fLineData.fInverted) { 217 out->setFillType(kDefaultPathInverseFillType); 218 } else { 219 out->setFillType(kDefaultPathFillType); 220 } 221 break; 222 case Type::kPath: 223 *out = this->path(); 224 break; 225 } 226 } 227 228 /** 229 * Returns whether the geometry is empty. Note that applying the style could produce a 230 * non-empty shape. It also may have an inverse fill. 231 */ 232 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; } 233 234 /** 235 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores 236 * the inverse fill nature of the geometry. 237 */ 238 SkRect bounds() const; 239 240 /** 241 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill 242 * status). 243 */ 244 SkRect styledBounds() const; 245 246 /** 247 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise 248 * convex path is considered to be closed if they styling reflects a fill and not otherwise. 249 * This is because filling closes all contours in the path. 250 */ 251 bool knownToBeConvex() const { 252 switch (fType) { 253 case Type::kEmpty: 254 return true; 255 case Type::kInvertedEmpty: 256 return true; 257 case Type::kRRect: 258 return true; 259 case Type::kLine: 260 return true; 261 case Type::kPath: 262 // SkPath.isConvex() really means "is this path convex were it to be closed" and 263 // thus doesn't give the correct answer for stroked paths, hence we also check 264 // whether the path is either filled or closed. Convex paths may only have one 265 // contour hence isLastContourClosed() is a sufficient for a convex path. 266 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) && 267 this->path().isConvex(); 268 } 269 return false; 270 } 271 272 /** Is the pre-styled geometry inverse filled? */ 273 bool inverseFilled() const { 274 bool ret = false; 275 switch (fType) { 276 case Type::kEmpty: 277 ret = false; 278 break; 279 case Type::kInvertedEmpty: 280 ret = true; 281 break; 282 case Type::kRRect: 283 ret = fRRectData.fInverted; 284 break; 285 case Type::kLine: 286 ret = fLineData.fInverted; 287 break; 288 case Type::kPath: 289 ret = this->path().isInverseFillType(); 290 break; 291 } 292 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 293 SkASSERT(!(ret && this->style().isDashed())); 294 return ret; 295 } 296 297 /** 298 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in 299 * because an arbitrary path effect could produce an inverse filled path. In other cases this 300 * can be thought of as "inverseFilledAfterStyling()". 301 */ 302 bool mayBeInverseFilledAfterStyling() const { 303 // An arbitrary path effect can produce an arbitrary output path, which may be inverse 304 // filled. 305 if (this->style().hasNonDashPathEffect()) { 306 return true; 307 } 308 return this->inverseFilled(); 309 } 310 311 /** 312 * Is it known that the unstyled geometry has no unclosed contours. This means that it will 313 * not have any caps if stroked (modulo the effect of any path effect). 314 */ 315 bool knownToBeClosed() const { 316 switch (fType) { 317 case Type::kEmpty: 318 return true; 319 case Type::kInvertedEmpty: 320 return true; 321 case Type::kRRect: 322 return true; 323 case Type::kLine: 324 return false; 325 case Type::kPath: 326 // SkPath doesn't keep track of the closed status of each contour. 327 return SkPathPriv::IsClosedSingleContour(this->path()); 328 } 329 return false; 330 } 331 332 uint32_t segmentMask() const { 333 switch (fType) { 334 case Type::kEmpty: 335 return 0; 336 case Type::kInvertedEmpty: 337 return 0; 338 case Type::kRRect: 339 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) { 340 return SkPath::kConic_SegmentMask; 341 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type || 342 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) { 343 return SkPath::kLine_SegmentMask; 344 } 345 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask; 346 case Type::kLine: 347 return SkPath::kLine_SegmentMask; 348 case Type::kPath: 349 return this->path().getSegmentMasks(); 350 } 351 return 0; 352 } 353 354 /** 355 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling). 356 * A negative value is returned if the shape has no key (shouldn't be cached). 357 */ 358 int unstyledKeySize() const; 359 360 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } 361 362 /** 363 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough 364 * space allocated for the key and that unstyledKeySize() does not return a negative value 365 * for this shape. 366 */ 367 void writeUnstyledKey(uint32_t* key) const; 368 369 /** 370 * Adds a listener to the *original* path. Typically used to invalidate cached resources when 371 * a path is no longer in-use. If the shape started out as something other than a path, this 372 * does nothing (but will delete the listener). 373 */ 374 void addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const; 375 376 /** 377 * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get 378 * the generation ID of the *original* path. This is the path that will receive 379 * GenIDChangeListeners added to this shape. 380 */ 381 uint32_t testingOnly_getOriginalGenerationID() const; 382 bool testingOnly_isPath() const; 383 bool testingOnly_isNonVolatilePath() const; 384 385private: 386 enum class Type { 387 kEmpty, 388 kInvertedEmpty, 389 kRRect, 390 kLine, 391 kPath, 392 }; 393 394 void initType(Type type, const SkPath* path = nullptr) { 395 fType = Type::kEmpty; 396 this->changeType(type, path); 397 } 398 399 void changeType(Type type, const SkPath* path = nullptr) { 400 bool wasPath = Type::kPath == fType; 401 fType = type; 402 bool isPath = Type::kPath == type; 403 SkASSERT(!path || isPath); 404 if (wasPath && !isPath) { 405 fPathData.fPath.~SkPath(); 406 } else if (!wasPath && isPath) { 407 if (path) { 408 new (&fPathData.fPath) SkPath(*path); 409 } else { 410 new (&fPathData.fPath) SkPath(); 411 } 412 } else if (isPath && path) { 413 fPathData.fPath = *path; 414 } 415 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath. 416 fPathData.fGenID = 0; 417 } 418 419 SkPath& path() { 420 SkASSERT(Type::kPath == fType); 421 return fPathData.fPath; 422 } 423 424 const SkPath& path() const { 425 SkASSERT(Type::kPath == fType); 426 return fPathData.fPath; 427 } 428 429 /** Constructor used by the applyStyle() function */ 430 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 431 432 /** 433 * Determines the key we should inherit from the input shape's geometry and style when 434 * we are applying the style to create a new shape. 435 */ 436 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 437 438 void attemptToSimplifyPath(); 439 void attemptToSimplifyRRect(); 440 void attemptToSimplifyLine(); 441 442 bool attemptToSimplifyStrokedLineToRRect(); 443 444 /** Gets the path that gen id listeners should be added to. */ 445 const SkPath* originalPathForListeners() const; 446 447 // Defaults to use when there is no distinction between even/odd and winding fills. 448 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType; 449 static constexpr SkPath::FillType kDefaultPathInverseFillType = 450 SkPath::kInverseEvenOdd_FillType; 451 452 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction; 453 static constexpr unsigned kDefaultRRectStart = 0; 454 455 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect, 456 SkPath::Direction* dir) { 457 *dir = kDefaultRRectDir; 458 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise 459 // beginning at index 0 (which happens to correspond to rrect index 0 or 7). 460 if (!hasPathEffect) { 461 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 462 return kDefaultRRectStart; 463 } 464 // In SkPath a rect starts at index 0 by default. This is the top left corner. However, 465 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the 466 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort. 467 bool swapX = rect.fLeft > rect.fRight; 468 bool swapY = rect.fTop > rect.fBottom; 469 if (swapX && swapY) { 470 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices. 471 return 2 * 2; 472 } else if (swapX) { 473 *dir = SkPath::kCCW_Direction; 474 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices. 475 return 2 * 1; 476 } else if (swapY) { 477 *dir = SkPath::kCCW_Direction; 478 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices. 479 return 2 * 3; 480 } 481 return 0; 482 } 483 484 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect, 485 SkPath::Direction* dir) { 486 // This comes from SkPath's interface. The default for adding a SkRRect to a path is 487 // clockwise beginning at starting index 6. 488 static constexpr unsigned kPathRRectStartIdx = 6; 489 *dir = kDefaultRRectDir; 490 if (!hasPathEffect) { 491 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 492 return kDefaultRRectStart; 493 } 494 return kPathRRectStartIdx; 495 } 496 497 Type fType; 498 union { 499 struct { 500 SkRRect fRRect; 501 SkPath::Direction fDir; 502 unsigned fStart; 503 bool fInverted; 504 } fRRectData; 505 struct { 506 SkPath fPath; 507 // Gen ID of the original path (fPath may be modified) 508 int32_t fGenID; 509 } fPathData; 510 struct { 511 SkPoint fPts[2]; 512 bool fInverted; 513 } fLineData; 514 }; 515 GrStyle fStyle; 516 SkTLazy<SkPath> fInheritedPathForListeners; 517 SkAutoSTArray<8, uint32_t> fInheritedKey; 518}; 519#endif 520