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 SkCanvas::draw APIs. 34 */ 35 class GrShape { 36 public: 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 GrShape()41 GrShape() { this->initType(Type::kEmpty); } 42 GrShape(const SkPath & path)43 explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {} 44 GrShape(const SkRRect & rrect)45 explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {} 46 GrShape(const SkRect & rect)47 explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {} 48 GrShape(const SkPath & path,const GrStyle & style)49 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) { 50 this->initType(Type::kPath, &path); 51 this->attemptToSimplifyPath(); 52 } 53 GrShape(const SkRRect & rrect,const GrStyle & style)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 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) 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 GrShape(const SkPath & path,const SkPaint & paint)94 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) { 95 this->initType(Type::kPath, &path); 96 this->attemptToSimplifyPath(); 97 } 98 GrShape(const SkRRect & rrect,const SkPaint & paint)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 GrShape(const SkRect & rect,const SkPaint & paint)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 ~GrShape()122 ~GrShape() { this->changeType(Type::kEmpty); } 123 style()124 const GrStyle& style() const { return fStyle; } 125 126 /** 127 * Returns a shape that has either applied the path effect or path effect and stroking 128 * information from this shape's style to its geometry. Scale is used when approximating the 129 * output geometry and typically is computed from the view matrix 130 */ applyStyle(GrStyle::Apply apply,SkScalar scale)131 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { 132 return GrShape(*this, apply, scale); 133 } 134 135 /** Returns the unstyled geometry as a rrect if possible. */ asRRect(SkRRect * rrect,SkPath::Direction * dir,unsigned * start,bool * inverted)136 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const { 137 if (Type::kRRect != fType) { 138 return false; 139 } 140 if (rrect) { 141 *rrect = fRRectData.fRRect; 142 } 143 if (dir) { 144 *dir = fRRectData.fDir; 145 } 146 if (start) { 147 *start = fRRectData.fStart; 148 } 149 if (inverted) { 150 *inverted = fRRectData.fInverted; 151 } 152 return true; 153 } 154 155 /** 156 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. 157 * An inverse filled line path is still considered a line. 158 */ asLine(SkPoint pts[2],bool * inverted)159 bool asLine(SkPoint pts[2], bool* inverted) const { 160 if (fType != Type::kLine) { 161 return false; 162 } 163 if (pts) { 164 pts[0] = fLineData.fPts[0]; 165 pts[1] = fLineData.fPts[1]; 166 } 167 if (inverted) { 168 *inverted = fLineData.fInverted; 169 } 170 return true; 171 } 172 173 /** Returns the unstyled geometry as a path. */ asPath(SkPath * out)174 void asPath(SkPath* out) const { 175 switch (fType) { 176 case Type::kEmpty: 177 out->reset(); 178 break; 179 case Type::kRRect: 180 out->reset(); 181 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart); 182 // Below matches the fill type that attemptToSimplifyPath uses. 183 if (fRRectData.fInverted) { 184 out->setFillType(kDefaultPathInverseFillType); 185 } else { 186 out->setFillType(kDefaultPathFillType); 187 } 188 break; 189 case Type::kLine: 190 out->reset(); 191 out->moveTo(fLineData.fPts[0]); 192 out->lineTo(fLineData.fPts[1]); 193 if (fLineData.fInverted) { 194 out->setFillType(kDefaultPathInverseFillType); 195 } else { 196 out->setFillType(kDefaultPathFillType); 197 } 198 break; 199 case Type::kPath: 200 *out = this->path(); 201 break; 202 } 203 } 204 205 /** 206 * Returns whether the geometry is empty. Note that applying the style could produce a 207 * non-empty shape. 208 */ isEmpty()209 bool isEmpty() const { return Type::kEmpty == fType; } 210 211 /** 212 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores 213 * the inverse fill nature of the geometry. 214 */ 215 SkRect bounds() const; 216 217 /** 218 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill 219 * status). 220 */ 221 SkRect styledBounds() const; 222 223 /** 224 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise 225 * convex path is considered to be closed if they styling reflects a fill and not otherwise. 226 * This is because filling closes all contours in the path. 227 */ knownToBeConvex()228 bool knownToBeConvex() const { 229 switch (fType) { 230 case Type::kEmpty: 231 return true; 232 case Type::kRRect: 233 return true; 234 case Type::kLine: 235 return true; 236 case Type::kPath: 237 // SkPath.isConvex() really means "is this path convex were it to be closed" and 238 // thus doesn't give the correct answer for stroked paths, hence we also check 239 // whether the path is either filled or closed. Convex paths may only have one 240 // contour hence isLastContourClosed() is a sufficient for a convex path. 241 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) && 242 this->path().isConvex(); 243 } 244 return false; 245 } 246 247 /** Is the pre-styled geometry inverse filled? */ inverseFilled()248 bool inverseFilled() const { 249 bool ret = false; 250 switch (fType) { 251 case Type::kEmpty: 252 ret = false; 253 break; 254 case Type::kRRect: 255 ret = fRRectData.fInverted; 256 break; 257 case Type::kLine: 258 ret = fLineData.fInverted; 259 break; 260 case Type::kPath: 261 ret = this->path().isInverseFillType(); 262 break; 263 } 264 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 265 SkASSERT(!(ret && this->style().isDashed())); 266 return ret; 267 } 268 269 /** 270 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in 271 * because an arbitrary path effect could produce an inverse filled path. In other cases this 272 * can be thought of as "inverseFilledAfterStyling()". 273 */ mayBeInverseFilledAfterStyling()274 bool mayBeInverseFilledAfterStyling() const { 275 // An arbitrary path effect can produce an arbitrary output path, which may be inverse 276 // filled. 277 if (this->style().hasNonDashPathEffect()) { 278 return true; 279 } 280 return this->inverseFilled(); 281 } 282 283 /** 284 * Is it known that the unstyled geometry has no unclosed contours. This means that it will 285 * not have any caps if stroked (modulo the effect of any path effect). 286 */ knownToBeClosed()287 bool knownToBeClosed() const { 288 switch (fType) { 289 case Type::kEmpty: 290 return true; 291 case Type::kRRect: 292 return true; 293 case Type::kLine: 294 return false; 295 case Type::kPath: 296 // SkPath doesn't keep track of the closed status of each contour. 297 return SkPathPriv::IsClosedSingleContour(this->path()); 298 } 299 return false; 300 } 301 segmentMask()302 uint32_t segmentMask() const { 303 switch (fType) { 304 case Type::kEmpty: 305 return 0; 306 case Type::kRRect: 307 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) { 308 return SkPath::kConic_SegmentMask; 309 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) { 310 return SkPath::kLine_SegmentMask; 311 } 312 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask; 313 case Type::kLine: 314 return SkPath::kLine_SegmentMask; 315 case Type::kPath: 316 return this->path().getSegmentMasks(); 317 } 318 return 0; 319 } 320 321 /** 322 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling). 323 * A negative value is returned if the shape has no key (shouldn't be cached). 324 */ 325 int unstyledKeySize() const; 326 hasUnstyledKey()327 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } 328 329 /** 330 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough 331 * space allocated for the key and that unstyledKeySize() does not return a negative value 332 * for this shape. 333 */ 334 void writeUnstyledKey(uint32_t* key) const; 335 336 private: 337 enum class Type { 338 kEmpty, 339 kRRect, 340 kLine, 341 kPath, 342 }; 343 344 void initType(Type type, const SkPath* path = nullptr) { 345 fType = Type::kEmpty; 346 this->changeType(type, path); 347 } 348 349 void changeType(Type type, const SkPath* path = nullptr) { 350 bool wasPath = Type::kPath == fType; 351 fType = type; 352 bool isPath = Type::kPath == type; 353 SkASSERT(!path || isPath); 354 if (wasPath && !isPath) { 355 fPathData.fPath.~SkPath(); 356 } else if (!wasPath && isPath) { 357 if (path) { 358 new (&fPathData.fPath) SkPath(*path); 359 } else { 360 new (&fPathData.fPath) SkPath(); 361 } 362 } else if (isPath && path) { 363 fPathData.fPath = *path; 364 } 365 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath. 366 fPathData.fGenID = 0; 367 } 368 path()369 SkPath& path() { 370 SkASSERT(Type::kPath == fType); 371 return fPathData.fPath; 372 } 373 path()374 const SkPath& path() const { 375 SkASSERT(Type::kPath == fType); 376 return fPathData.fPath; 377 } 378 379 /** Constructor used by the applyStyle() function */ 380 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 381 382 /** 383 * Determines the key we should inherit from the input shape's geometry and style when 384 * we are applying the style to create a new shape. 385 */ 386 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale); 387 388 void attemptToSimplifyPath(); 389 void attemptToSimplifyRRect(); 390 void attemptToSimplifyLine(); 391 392 // Defaults to use when there is no distinction between even/odd and winding fills. 393 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType; 394 static constexpr SkPath::FillType kDefaultPathInverseFillType = 395 SkPath::kInverseEvenOdd_FillType; 396 397 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction; 398 static constexpr unsigned kDefaultRRectStart = 0; 399 DefaultRectDirAndStartIndex(const SkRect & rect,bool hasPathEffect,SkPath::Direction * dir)400 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect, 401 SkPath::Direction* dir) { 402 *dir = kDefaultRRectDir; 403 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise 404 // beginning at index 0 (which happens to correspond to rrect index 0 or 7). 405 if (!hasPathEffect) { 406 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 407 return kDefaultRRectStart; 408 } 409 // In SkPath a rect starts at index 0 by default. This is the top left corner. However, 410 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the 411 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort. 412 bool swapX = rect.fLeft > rect.fRight; 413 bool swapY = rect.fTop > rect.fBottom; 414 if (swapX && swapY) { 415 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices. 416 return 2 * 2; 417 } else if (swapX) { 418 *dir = SkPath::kCCW_Direction; 419 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices. 420 return 2 * 1; 421 } else if (swapY) { 422 *dir = SkPath::kCCW_Direction; 423 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices. 424 return 2 * 3; 425 } 426 return 0; 427 } 428 DefaultRRectDirAndStartIndex(const SkRRect & rrect,bool hasPathEffect,SkPath::Direction * dir)429 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect, 430 SkPath::Direction* dir) { 431 // This comes from SkPath's interface. The default for adding a SkRRect to a path is 432 // clockwise beginning at starting index 6. 433 static constexpr unsigned kPathRRectStartIdx = 6; 434 *dir = kDefaultRRectDir; 435 if (!hasPathEffect) { 436 // It doesn't matter what start we use, just be consistent to avoid redundant keys. 437 return kDefaultRRectStart; 438 } 439 return kPathRRectStartIdx; 440 } 441 442 Type fType; 443 union { 444 struct { 445 SkRRect fRRect; 446 SkPath::Direction fDir; 447 unsigned fStart; 448 bool fInverted; 449 } fRRectData; 450 struct { 451 SkPath fPath; 452 // Gen ID of the original path (fPath may be modified) 453 int32_t fGenID; 454 } fPathData; 455 struct { 456 SkPoint fPts[2]; 457 bool fInverted; 458 } fLineData; 459 }; 460 GrStyle fStyle; 461 SkAutoSTArray<8, uint32_t> fInheritedKey; 462 }; 463 #endif 464