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 "src/gpu/geometry/GrShape.h" |
9 | |
10 | #include "include/private/SkIDChangeListener.h" |
11 | |
12 | #include <utility> |
13 | |
14 | GrShape& GrShape::operator=(const GrShape& that) { |
15 | fStyle = that.fStyle; |
16 | this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr); |
17 | switch (fType) { |
18 | case Type::kEmpty: |
19 | break; |
20 | case Type::kInvertedEmpty: |
21 | break; |
22 | case Type::kRRect: |
23 | fRRectData = that.fRRectData; |
24 | break; |
25 | case Type::kArc: |
26 | fArcData = that.fArcData; |
27 | break; |
28 | case Type::kLine: |
29 | fLineData = that.fLineData; |
30 | break; |
31 | case Type::kPath: |
32 | fPathData.fGenID = that.fPathData.fGenID; |
33 | break; |
34 | } |
35 | fInheritedKey.reset(that.fInheritedKey.count()); |
36 | sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), |
37 | sizeof(uint32_t) * fInheritedKey.count()); |
38 | if (that.fInheritedPathForListeners.isValid()) { |
39 | fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get()); |
40 | } else { |
41 | fInheritedPathForListeners.reset(); |
42 | } |
43 | return *this; |
44 | } |
45 | |
46 | static bool flip_inversion(bool originalIsInverted, GrShape::FillInversion inversion) { |
47 | switch (inversion) { |
48 | case GrShape::FillInversion::kPreserve: |
49 | return false; |
50 | case GrShape::FillInversion::kFlip: |
51 | return true; |
52 | case GrShape::FillInversion::kForceInverted: |
53 | return !originalIsInverted; |
54 | case GrShape::FillInversion::kForceNoninverted: |
55 | return originalIsInverted; |
56 | } |
57 | return false; |
58 | } |
59 | |
60 | static bool is_inverted(bool originalIsInverted, GrShape::FillInversion inversion) { |
61 | switch (inversion) { |
62 | case GrShape::FillInversion::kPreserve: |
63 | return originalIsInverted; |
64 | case GrShape::FillInversion::kFlip: |
65 | return !originalIsInverted; |
66 | case GrShape::FillInversion::kForceInverted: |
67 | return true; |
68 | case GrShape::FillInversion::kForceNoninverted: |
69 | return false; |
70 | } |
71 | return false; |
72 | } |
73 | |
74 | GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) { |
75 | if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) { |
76 | // By returning the original rather than falling through we can preserve any inherited style |
77 | // key. Otherwise, we wipe it out below since the style change invalidates it. |
78 | return original; |
79 | } |
80 | GrShape result; |
81 | if (original.fInheritedPathForListeners.isValid()) { |
82 | result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get()); |
83 | } |
84 | switch (original.fType) { |
85 | case Type::kRRect: |
86 | result.fType = original.fType; |
87 | result.fRRectData.fRRect = original.fRRectData.fRRect; |
88 | result.fRRectData.fDir = kDefaultRRectDir; |
89 | result.fRRectData.fStart = kDefaultRRectStart; |
90 | result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion); |
91 | break; |
92 | case Type::kArc: |
93 | result.fType = original.fType; |
94 | result.fArcData.fOval = original.fArcData.fOval; |
95 | result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees; |
96 | result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees; |
97 | result.fArcData.fUseCenter = original.fArcData.fUseCenter; |
98 | result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion); |
99 | break; |
100 | case Type::kLine: |
101 | // Lines don't fill. |
102 | if (is_inverted(original.fLineData.fInverted, inversion)) { |
103 | result.fType = Type::kInvertedEmpty; |
104 | } else { |
105 | result.fType = Type::kEmpty; |
106 | } |
107 | break; |
108 | case Type::kEmpty: |
109 | result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty : Type::kEmpty; |
110 | break; |
111 | case Type::kInvertedEmpty: |
112 | result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty : Type::kEmpty; |
113 | break; |
114 | case Type::kPath: |
115 | result.initType(Type::kPath, &original.fPathData.fPath); |
116 | result.fPathData.fGenID = original.fPathData.fGenID; |
117 | if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) { |
118 | result.fPathData.fPath.toggleInverseFillType(); |
119 | } |
120 | if (!original.style().isSimpleFill()) { |
121 | // Going from a non-filled style to fill may allow additional simplifications (e.g. |
122 | // closing an open rect that wasn't closed in the original shape because it had |
123 | // stroke style). |
124 | result.attemptToSimplifyPath(); |
125 | } |
126 | break; |
127 | } |
128 | // We don't copy the inherited key since it can contain path effect information that we just |
129 | // stripped. |
130 | return result; |
131 | } |
132 | |
133 | SkRect GrShape::bounds() const { |
134 | // Bounds where left == bottom or top == right can indicate a line or point shape. We return |
135 | // inverted bounds for a truly empty shape. |
136 | static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1); |
137 | switch (fType) { |
138 | case Type::kEmpty: |
139 | return kInverted; |
140 | case Type::kInvertedEmpty: |
141 | return kInverted; |
142 | case Type::kLine: { |
143 | SkRect bounds; |
144 | if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) { |
145 | bounds.fLeft = fLineData.fPts[0].fX; |
146 | bounds.fRight = fLineData.fPts[1].fX; |
147 | } else { |
148 | bounds.fLeft = fLineData.fPts[1].fX; |
149 | bounds.fRight = fLineData.fPts[0].fX; |
150 | } |
151 | if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) { |
152 | bounds.fTop = fLineData.fPts[0].fY; |
153 | bounds.fBottom = fLineData.fPts[1].fY; |
154 | } else { |
155 | bounds.fTop = fLineData.fPts[1].fY; |
156 | bounds.fBottom = fLineData.fPts[0].fY; |
157 | } |
158 | return bounds; |
159 | } |
160 | case Type::kRRect: |
161 | return fRRectData.fRRect.getBounds(); |
162 | case Type::kArc: |
163 | // Could make this less conservative by looking at angles. |
164 | return fArcData.fOval; |
165 | case Type::kPath: |
166 | return this->path().getBounds(); |
167 | } |
168 | SK_ABORT("Unknown shape type" ); |
169 | } |
170 | |
171 | SkRect GrShape::styledBounds() const { |
172 | if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) { |
173 | return SkRect::MakeEmpty(); |
174 | } |
175 | |
176 | SkRect bounds; |
177 | fStyle.adjustBounds(&bounds, this->bounds()); |
178 | return bounds; |
179 | } |
180 | |
181 | // If the path is small enough to be keyed from its data this returns key length, otherwise -1. |
182 | static int path_key_from_data_size(const SkPath& path) { |
183 | const int verbCnt = path.countVerbs(); |
184 | if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) { |
185 | return -1; |
186 | } |
187 | const int pointCnt = path.countPoints(); |
188 | const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); |
189 | |
190 | static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t)); |
191 | static_assert(sizeof(SkScalar) == sizeof(uint32_t)); |
192 | // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to |
193 | // a uint32_t length. |
194 | return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt; |
195 | } |
196 | |
197 | // Writes the path data key into the passed pointer. |
198 | static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) { |
199 | uint32_t* key = origKey; |
200 | // The check below should take care of negative values casted positive. |
201 | const int verbCnt = path.countVerbs(); |
202 | const int pointCnt = path.countPoints(); |
203 | const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path); |
204 | SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt); |
205 | SkASSERT(pointCnt && verbCnt); |
206 | *key++ = (uint32_t)path.getFillType(); |
207 | *key++ = verbCnt; |
208 | memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t)); |
209 | int verbKeySize = SkAlign4(verbCnt); |
210 | // pad out to uint32_t alignment using value that will stand out when debugging. |
211 | uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt; |
212 | memset(pad, 0xDE, verbKeySize - verbCnt); |
213 | key += verbKeySize >> 2; |
214 | |
215 | memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt); |
216 | static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t)); |
217 | key += 2 * pointCnt; |
218 | sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt); |
219 | static_assert(sizeof(SkScalar) == sizeof(uint32_t)); |
220 | SkDEBUGCODE(key += conicWeightCnt); |
221 | SkASSERT(key - origKey == path_key_from_data_size(path)); |
222 | } |
223 | |
224 | int GrShape::unstyledKeySize() const { |
225 | if (fInheritedKey.count()) { |
226 | return fInheritedKey.count(); |
227 | } |
228 | switch (fType) { |
229 | case Type::kEmpty: |
230 | return 1; |
231 | case Type::kInvertedEmpty: |
232 | return 1; |
233 | case Type::kRRect: |
234 | SkASSERT(!fInheritedKey.count()); |
235 | static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); |
236 | // + 1 for the direction, start index, and inverseness. |
237 | return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1; |
238 | case Type::kArc: |
239 | SkASSERT(!fInheritedKey.count()); |
240 | static_assert(0 == sizeof(fArcData) % sizeof(uint32_t)); |
241 | return sizeof(fArcData) / sizeof(uint32_t); |
242 | case Type::kLine: |
243 | static_assert(2 * sizeof(uint32_t) == sizeof(SkPoint)); |
244 | // 4 for the end points and 1 for the inverseness |
245 | return 5; |
246 | case Type::kPath: { |
247 | if (0 == fPathData.fGenID) { |
248 | return -1; |
249 | } |
250 | int dataKeySize = path_key_from_data_size(fPathData.fPath); |
251 | if (dataKeySize >= 0) { |
252 | return dataKeySize; |
253 | } |
254 | // The key is the path ID and fill type. |
255 | return 2; |
256 | } |
257 | } |
258 | SK_ABORT("Should never get here." ); |
259 | } |
260 | |
261 | void GrShape::writeUnstyledKey(uint32_t* key) const { |
262 | SkASSERT(this->unstyledKeySize()); |
263 | SkDEBUGCODE(uint32_t* origKey = key;) |
264 | if (fInheritedKey.count()) { |
265 | memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count()); |
266 | SkDEBUGCODE(key += fInheritedKey.count();) |
267 | } else { |
268 | switch (fType) { |
269 | case Type::kEmpty: |
270 | *key++ = 1; |
271 | break; |
272 | case Type::kInvertedEmpty: |
273 | *key++ = 2; |
274 | break; |
275 | case Type::kRRect: |
276 | fRRectData.fRRect.writeToMemory(key); |
277 | key += SkRRect::kSizeInMemory / sizeof(uint32_t); |
278 | *key = (fRRectData.fDir == SkPathDirection::kCCW) ? (1 << 31) : 0; |
279 | *key |= fRRectData.fInverted ? (1 << 30) : 0; |
280 | *key++ |= fRRectData.fStart; |
281 | SkASSERT(fRRectData.fStart < 8); |
282 | break; |
283 | case Type::kArc: |
284 | memcpy(key, &fArcData, sizeof(fArcData)); |
285 | key += sizeof(fArcData) / sizeof(uint32_t); |
286 | break; |
287 | case Type::kLine: |
288 | memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint)); |
289 | key += 4; |
290 | *key++ = fLineData.fInverted ? 1 : 0; |
291 | break; |
292 | case Type::kPath: { |
293 | SkASSERT(fPathData.fGenID); |
294 | int dataKeySize = path_key_from_data_size(fPathData.fPath); |
295 | if (dataKeySize >= 0) { |
296 | write_path_key_from_data(fPathData.fPath, key); |
297 | return; |
298 | } |
299 | *key++ = fPathData.fGenID; |
300 | // We could canonicalize the fill rule for paths that don't differentiate between |
301 | // even/odd or winding fill (e.g. convex). |
302 | *key++ = (uint32_t)this->path().getFillType(); |
303 | break; |
304 | } |
305 | } |
306 | } |
307 | SkASSERT(key - origKey == this->unstyledKeySize()); |
308 | } |
309 | |
310 | void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) { |
311 | SkASSERT(!fInheritedKey.count()); |
312 | // If the output shape turns out to be simple, then we will just use its geometric key |
313 | if (Type::kPath == fType) { |
314 | // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as |
315 | // ApplyFullStyle(shape). |
316 | // The full key is structured as (geo,path_effect,stroke). |
317 | // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then |
318 | // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key |
319 | // and then append the style key (which should now be stroke only) at the end. |
320 | int parentCnt = parent.fInheritedKey.count(); |
321 | bool useParentGeoKey = !parentCnt; |
322 | if (useParentGeoKey) { |
323 | parentCnt = parent.unstyledKeySize(); |
324 | if (parentCnt < 0) { |
325 | // The parent's geometry has no key so we will have no key. |
326 | fPathData.fGenID = 0; |
327 | return; |
328 | } |
329 | } |
330 | uint32_t styleKeyFlags = 0; |
331 | if (parent.knownToBeClosed()) { |
332 | styleKeyFlags |= GrStyle::kClosed_KeyFlag; |
333 | } |
334 | if (parent.asLine(nullptr, nullptr)) { |
335 | styleKeyFlags |= GrStyle::kNoJoins_KeyFlag; |
336 | } |
337 | int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags); |
338 | if (styleCnt < 0) { |
339 | // The style doesn't allow a key, set the path gen ID to 0 so that we fail when |
340 | // we try to get a key for the shape. |
341 | fPathData.fGenID = 0; |
342 | return; |
343 | } |
344 | fInheritedKey.reset(parentCnt + styleCnt); |
345 | if (useParentGeoKey) { |
346 | // This will be the geo key. |
347 | parent.writeUnstyledKey(fInheritedKey.get()); |
348 | } else { |
349 | // This should be (geo,path_effect). |
350 | memcpy(fInheritedKey.get(), parent.fInheritedKey.get(), |
351 | parentCnt * sizeof(uint32_t)); |
352 | } |
353 | // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke) |
354 | GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale, |
355 | styleKeyFlags); |
356 | } |
357 | } |
358 | |
359 | const SkPath* GrShape::originalPathForListeners() const { |
360 | if (fInheritedPathForListeners.isValid()) { |
361 | return fInheritedPathForListeners.get(); |
362 | } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) { |
363 | return &fPathData.fPath; |
364 | } |
365 | return nullptr; |
366 | } |
367 | |
368 | void GrShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const { |
369 | if (const auto* lp = this->originalPathForListeners()) { |
370 | SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener)); |
371 | } |
372 | } |
373 | |
374 | GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees, |
375 | bool useCenter, const GrStyle& style) { |
376 | GrShape result; |
377 | result.changeType(Type::kArc); |
378 | result.fArcData.fOval = oval; |
379 | result.fArcData.fStartAngleDegrees = startAngleDegrees; |
380 | result.fArcData.fSweepAngleDegrees = sweepAngleDegrees; |
381 | result.fArcData.fUseCenter = useCenter; |
382 | result.fArcData.fInverted = false; |
383 | result.fStyle = style; |
384 | result.attemptToSimplifyArc(); |
385 | return result; |
386 | } |
387 | |
388 | GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) { |
389 | const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr; |
390 | this->initType(that.fType, thatPath); |
391 | switch (fType) { |
392 | case Type::kEmpty: |
393 | break; |
394 | case Type::kInvertedEmpty: |
395 | break; |
396 | case Type::kRRect: |
397 | fRRectData = that.fRRectData; |
398 | break; |
399 | case Type::kArc: |
400 | fArcData = that.fArcData; |
401 | break; |
402 | case Type::kLine: |
403 | fLineData = that.fLineData; |
404 | break; |
405 | case Type::kPath: |
406 | fPathData.fGenID = that.fPathData.fGenID; |
407 | break; |
408 | } |
409 | fInheritedKey.reset(that.fInheritedKey.count()); |
410 | sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(), |
411 | sizeof(uint32_t) * fInheritedKey.count()); |
412 | if (that.fInheritedPathForListeners.isValid()) { |
413 | fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get()); |
414 | } |
415 | } |
416 | |
417 | GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) { |
418 | // TODO: Add some quantization of scale for better cache performance here or leave that up |
419 | // to caller? |
420 | // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel |
421 | // stroke of a rect). |
422 | if (!parent.style().applies() || |
423 | (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) { |
424 | this->initType(Type::kEmpty); |
425 | *this = parent; |
426 | return; |
427 | } |
428 | |
429 | SkPathEffect* pe = parent.fStyle.pathEffect(); |
430 | SkTLazy<SkPath> tmpPath; |
431 | const GrShape* parentForKey = &parent; |
432 | SkTLazy<GrShape> tmpParent; |
433 | this->initType(Type::kPath); |
434 | fPathData.fGenID = 0; |
435 | if (pe) { |
436 | const SkPath* srcForPathEffect; |
437 | if (parent.fType == Type::kPath) { |
438 | srcForPathEffect = &parent.path(); |
439 | } else { |
440 | srcForPathEffect = tmpPath.init(); |
441 | parent.asPath(tmpPath.get()); |
442 | } |
443 | // Should we consider bounds? Would have to include in key, but it'd be nice to know |
444 | // if the bounds actually modified anything before including in key. |
445 | SkStrokeRec strokeRec = parent.fStyle.strokeRec(); |
446 | if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect, |
447 | scale)) { |
448 | tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr)); |
449 | *this = tmpParent.get()->applyStyle(apply, scale); |
450 | return; |
451 | } |
452 | // A path effect has access to change the res scale but we aren't expecting it to and it |
453 | // would mess up our key computation. |
454 | SkASSERT(scale == strokeRec.getResScale()); |
455 | if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) { |
456 | // The intermediate shape may not be a general path. If we we're just applying |
457 | // the path effect then attemptToReduceFromPath would catch it. This means that |
458 | // when we subsequently applied the remaining strokeRec we would have a non-path |
459 | // parent shape that would be used to determine the the stroked path's key. |
460 | // We detect that case here and change parentForKey to a temporary that represents |
461 | // the simpler shape so that applying both path effect and the strokerec all at |
462 | // once produces the same key. |
463 | tmpParent.init(this->path(), GrStyle(strokeRec, nullptr)); |
464 | tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale); |
465 | if (!tmpPath.isValid()) { |
466 | tmpPath.init(); |
467 | } |
468 | tmpParent.get()->asPath(tmpPath.get()); |
469 | SkStrokeRec::InitStyle fillOrHairline; |
470 | // The parent shape may have simplified away the strokeRec, check for that here. |
471 | if (tmpParent.get()->style().applies()) { |
472 | SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline, |
473 | *tmpPath.get(), scale)); |
474 | } else if (tmpParent.get()->style().isSimpleFill()) { |
475 | fillOrHairline = SkStrokeRec::kFill_InitStyle; |
476 | } else { |
477 | SkASSERT(tmpParent.get()->style().isSimpleHairline()); |
478 | fillOrHairline = SkStrokeRec::kHairline_InitStyle; |
479 | } |
480 | fStyle.resetToInitStyle(fillOrHairline); |
481 | parentForKey = tmpParent.get(); |
482 | } else { |
483 | fStyle = GrStyle(strokeRec, nullptr); |
484 | } |
485 | } else { |
486 | const SkPath* srcForParentStyle; |
487 | if (parent.fType == Type::kPath) { |
488 | srcForParentStyle = &parent.path(); |
489 | } else { |
490 | srcForParentStyle = tmpPath.init(); |
491 | parent.asPath(tmpPath.get()); |
492 | } |
493 | SkStrokeRec::InitStyle fillOrHairline; |
494 | SkASSERT(parent.fStyle.applies()); |
495 | SkASSERT(!parent.fStyle.pathEffect()); |
496 | SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle, |
497 | scale)); |
498 | fStyle.resetToInitStyle(fillOrHairline); |
499 | } |
500 | if (parent.fInheritedPathForListeners.isValid()) { |
501 | fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get()); |
502 | } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) { |
503 | fInheritedPathForListeners.set(parent.fPathData.fPath); |
504 | } |
505 | this->attemptToSimplifyPath(); |
506 | this->setInheritedKey(*parentForKey, apply, scale); |
507 | } |
508 | |
509 | void GrShape::attemptToSimplifyPath() { |
510 | SkRect rect; |
511 | SkRRect rrect; |
512 | SkPathDirection rrectDir; |
513 | unsigned rrectStart; |
514 | bool inverted = this->path().isInverseFillType(); |
515 | SkPoint pts[2]; |
516 | if (this->path().isEmpty()) { |
517 | // Dashing ignores inverseness skbug.com/5421. |
518 | this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty |
519 | : Type::kEmpty); |
520 | } else if (this->path().isLine(pts)) { |
521 | this->changeType(Type::kLine); |
522 | fLineData.fPts[0] = pts[0]; |
523 | fLineData.fPts[1] = pts[1]; |
524 | fLineData.fInverted = inverted; |
525 | } else if (SkPathPriv::IsRRect(this->path(), &rrect, &rrectDir, &rrectStart)) { |
526 | this->changeType(Type::kRRect); |
527 | fRRectData.fRRect = rrect; |
528 | fRRectData.fDir = rrectDir; |
529 | fRRectData.fStart = rrectStart; |
530 | fRRectData.fInverted = inverted; |
531 | SkASSERT(!fRRectData.fRRect.isEmpty()); |
532 | } else if (SkPathPriv::IsOval(this->path(), &rect, &rrectDir, &rrectStart)) { |
533 | this->changeType(Type::kRRect); |
534 | fRRectData.fRRect.setOval(rect); |
535 | fRRectData.fDir = rrectDir; |
536 | fRRectData.fInverted = inverted; |
537 | // convert from oval indexing to rrect indexiing. |
538 | fRRectData.fStart = 2 * rrectStart; |
539 | } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) { |
540 | this->changeType(Type::kRRect); |
541 | // When there is a path effect we restrict rect detection to the narrower API that |
542 | // gives us the starting position. Otherwise, we will retry with the more aggressive |
543 | // isRect(). |
544 | fRRectData.fRRect.setRect(rect); |
545 | fRRectData.fInverted = inverted; |
546 | fRRectData.fDir = rrectDir; |
547 | // convert from rect indexing to rrect indexiing. |
548 | fRRectData.fStart = 2 * rrectStart; |
549 | } else if (!this->style().hasPathEffect()) { |
550 | bool closed; |
551 | if (this->path().isRect(&rect, &closed, nullptr)) { |
552 | if (closed || this->style().isSimpleFill()) { |
553 | this->changeType(Type::kRRect); |
554 | fRRectData.fRRect.setRect(rect); |
555 | // Since there is no path effect the dir and start index is immaterial. |
556 | fRRectData.fDir = kDefaultRRectDir; |
557 | fRRectData.fStart = kDefaultRRectStart; |
558 | // There isn't dashing so we will have to preserver inverseness. |
559 | fRRectData.fInverted = inverted; |
560 | } |
561 | } |
562 | } |
563 | if (Type::kPath != fType) { |
564 | fInheritedKey.reset(0); |
565 | // Whenever we simplify to a non-path, break the chain so we no longer refer to the |
566 | // original path. This prevents attaching genID listeners to temporary paths created when |
567 | // drawing simple shapes. |
568 | fInheritedPathForListeners.reset(); |
569 | if (Type::kRRect == fType) { |
570 | this->attemptToSimplifyRRect(); |
571 | } else if (Type::kLine == fType) { |
572 | this->attemptToSimplifyLine(); |
573 | } |
574 | } else { |
575 | if (fInheritedKey.count() || this->path().isVolatile()) { |
576 | fPathData.fGenID = 0; |
577 | } else { |
578 | fPathData.fGenID = this->path().getGenerationID(); |
579 | } |
580 | if (!this->style().hasNonDashPathEffect()) { |
581 | if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style || |
582 | this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) { |
583 | // Stroke styles don't differentiate between winding and even/odd. |
584 | // Moreover, dashing ignores inverseness (skbug.com/5421) |
585 | bool inverse = !this->style().isDashed() && this->path().isInverseFillType(); |
586 | if (inverse) { |
587 | this->path().setFillType(kDefaultPathInverseFillType); |
588 | } else { |
589 | this->path().setFillType(kDefaultPathFillType); |
590 | } |
591 | } else if (this->path().isConvex()) { |
592 | // There is no distinction between even/odd and non-zero winding count for convex |
593 | // paths. |
594 | if (this->path().isInverseFillType()) { |
595 | this->path().setFillType(kDefaultPathInverseFillType); |
596 | } else { |
597 | this->path().setFillType(kDefaultPathFillType); |
598 | } |
599 | } |
600 | } |
601 | } |
602 | } |
603 | |
604 | void GrShape::attemptToSimplifyRRect() { |
605 | SkASSERT(Type::kRRect == fType); |
606 | SkASSERT(!fInheritedKey.count()); |
607 | if (fRRectData.fRRect.isEmpty()) { |
608 | // An empty filled rrect is equivalent to a filled empty path with inversion preserved. |
609 | if (fStyle.isSimpleFill()) { |
610 | fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty; |
611 | fStyle = GrStyle::SimpleFill(); |
612 | return; |
613 | } |
614 | // Dashing a rrect with no width or height is equivalent to filling an emtpy path. |
615 | // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length |
616 | // line will produce cap geometry if the effect begins in an "on" interval. |
617 | if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) { |
618 | // Dashing ignores the inverseness (currently). skbug.com/5421. |
619 | fType = Type::kEmpty; |
620 | fStyle = GrStyle::SimpleFill(); |
621 | return; |
622 | } |
623 | } |
624 | if (!this->style().hasPathEffect()) { |
625 | fRRectData.fDir = kDefaultRRectDir; |
626 | fRRectData.fStart = kDefaultRRectStart; |
627 | } else if (fStyle.isDashed()) { |
628 | // Dashing ignores the inverseness (currently). skbug.com/5421 |
629 | fRRectData.fInverted = false; |
630 | // Possible TODO here: Check whether the dash results in a single arc or line. |
631 | } |
632 | // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts. |
633 | if (!fStyle.hasPathEffect() && |
634 | fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style && |
635 | fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join && |
636 | fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 && |
637 | fRRectData.fRRect.isRect()) { |
638 | SkScalar r = fStyle.strokeRec().getWidth() / 2; |
639 | fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r)); |
640 | fStyle = GrStyle::SimpleFill(); |
641 | } |
642 | } |
643 | |
644 | void GrShape::attemptToSimplifyLine() { |
645 | SkASSERT(Type::kLine == fType); |
646 | SkASSERT(!fInheritedKey.count()); |
647 | if (fStyle.isDashed()) { |
648 | bool allOffsZero = true; |
649 | for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) { |
650 | allOffsZero = !fStyle.dashIntervals()[i]; |
651 | } |
652 | if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) { |
653 | return; |
654 | } |
655 | // Dashing ignores inverseness. |
656 | fLineData.fInverted = false; |
657 | return; |
658 | } else if (fStyle.hasPathEffect()) { |
659 | return; |
660 | } |
661 | if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) { |
662 | // Make stroke + fill be stroke since the fill is empty. |
663 | SkStrokeRec rec = fStyle.strokeRec(); |
664 | rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false); |
665 | fStyle = GrStyle(rec, nullptr); |
666 | } |
667 | if (fStyle.isSimpleFill()) { |
668 | this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty); |
669 | return; |
670 | } |
671 | if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style && |
672 | this->attemptToSimplifyStrokedLineToRRect()) { |
673 | return; |
674 | } |
675 | // Only path effects could care about the order of the points. Otherwise canonicalize |
676 | // the point order. |
677 | SkPoint* pts = fLineData.fPts; |
678 | if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) { |
679 | using std::swap; |
680 | swap(pts[0], pts[1]); |
681 | } |
682 | } |
683 | |
684 | void GrShape::attemptToSimplifyArc() { |
685 | SkASSERT(fType == Type::kArc); |
686 | SkASSERT(!fArcData.fInverted); |
687 | if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) { |
688 | this->changeType(Type::kEmpty); |
689 | return; |
690 | } |
691 | |
692 | // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses |
693 | // the full circle and doesn't use the center point is an oval. Unless it has square or round |
694 | // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're |
695 | // ignoring that for now. |
696 | if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter && |
697 | fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) { |
698 | if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) { |
699 | auto oval = fArcData.fOval; |
700 | this->changeType(Type::kRRect); |
701 | this->fRRectData.fRRect.setOval(oval); |
702 | this->fRRectData.fDir = kDefaultRRectDir; |
703 | this->fRRectData.fStart = kDefaultRRectStart; |
704 | this->fRRectData.fInverted = false; |
705 | return; |
706 | } |
707 | } |
708 | if (!fStyle.pathEffect()) { |
709 | // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always |
710 | // positive. |
711 | if (fArcData.fSweepAngleDegrees < 0) { |
712 | fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees; |
713 | fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees; |
714 | } |
715 | } |
716 | if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) { |
717 | this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f); |
718 | } |
719 | // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to |
720 | // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke |
721 | // could as well if the stroke fills the center. |
722 | } |
723 | |
724 | bool GrShape::attemptToSimplifyStrokedLineToRRect() { |
725 | SkASSERT(Type::kLine == fType); |
726 | SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style); |
727 | |
728 | SkRect rect; |
729 | SkVector outset; |
730 | // If we allowed a rotation angle for rrects we could capture all cases here. |
731 | if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) { |
732 | rect.fLeft = std::min(fLineData.fPts[0].fX, fLineData.fPts[1].fX); |
733 | rect.fRight = std::max(fLineData.fPts[0].fX, fLineData.fPts[1].fX); |
734 | rect.fTop = rect.fBottom = fLineData.fPts[0].fY; |
735 | outset.fY = fStyle.strokeRec().getWidth() / 2.f; |
736 | outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY; |
737 | } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) { |
738 | rect.fTop = std::min(fLineData.fPts[0].fY, fLineData.fPts[1].fY); |
739 | rect.fBottom = std::max(fLineData.fPts[0].fY, fLineData.fPts[1].fY); |
740 | rect.fLeft = rect.fRight = fLineData.fPts[0].fX; |
741 | outset.fX = fStyle.strokeRec().getWidth() / 2.f; |
742 | outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX; |
743 | } else { |
744 | return false; |
745 | } |
746 | rect.outset(outset.fX, outset.fY); |
747 | if (rect.isEmpty()) { |
748 | this->changeType(Type::kEmpty); |
749 | fStyle = GrStyle::SimpleFill(); |
750 | return true; |
751 | } |
752 | SkRRect rrect; |
753 | if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) { |
754 | SkASSERT(outset.fX == outset.fY); |
755 | rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY); |
756 | } else { |
757 | rrect = SkRRect::MakeRect(rect); |
758 | } |
759 | bool inverted = fLineData.fInverted && !fStyle.hasPathEffect(); |
760 | this->changeType(Type::kRRect); |
761 | fRRectData.fRRect = rrect; |
762 | fRRectData.fInverted = inverted; |
763 | fRRectData.fDir = kDefaultRRectDir; |
764 | fRRectData.fStart = kDefaultRRectStart; |
765 | fStyle = GrStyle::SimpleFill(); |
766 | return true; |
767 | } |
768 | |