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 "GrShape.h" 9 10 GrShape& GrShape::operator=(const GrShape& that) { 11 fStyle = that.fStyle; 12 this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr); 13 switch (fType) { 14 case Type::kEmpty: 15 break; 16 case Type::kRRect: 17 fRRectData = that.fRRectData; 18 break; 19 case Type::kLine: 20 fLineData = that.fLineData; 21 break; 22 case Type::kPath: 23 fPathData.fGenID = that.fPathData.fGenID; 24 break; 25 } 26 fInheritedKey.reset(that.fInheritedKey.count()); 27 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 28 sizeof(uint32_t) * fInheritedKey.count()); 29 return *this; 30 } 31 32 SkRect GrShape::bounds() const { 33 // Bounds where left == bottom or top == right can indicate a line or point shape. We return 34 // inverted bounds for a truly empty shape. 35 static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1); 36 switch (fType) { 37 case Type::kEmpty: 38 return kInverted; 39 case Type::kLine: { 40 SkRect bounds; 41 if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) { 42 bounds.fLeft = fLineData.fPts[0].fX; 43 bounds.fRight = fLineData.fPts[1].fX; 44 } else { 45 bounds.fLeft = fLineData.fPts[1].fX; 46 bounds.fRight = fLineData.fPts[0].fX; 47 } 48 if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) { 49 bounds.fTop = fLineData.fPts[0].fY; 50 bounds.fBottom = fLineData.fPts[1].fY; 51 } else { 52 bounds.fTop = fLineData.fPts[1].fY; 53 bounds.fBottom = fLineData.fPts[0].fY; 54 } 55 return bounds; 56 } 57 case Type::kRRect: 58 return fRRectData.fRRect.getBounds(); 59 case Type::kPath: 60 return this->path().getBounds(); 61 } 62 SkFAIL("Unknown shape type"); 63 return kInverted; 64 } 65 66 SkRect GrShape::styledBounds() const { 67 if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) { 68 return SkRect::MakeEmpty(); 69 } 70 SkRect bounds; 71 fStyle.adjustBounds(&bounds, this->bounds()); 72 return bounds; 73 } 74 75 // If the path is small enough to be keyed from its data this returns key length, otherwise -1. 76 static int path_key_from_data_size(const SkPath& path) { 77 const int verbCnt = path.countVerbs(); 78 if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) { 79 return -1; 80 } 81 const int pointCnt = path.countPoints(); 82 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 83 84 GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 85 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t)); 86 // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to 87 // a uint32_t length. 88 return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt; 89 } 90 91 // Writes the path data key into the passed pointer. 92 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) { 93 uint32_t* key = origKey; 94 // The check below should take care of negative values casted positive. 95 const int verbCnt = path.countVerbs(); 96 const int pointCnt = path.countPoints(); 97 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); 98 SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt); 99 SkASSERT(pointCnt && verbCnt); 100 *key++ = path.getFillType(); 101 *key++ = verbCnt; 102 memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t)); 103 int verbKeySize = SkAlign4(verbCnt); 104 // pad out to uint32_t alignment using value that will stand out when debugging. 105 uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt; 106 memset(pad, 0xDE, verbKeySize - verbCnt); 107 key += verbKeySize >> 2; 108 109 memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt); 110 GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t)); 111 key += 2 * pointCnt; 112 sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt); 113 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t)); 114 SkDEBUGCODE(key += conicWeightCnt); 115 SkASSERT(key - origKey == path_key_from_data_size(path)); 116 } 117 118 int GrShape::unstyledKeySize() const { 119 if (fInheritedKey.count()) { 120 return fInheritedKey.count(); 121 } 122 switch (fType) { 123 case Type::kEmpty: 124 return 1; 125 case Type::kRRect: 126 SkASSERT(!fInheritedKey.count()); 127 SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); 128 // + 1 for the direction, start index, and inverseness. 129 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1; 130 case Type::kLine: 131 GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint)); 132 // 4 for the end points and 1 for the inverseness 133 return 5; 134 case Type::kPath: { 135 if (0 == fPathData.fGenID) { 136 return -1; 137 } 138 int dataKeySize = path_key_from_data_size(fPathData.fPath); 139 if (dataKeySize >= 0) { 140 return dataKeySize; 141 } 142 // The key is the path ID and fill type. 143 return 2; 144 } 145 } 146 SkFAIL("Should never get here."); 147 return 0; 148 } 149 150 void GrShape::writeUnstyledKey(uint32_t* key) const { 151 SkASSERT(this->unstyledKeySize()); 152 SkDEBUGCODE(uint32_t* origKey = key;) 153 if (fInheritedKey.count()) { 154 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count()); 155 SkDEBUGCODE(key += fInheritedKey.count();) 156 } else { 157 switch (fType) { 158 case Type::kEmpty: 159 *key++ = 1; 160 break; 161 case Type::kRRect: 162 fRRectData.fRRect.writeToMemory(key); 163 key += SkRRect::kSizeInMemory / sizeof(uint32_t); 164 *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0; 165 *key |= fRRectData.fInverted ? (1 << 30) : 0; 166 *key++ |= fRRectData.fStart; 167 SkASSERT(fRRectData.fStart < 8); 168 break; 169 case Type::kLine: 170 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint)); 171 key += 4; 172 *key++ = fLineData.fInverted ? 1 : 0; 173 break; 174 case Type::kPath: { 175 SkASSERT(fPathData.fGenID); 176 int dataKeySize = path_key_from_data_size(fPathData.fPath); 177 if (dataKeySize >= 0) { 178 write_path_key_from_data(fPathData.fPath, key); 179 return; 180 } 181 *key++ = fPathData.fGenID; 182 // We could canonicalize the fill rule for paths that don't differentiate between 183 // even/odd or winding fill (e.g. convex). 184 *key++ = this->path().getFillType(); 185 break; 186 } 187 } 188 } 189 SkASSERT(key - origKey == this->unstyledKeySize()); 190 } 191 192 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) { 193 SkASSERT(!fInheritedKey.count()); 194 // If the output shape turns out to be simple, then we will just use its geometric key 195 if (Type::kPath == fType) { 196 // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as 197 // ApplyFullStyle(shape). 198 // The full key is structured as (geo,path_effect,stroke). 199 // If we do ApplyPathEffect we get get,path_effect as the inherited key. If we then 200 // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key 201 // and then append the style key (which should now be stroke only) at the end. 202 int parentCnt = parent.fInheritedKey.count(); 203 bool useParentGeoKey = !parentCnt; 204 if (useParentGeoKey) { 205 parentCnt = parent.unstyledKeySize(); 206 if (parentCnt < 0) { 207 // The parent's geometry has no key so we will have no key. 208 fPathData.fGenID = 0; 209 return; 210 } 211 } 212 uint32_t styleKeyFlags = 0; 213 if (parent.knownToBeClosed()) { 214 styleKeyFlags |= GrStyle::kClosed_KeyFlag; 215 } 216 if (parent.asLine(nullptr, nullptr)) { 217 styleKeyFlags |= GrStyle::kNoJoins_KeyFlag; 218 } 219 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); 220 if (styleCnt < 0) { 221 // The style doesn't allow a key, set the path gen ID to 0 so that we fail when 222 // we try to get a key for the shape. 223 fPathData.fGenID = 0; 224 return; 225 } 226 fInheritedKey.reset(parentCnt + styleCnt); 227 if (useParentGeoKey) { 228 // This will be the geo key. 229 parent.writeUnstyledKey(fInheritedKey.get()); 230 } else { 231 // This should be (geo,path_effect). 232 memcpy(fInheritedKey.get(), parent.fInheritedKey.get(), 233 parentCnt * sizeof(uint32_t)); 234 } 235 // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke) 236 GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale, 237 styleKeyFlags); 238 } 239 } 240 241 GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) { 242 const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr; 243 this->initType(that.fType, thatPath); 244 switch (fType) { 245 case Type::kEmpty: 246 break; 247 case Type::kRRect: 248 fRRectData = that.fRRectData; 249 break; 250 case Type::kLine: 251 fLineData = that.fLineData; 252 break; 253 case Type::kPath: 254 fPathData.fGenID = that.fPathData.fGenID; 255 break; 256 } 257 fInheritedKey.reset(that.fInheritedKey.count()); 258 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), 259 sizeof(uint32_t) * fInheritedKey.count()); 260 } 261 262 GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) { 263 // TODO: Add some quantization of scale for better cache performance here or leave that up 264 // to caller? 265 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel 266 // stroke of a rect). 267 if (!parent.style().applies() || 268 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) { 269 this->initType(Type::kEmpty); 270 *this = parent; 271 return; 272 } 273 274 SkPathEffect* pe = parent.fStyle.pathEffect(); 275 SkTLazy<SkPath> tmpPath; 276 const GrShape* parentForKey = &parent; 277 SkTLazy<GrShape> tmpParent; 278 this->initType(Type::kPath); 279 fPathData.fGenID = 0; 280 if (pe) { 281 const SkPath* srcForPathEffect; 282 if (parent.fType == Type::kPath) { 283 srcForPathEffect = &parent.path(); 284 } else { 285 srcForPathEffect = tmpPath.init(); 286 parent.asPath(tmpPath.get()); 287 } 288 // Should we consider bounds? Would have to include in key, but it'd be nice to know 289 // if the bounds actually modified anything before including in key. 290 SkStrokeRec strokeRec = parent.fStyle.strokeRec(); 291 if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect, 292 scale)) { 293 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr)); 294 *this = tmpParent.get()->applyStyle(apply, scale); 295 return; 296 } 297 // A path effect has access to change the res scale but we aren't expecting it to and it 298 // would mess up our key computation. 299 SkASSERT(scale == strokeRec.getResScale()); 300 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) { 301 // The intermediate shape may not be a general path. If we we're just applying 302 // the path effect then attemptToReduceFromPath would catch it. This means that 303 // when we subsequently applied the remaining strokeRec we would have a non-path 304 // parent shape that would be used to determine the the stroked path's key. 305 // We detect that case here and change parentForKey to a temporary that represents 306 // the simpler shape so that applying both path effect and the strokerec all at 307 // once produces the same key. 308 tmpParent.init(this->path(), GrStyle(strokeRec, nullptr)); 309 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale); 310 if (!tmpPath.isValid()) { 311 tmpPath.init(); 312 } 313 tmpParent.get()->asPath(tmpPath.get()); 314 SkStrokeRec::InitStyle fillOrHairline; 315 // The parent shape may have simplified away the strokeRec, check for that here. 316 if (tmpParent.get()->style().applies()) { 317 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline, 318 *tmpPath.get(), scale)); 319 } else if (tmpParent.get()->style().isSimpleFill()) { 320 fillOrHairline = SkStrokeRec::kFill_InitStyle; 321 } else { 322 SkASSERT(tmpParent.get()->style().isSimpleHairline()); 323 fillOrHairline = SkStrokeRec::kHairline_InitStyle; 324 } 325 fStyle.resetToInitStyle(fillOrHairline); 326 parentForKey = tmpParent.get(); 327 } else { 328 fStyle = GrStyle(strokeRec, nullptr); 329 } 330 } else { 331 const SkPath* srcForParentStyle; 332 if (parent.fType == Type::kPath) { 333 srcForParentStyle = &parent.path(); 334 } else { 335 srcForParentStyle = tmpPath.init(); 336 parent.asPath(tmpPath.get()); 337 } 338 SkStrokeRec::InitStyle fillOrHairline; 339 SkASSERT(parent.fStyle.applies()); 340 SkASSERT(!parent.fStyle.pathEffect()); 341 SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle, 342 scale)); 343 fStyle.resetToInitStyle(fillOrHairline); 344 } 345 this->attemptToSimplifyPath(); 346 this->setInheritedKey(*parentForKey, apply, scale); 347 } 348 349 void GrShape::attemptToSimplifyPath() { 350 SkRect rect; 351 SkRRect rrect; 352 SkPath::Direction rrectDir; 353 unsigned rrectStart; 354 bool inverted = this->path().isInverseFillType(); 355 SkPoint pts[2]; 356 if (this->path().isEmpty()) { 357 this->changeType(Type::kEmpty); 358 } else if (this->path().isLine(pts)) { 359 this->changeType(Type::kLine); 360 fLineData.fPts[0] = pts[0]; 361 fLineData.fPts[1] = pts[1]; 362 fLineData.fInverted = inverted; 363 } else if (this->path().isRRect(&rrect, &rrectDir, &rrectStart)) { 364 this->changeType(Type::kRRect); 365 fRRectData.fRRect = rrect; 366 fRRectData.fDir = rrectDir; 367 fRRectData.fStart = rrectStart; 368 fRRectData.fInverted = inverted; 369 // Currently SkPath does not acknowledge that empty, rect, or oval subtypes as rrects. 370 SkASSERT(!fRRectData.fRRect.isEmpty()); 371 SkASSERT(fRRectData.fRRect.getType() != SkRRect::kRect_Type); 372 SkASSERT(fRRectData.fRRect.getType() != SkRRect::kOval_Type); 373 } else if (this->path().isOval(&rect, &rrectDir, &rrectStart)) { 374 this->changeType(Type::kRRect); 375 fRRectData.fRRect.setOval(rect); 376 fRRectData.fDir = rrectDir; 377 fRRectData.fInverted = inverted; 378 // convert from oval indexing to rrect indexiing. 379 fRRectData.fStart = 2 * rrectStart; 380 } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) { 381 this->changeType(Type::kRRect); 382 // When there is a path effect we restrict rect detection to the narrower API that 383 // gives us the starting position. Otherwise, we will retry with the more aggressive 384 // isRect(). 385 fRRectData.fRRect.setRect(rect); 386 fRRectData.fInverted = inverted; 387 fRRectData.fDir = rrectDir; 388 // convert from rect indexing to rrect indexiing. 389 fRRectData.fStart = 2 * rrectStart; 390 } else if (!this->style().hasPathEffect()) { 391 bool closed; 392 if (this->path().isRect(&rect, &closed, nullptr)) { 393 if (closed || this->style().isSimpleFill()) { 394 this->changeType(Type::kRRect); 395 fRRectData.fRRect.setRect(rect); 396 // Since there is no path effect the dir and start index is immaterial. 397 fRRectData.fDir = kDefaultRRectDir; 398 fRRectData.fStart = kDefaultRRectStart; 399 // There isn't dashing so we will have to preserver inverseness. 400 fRRectData.fInverted = inverted; 401 } 402 } 403 } 404 if (Type::kPath != fType) { 405 fInheritedKey.reset(0); 406 if (Type::kRRect == fType) { 407 this->attemptToSimplifyRRect(); 408 } else if (Type::kLine == fType) { 409 this->attemptToSimplifyLine(); 410 } 411 } else { 412 if (fInheritedKey.count() || this->path().isVolatile()) { 413 fPathData.fGenID = 0; 414 } else { 415 fPathData.fGenID = this->path().getGenerationID(); 416 } 417 if (!this->style().hasNonDashPathEffect()) { 418 if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style || 419 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) { 420 // Stroke styles don't differentiate between winding and even/odd. 421 // Moreover, dashing ignores inverseness (skbug.com/5421) 422 bool inverse = !this->style().isDashed() && this->path().isInverseFillType(); 423 if (inverse) { 424 this->path().setFillType(kDefaultPathInverseFillType); 425 } else { 426 this->path().setFillType(kDefaultPathFillType); 427 } 428 } else if (this->path().isConvex()) { 429 // There is no distinction between even/odd and non-zero winding count for convex 430 // paths. 431 if (this->path().isInverseFillType()) { 432 this->path().setFillType(kDefaultPathInverseFillType); 433 } else { 434 this->path().setFillType(kDefaultPathFillType); 435 } 436 } 437 } 438 } 439 } 440 441 void GrShape::attemptToSimplifyRRect() { 442 SkASSERT(Type::kRRect == fType); 443 SkASSERT(!fInheritedKey.count()); 444 if (fRRectData.fRRect.isEmpty()) { 445 fType = Type::kEmpty; 446 return; 447 } 448 if (!this->style().hasPathEffect()) { 449 fRRectData.fDir = kDefaultRRectDir; 450 fRRectData.fStart = kDefaultRRectStart; 451 } else if (fStyle.isDashed()) { 452 // Dashing ignores the inverseness (currently). skbug.com/5421 453 fRRectData.fInverted = false; 454 } 455 // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts. 456 if (!fStyle.hasPathEffect() && 457 fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style && 458 fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join && 459 fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 && 460 fRRectData.fRRect.isRect()) { 461 SkScalar r = fStyle.strokeRec().getWidth() / 2; 462 fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r)); 463 fStyle = GrStyle::SimpleFill(); 464 } 465 } 466 467 void GrShape::attemptToSimplifyLine() { 468 SkASSERT(Type::kLine == fType); 469 SkASSERT(!fInheritedKey.count()); 470 if (fStyle.isDashed()) { 471 // Dashing ignores inverseness. 472 fLineData.fInverted = false; 473 return; 474 } else if (fStyle.hasPathEffect()) { 475 return; 476 } 477 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 478 // Make stroke + fill be stroke since the fill is empty. 479 SkStrokeRec rec = fStyle.strokeRec(); 480 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); 481 fStyle = GrStyle(rec, nullptr); 482 } 483 if (fStyle.isSimpleFill() && !fLineData.fInverted) { 484 this->changeType(Type::kEmpty); 485 return; 486 } 487 SkPoint* pts = fLineData.fPts; 488 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style) { 489 // If it is horizontal or vertical we will turn it into a filled rrect. 490 SkRect rect; 491 rect.fLeft = SkTMin(pts[0].fX, pts[1].fX); 492 rect.fRight = SkTMax(pts[0].fX, pts[1].fX); 493 rect.fTop = SkTMin(pts[0].fY, pts[1].fY); 494 rect.fBottom = SkTMax(pts[0].fY, pts[1].fY); 495 bool eqX = rect.fLeft == rect.fRight; 496 bool eqY = rect.fTop == rect.fBottom; 497 if (eqX || eqY) { 498 SkScalar r = fStyle.strokeRec().getWidth() / 2; 499 bool inverted = fLineData.fInverted; 500 this->changeType(Type::kRRect); 501 switch (fStyle.strokeRec().getCap()) { 502 case SkPaint::kButt_Cap: 503 if (eqX && eqY) { 504 this->changeType(Type::kEmpty); 505 return; 506 } 507 if (eqX) { 508 rect.outset(r, 0); 509 } else { 510 rect.outset(0, r); 511 } 512 fRRectData.fRRect = SkRRect::MakeRect(rect); 513 break; 514 case SkPaint::kSquare_Cap: 515 rect.outset(r, r); 516 fRRectData.fRRect = SkRRect::MakeRect(rect); 517 break; 518 case SkPaint::kRound_Cap: 519 rect.outset(r, r); 520 fRRectData.fRRect = SkRRect::MakeRectXY(rect, r, r); 521 break; 522 } 523 fRRectData.fInverted = inverted; 524 fRRectData.fDir = kDefaultRRectDir; 525 fRRectData.fStart = kDefaultRRectStart; 526 if (fRRectData.fRRect.isEmpty()) { 527 // This can happen when r is very small relative to the rect edges. 528 this->changeType(Type::kEmpty); 529 return; 530 } 531 fStyle = GrStyle::SimpleFill(); 532 return; 533 } 534 } 535 // Only path effects could care about the order of the points. Otherwise canonicalize 536 // the point order. 537 if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) { 538 SkTSwap(pts[0], pts[1]); 539 } 540 } 541