| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "GrShape.h" | 8 #include "GrShape.h" |
| 9 | 9 |
| 10 GrShape& GrShape::operator=(const GrShape& that) { | 10 GrShape& GrShape::operator=(const GrShape& that) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 fPathData.fGenID = that.fPathData.fGenID; | 23 fPathData.fGenID = that.fPathData.fGenID; |
| 24 break; | 24 break; |
| 25 } | 25 } |
| 26 fInheritedKey.reset(that.fInheritedKey.count()); | 26 fInheritedKey.reset(that.fInheritedKey.count()); |
| 27 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), | 27 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), |
| 28 sizeof(uint32_t) * fInheritedKey.count()); | 28 sizeof(uint32_t) * fInheritedKey.count()); |
| 29 return *this; | 29 return *this; |
| 30 } | 30 } |
| 31 | 31 |
| 32 SkRect GrShape::bounds() const { | 32 SkRect GrShape::bounds() const { |
| 33 static constexpr SkRect kEmpty = SkRect::MakeEmpty(); | 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); |
| 34 switch (fType) { | 36 switch (fType) { |
| 35 case Type::kEmpty: | 37 case Type::kEmpty: |
| 36 return kEmpty; | 38 return kInverted; |
| 37 case Type::kLine: { | 39 case Type::kLine: { |
| 38 SkRect bounds; | 40 SkRect bounds; |
| 39 if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) { | 41 if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) { |
| 40 bounds.fLeft = fLineData.fPts[0].fX; | 42 bounds.fLeft = fLineData.fPts[0].fX; |
| 41 bounds.fRight = fLineData.fPts[1].fX; | 43 bounds.fRight = fLineData.fPts[1].fX; |
| 42 } else { | 44 } else { |
| 43 bounds.fLeft = fLineData.fPts[1].fX; | 45 bounds.fLeft = fLineData.fPts[1].fX; |
| 44 bounds.fRight = fLineData.fPts[0].fX; | 46 bounds.fRight = fLineData.fPts[0].fX; |
| 45 } | 47 } |
| 46 if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) { | 48 if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) { |
| 47 bounds.fTop = fLineData.fPts[0].fY; | 49 bounds.fTop = fLineData.fPts[0].fY; |
| 48 bounds.fBottom = fLineData.fPts[1].fY; | 50 bounds.fBottom = fLineData.fPts[1].fY; |
| 49 } else { | 51 } else { |
| 50 bounds.fTop = fLineData.fPts[1].fY; | 52 bounds.fTop = fLineData.fPts[1].fY; |
| 51 bounds.fBottom = fLineData.fPts[0].fY; | 53 bounds.fBottom = fLineData.fPts[0].fY; |
| 52 } | 54 } |
| 53 return bounds; | 55 return bounds; |
| 54 } | 56 } |
| 55 case Type::kRRect: | 57 case Type::kRRect: |
| 56 return fRRectData.fRRect.getBounds(); | 58 return fRRectData.fRRect.getBounds(); |
| 57 case Type::kPath: | 59 case Type::kPath: |
| 58 return this->path().getBounds(); | 60 return this->path().getBounds(); |
| 59 } | 61 } |
| 60 SkFAIL("Unknown shape type"); | 62 SkFAIL("Unknown shape type"); |
| 61 return kEmpty; | 63 return kInverted; |
| 62 } | 64 } |
| 63 | 65 |
| 64 SkRect GrShape::styledBounds() const { | 66 SkRect GrShape::styledBounds() const { |
| 65 if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) { | 67 if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) { |
| 66 return SkRect::MakeEmpty(); | 68 return SkRect::MakeEmpty(); |
| 67 } | 69 } |
| 68 SkRect bounds; | 70 SkRect bounds; |
| 69 fStyle.adjustBounds(&bounds, this->bounds()); | 71 fStyle.adjustBounds(&bounds, this->bounds()); |
| 70 return bounds; | 72 return bounds; |
| 71 } | 73 } |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 if (parentCnt < 0) { | 153 if (parentCnt < 0) { |
| 152 // The parent's geometry has no key so we will have no key. | 154 // The parent's geometry has no key so we will have no key. |
| 153 fPathData.fGenID = 0; | 155 fPathData.fGenID = 0; |
| 154 return; | 156 return; |
| 155 } | 157 } |
| 156 } | 158 } |
| 157 uint32_t styleKeyFlags = 0; | 159 uint32_t styleKeyFlags = 0; |
| 158 if (parent.knownToBeClosed()) { | 160 if (parent.knownToBeClosed()) { |
| 159 styleKeyFlags |= GrStyle::kClosed_KeyFlag; | 161 styleKeyFlags |= GrStyle::kClosed_KeyFlag; |
| 160 } | 162 } |
| 163 if (parent.asLine(nullptr, nullptr)) { |
| 164 styleKeyFlags |= GrStyle::kNoJoins_KeyFlag; |
| 165 } |
| 161 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); | 166 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); |
| 162 if (styleCnt < 0) { | 167 if (styleCnt < 0) { |
| 163 // The style doesn't allow a key, set the path gen ID to 0 so that w
e fail when | 168 // The style doesn't allow a key, set the path gen ID to 0 so that w
e fail when |
| 164 // we try to get a key for the shape. | 169 // we try to get a key for the shape. |
| 165 fPathData.fGenID = 0; | 170 fPathData.fGenID = 0; |
| 166 return; | 171 return; |
| 167 } | 172 } |
| 168 fInheritedKey.reset(parentCnt + styleCnt); | 173 fInheritedKey.reset(parentCnt + styleCnt); |
| 169 if (useParentGeoKey) { | 174 if (useParentGeoKey) { |
| 170 // This will be the geo key. | 175 // This will be the geo key. |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 srcForPathEffect = &parent.path(); | 230 srcForPathEffect = &parent.path(); |
| 226 } else { | 231 } else { |
| 227 srcForPathEffect = tmpPath.init(); | 232 srcForPathEffect = tmpPath.init(); |
| 228 parent.asPath(tmpPath.get()); | 233 parent.asPath(tmpPath.get()); |
| 229 } | 234 } |
| 230 // Should we consider bounds? Would have to include in key, but it'd be
nice to know | 235 // Should we consider bounds? Would have to include in key, but it'd be
nice to know |
| 231 // if the bounds actually modified anything before including in key. | 236 // if the bounds actually modified anything before including in key. |
| 232 SkStrokeRec strokeRec = parent.fStyle.strokeRec(); | 237 SkStrokeRec strokeRec = parent.fStyle.strokeRec(); |
| 233 if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *src
ForPathEffect, | 238 if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *src
ForPathEffect, |
| 234 scale)) { | 239 scale)) { |
| 235 // If the path effect fails then we continue as though there was no
path effect. | 240 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr)); |
| 236 // If the original was a rrect that we couldn't canonicalize because
of the path | 241 *this = tmpParent.get()->applyStyle(apply, scale); |
| 237 // effect, then do so now. | 242 return; |
| 238 if (parent.fType == Type::kRRect && (parent.fRRectData.fDir != kDefa
ultRRectDir || | |
| 239 parent.fRRectData.fStart != kDe
faultRRectStart)) { | |
| 240 SkASSERT(srcForPathEffect == tmpPath.get()); | |
| 241 tmpPath.get()->reset(); | |
| 242 tmpPath.get()->addRRect(parent.fRRectData.fRRect, kDefaultRRectD
ir, | |
| 243 kDefaultRRectDir); | |
| 244 } | |
| 245 this->path() = *srcForPathEffect; | |
| 246 } | 243 } |
| 247 // A path effect has access to change the res scale but we aren't expect
ing it to and it | 244 // A path effect has access to change the res scale but we aren't expect
ing it to and it |
| 248 // would mess up our key computation. | 245 // would mess up our key computation. |
| 249 SkASSERT(scale == strokeRec.getResScale()); | 246 SkASSERT(scale == strokeRec.getResScale()); |
| 250 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needTo
Apply()) { | 247 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needTo
Apply()) { |
| 251 // The intermediate shape may not be a general path. If we we're jus
t applying | 248 // The intermediate shape may not be a general path. If we we're jus
t applying |
| 252 // the path effect then attemptToReduceFromPath would catch it. This
means that | 249 // the path effect then attemptToReduceFromPath would catch it. This
means that |
| 253 // when we subsequently applied the remaining strokeRec we would hav
e a non-path | 250 // when we subsequently applied the remaining strokeRec we would hav
e a non-path |
| 254 // parent shape that would be used to determine the the stroked path
's key. | 251 // parent shape that would be used to determine the the stroked path
's key. |
| 255 // We detect that case here and change parentForKey to a temporary t
hat represents | 252 // We detect that case here and change parentForKey to a temporary t
hat represents |
| 256 // the simpler shape so that applying both path effect and the strok
erec all at | 253 // the simpler shape so that applying both path effect and the strok
erec all at |
| 257 // once produces the same key. | 254 // once produces the same key. |
| 258 tmpParent.init(this->path(), GrStyle(strokeRec, nullptr)); | 255 tmpParent.init(this->path(), GrStyle(strokeRec, nullptr)); |
| 259 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffect
Only, scale); | 256 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffect
Only, scale); |
| 260 if (!tmpPath.isValid()) { | 257 if (!tmpPath.isValid()) { |
| 261 tmpPath.init(); | 258 tmpPath.init(); |
| 262 } | 259 } |
| 263 tmpParent.get()->asPath(tmpPath.get()); | 260 tmpParent.get()->asPath(tmpPath.get()); |
| 264 SkStrokeRec::InitStyle fillOrHairline; | 261 SkStrokeRec::InitStyle fillOrHairline; |
| 265 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &
fillOrHairline, | 262 // The parent shape may have simplified away the strokeRec, check fo
r that here. |
| 266 *tmpPath.get(),
scale)); | 263 if (tmpParent.get()->style().applies()) { |
| 264 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(
), &fillOrHairline, |
| 265 *tmpPath.get
(), scale)); |
| 266 } else if (tmpParent.get()->style().isSimpleFill()) { |
| 267 fillOrHairline = SkStrokeRec::kFill_InitStyle; |
| 268 } else { |
| 269 SkASSERT(tmpParent.get()->style().isSimpleHairline()); |
| 270 fillOrHairline = SkStrokeRec::kHairline_InitStyle; |
| 271 } |
| 267 fStyle.resetToInitStyle(fillOrHairline); | 272 fStyle.resetToInitStyle(fillOrHairline); |
| 268 parentForKey = tmpParent.get(); | 273 parentForKey = tmpParent.get(); |
| 269 } else { | 274 } else { |
| 270 fStyle = GrStyle(strokeRec, nullptr); | 275 fStyle = GrStyle(strokeRec, nullptr); |
| 271 } | 276 } |
| 272 } else { | 277 } else { |
| 273 const SkPath* srcForParentStyle; | 278 const SkPath* srcForParentStyle; |
| 274 if (parent.fType == Type::kPath) { | 279 if (parent.fType == Type::kPath) { |
| 275 srcForParentStyle = &parent.path(); | 280 srcForParentStyle = &parent.path(); |
| 276 } else { | 281 } else { |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 if (!this->style().hasPathEffect()) { | 399 if (!this->style().hasPathEffect()) { |
| 395 fRRectData.fDir = kDefaultRRectDir; | 400 fRRectData.fDir = kDefaultRRectDir; |
| 396 fRRectData.fStart = kDefaultRRectStart; | 401 fRRectData.fStart = kDefaultRRectStart; |
| 397 } else if (fStyle.isDashed()) { | 402 } else if (fStyle.isDashed()) { |
| 398 // Dashing ignores the inverseness (currently). skbug.com/5421 | 403 // Dashing ignores the inverseness (currently). skbug.com/5421 |
| 399 fRRectData.fInverted = false; | 404 fRRectData.fInverted = false; |
| 400 } | 405 } |
| 401 } | 406 } |
| 402 | 407 |
| 403 void GrShape::attemptToSimplifyLine() { | 408 void GrShape::attemptToSimplifyLine() { |
| 409 SkASSERT(Type::kLine == fType); |
| 410 SkASSERT(!fInheritedKey.count()); |
| 411 if (fStyle.isDashed()) { |
| 412 // Dashing ignores inverseness. |
| 413 fLineData.fInverted = false; |
| 414 return; |
| 415 } else if (fStyle.hasPathEffect()) { |
| 416 return; |
| 417 } |
| 418 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { |
| 419 // Make stroke + fill be stroke since the fill is empty. |
| 420 SkStrokeRec rec = fStyle.strokeRec(); |
| 421 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); |
| 422 fStyle = GrStyle(rec, nullptr); |
| 423 } |
| 404 if (fStyle.isSimpleFill() && !fLineData.fInverted) { | 424 if (fStyle.isSimpleFill() && !fLineData.fInverted) { |
| 405 this->changeType(Type::kEmpty); | 425 this->changeType(Type::kEmpty); |
| 406 } else { | 426 return; |
| 407 // Only path effects could care about the order of the points. Otherwise
canonicalize | 427 } |
| 408 // the point order | 428 SkPoint* pts = fLineData.fPts; |
| 409 if (!fStyle.hasPathEffect()) { | 429 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style) { |
| 410 SkPoint* pts = fLineData.fPts; | 430 // If it is horizontal or vertical we will turn it into a filled rrect. |
| 411 if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX <
pts[0].fX)) { | 431 SkRect rect; |
| 412 SkTSwap(pts[0], pts[1]); | 432 rect.fLeft = SkTMin(pts[0].fX, pts[1].fX); |
| 433 rect.fRight = SkTMax(pts[0].fX, pts[1].fX); |
| 434 rect.fTop = SkTMin(pts[0].fY, pts[1].fY); |
| 435 rect.fBottom = SkTMax(pts[0].fY, pts[1].fY); |
| 436 bool eqX = rect.fLeft == rect.fRight; |
| 437 bool eqY = rect.fTop == rect.fBottom; |
| 438 if (eqX || eqY) { |
| 439 SkScalar r = fStyle.strokeRec().getWidth() / 2; |
| 440 bool inverted = fLineData.fInverted; |
| 441 this->changeType(Type::kRRect); |
| 442 switch (fStyle.strokeRec().getCap()) { |
| 443 case SkPaint::kButt_Cap: |
| 444 if (eqX && eqY) { |
| 445 this->changeType(Type::kEmpty); |
| 446 return; |
| 447 } |
| 448 if (eqX) { |
| 449 rect.outset(r, 0); |
| 450 } else { |
| 451 rect.outset(0, r); |
| 452 } |
| 453 fRRectData.fRRect = SkRRect::MakeRect(rect); |
| 454 break; |
| 455 case SkPaint::kSquare_Cap: |
| 456 rect.outset(r, r); |
| 457 fRRectData.fRRect = SkRRect::MakeRect(rect); |
| 458 break; |
| 459 case SkPaint::kRound_Cap: |
| 460 rect.outset(r, r); |
| 461 fRRectData.fRRect = SkRRect::MakeRectXY(rect, r, r); |
| 462 break; |
| 413 } | 463 } |
| 414 } else if (fStyle.isDashed()) { | 464 fRRectData.fInverted = inverted; |
| 415 // Dashing ignores inverseness. | 465 fRRectData.fDir = kDefaultRRectDir; |
| 416 fLineData.fInverted = false; | 466 fRRectData.fStart = kDefaultRRectStart; |
| 467 if (fRRectData.fRRect.isEmpty()) { |
| 468 // This can happen when r is very small relative to the rect edg
es. |
| 469 this->changeType(Type::kEmpty); |
| 470 return; |
| 471 } |
| 472 fStyle = GrStyle::SimpleFill(); |
| 473 return; |
| 417 } | 474 } |
| 418 } | 475 } |
| 476 // Only path effects could care about the order of the points. Otherwise can
onicalize |
| 477 // the point order. |
| 478 if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].f
X)) { |
| 479 SkTSwap(pts[0], pts[1]); |
| 480 } |
| 419 } | 481 } |
| OLD | NEW |