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 |