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 "GrReducedClip.h" | 8 #include "GrReducedClip.h" |
9 | 9 |
10 #include "GrClip.h" | 10 #include "GrClip.h" |
11 | 11 |
12 typedef SkClipStack::Element Element; | 12 typedef SkClipStack::Element Element; |
13 | 13 |
14 static GrReducedClip::InitialState reduced_stack_walker(const SkClipStack& stack
, | 14 /** |
15 const SkRect& queryBound
s, | 15 * There are plenty of optimizations that could be added here. Maybe flips could
be folded into |
16 const SkIRect& clipIBoun
ds, | 16 * earlier operations. Or would inserting flips and reversing earlier ops ever b
e a win? Perhaps |
17 GrReducedClip::ElementLi
st* result, | 17 * for the case where the bounds are kInsideOut_BoundsType. We could restrict ea
rlier operations |
18 int32_t* resultGenID, | 18 * based on later intersect operations, and perhaps remove intersect-rects. We c
ould optionally |
19 bool* requiresAA) { | 19 * take a rect in case the caller knows a bound on what is to be drawn through t
his clip. |
| 20 */ |
| 21 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
) { |
| 22 SkASSERT(!queryBounds.isEmpty()); |
| 23 fHasIBounds = false; |
20 | 24 |
| 25 if (stack.isWideOpen()) { |
| 26 fInitialState = InitialState::kAllIn; |
| 27 return; |
| 28 } |
| 29 |
| 30 SkClipStack::BoundsType stackBoundsType; |
| 31 SkRect stackBounds; |
| 32 bool iior; |
| 33 stack.getBounds(&stackBounds, &stackBoundsType, &iior); |
| 34 |
| 35 if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)
) { |
| 36 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType; |
| 37 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut
; |
| 38 return; |
| 39 } |
| 40 |
| 41 if (iior) { |
| 42 // "Is intersection of rects" means the clip is a single rect indicated
by the stack bounds. |
| 43 // This should only be true if aa/non-aa status matches among all elemen
ts. |
| 44 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); |
| 45 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); |
| 46 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) { |
| 47 // The clip is a non-aa rect. This is the one spot where we can actu
ally implement the |
| 48 // clip (using fIBounds) rather than just telling the caller what it
should be. |
| 49 stackBounds.round(&fIBounds); |
| 50 fHasIBounds = true; |
| 51 fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : Initial
State::kAllIn; |
| 52 return; |
| 53 } |
| 54 if (GrClip::IsInsideClip(stackBounds, queryBounds)) { |
| 55 fInitialState = InitialState::kAllIn; |
| 56 return; |
| 57 } |
| 58 |
| 59 SkRect tightBounds; |
| 60 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds)); |
| 61 fIBounds = GrClip::GetPixelIBounds(tightBounds); |
| 62 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOu
tsideClip above. |
| 63 fHasIBounds = true; |
| 64 |
| 65 // Implement the clip with an AA rect element. |
| 66 fElements.addToHead(stackBounds, SkRegion::kReplace_Op, true/*doAA*/); |
| 67 fElementsGenID = stack.getTopmostGenID(); |
| 68 fRequiresAA = true; |
| 69 |
| 70 fInitialState = InitialState::kAllOut; |
| 71 return; |
| 72 } |
| 73 |
| 74 SkRect tighterQuery = queryBounds; |
| 75 if (SkClipStack::kNormal_BoundsType == stackBoundsType) { |
| 76 // Tighten the query by introducing a new clip at the stack's pixel boun
daries. (This new |
| 77 // clip will be enforced by the scissor through fIBounds.) |
| 78 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds
))); |
| 79 } |
| 80 |
| 81 fIBounds = GrClip::GetPixelIBounds(tighterQuery); |
| 82 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsid
eClip above. |
| 83 fHasIBounds = true; |
| 84 |
| 85 // Now that we have determined the bounds to use and filtered out the trivia
l cases, call the |
| 86 // helper that actually walks the stack. |
| 87 this->walkStack(stack, tighterQuery); |
| 88 } |
| 89 |
| 90 void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
s) { |
21 // walk backwards until we get to: | 91 // walk backwards until we get to: |
22 // a) the beginning | 92 // a) the beginning |
23 // b) an operation that is known to make the bounds all inside/outside | 93 // b) an operation that is known to make the bounds all inside/outside |
24 // c) a replace operation | 94 // c) a replace operation |
25 | 95 |
26 enum class InitialTriState { | 96 enum class InitialTriState { |
27 kUnknown = -1, | 97 kUnknown = -1, |
28 kAllIn = (int)GrReducedClip::InitialState::kAllIn, | 98 kAllIn = (int)GrReducedClip::InitialState::kAllIn, |
29 kAllOut = (int)GrReducedClip::InitialState::kAllOut | 99 kAllOut = (int)GrReducedClip::InitialState::kAllOut |
30 } initialState = InitialTriState::kUnknown; | 100 } initialTriState = InitialTriState::kUnknown; |
31 | 101 |
32 // During our backwards walk, track whether we've seen ops that either grow
or shrink the clip. | 102 // During our backwards walk, track whether we've seen ops that either grow
or shrink the clip. |
33 // TODO: track these per saved clip so that we can consider them on the forw
ard pass. | 103 // TODO: track these per saved clip so that we can consider them on the forw
ard pass. |
34 bool embiggens = false; | 104 bool embiggens = false; |
35 bool emsmallens = false; | 105 bool emsmallens = false; |
36 | 106 |
37 // We use a slightly relaxed set of query bounds for element containment tes
ts. This is to | 107 // We use a slightly relaxed set of query bounds for element containment tes
ts. This is to |
38 // account for floating point rounding error that may have occurred during c
oord transforms. | 108 // account for floating point rounding error that may have occurred during c
oord transforms. |
39 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance, | 109 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance, |
40 GrClip::kBoundsTolerance); | 110 GrClip::kBoundsTolerance); |
41 | 111 |
42 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | 112 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); |
43 int numAAElements = 0; | 113 int numAAElements = 0; |
44 while (InitialTriState::kUnknown == initialState) { | 114 while (InitialTriState::kUnknown == initialTriState) { |
45 const Element* element = iter.prev(); | 115 const Element* element = iter.prev(); |
46 if (nullptr == element) { | 116 if (nullptr == element) { |
47 initialState = InitialTriState::kAllIn; | 117 initialTriState = InitialTriState::kAllIn; |
48 break; | 118 break; |
49 } | 119 } |
50 if (SkClipStack::kEmptyGenID == element->getGenID()) { | 120 if (SkClipStack::kEmptyGenID == element->getGenID()) { |
51 initialState = InitialTriState::kAllOut; | 121 initialTriState = InitialTriState::kAllOut; |
52 break; | 122 break; |
53 } | 123 } |
54 if (SkClipStack::kWideOpenGenID == element->getGenID()) { | 124 if (SkClipStack::kWideOpenGenID == element->getGenID()) { |
55 initialState = InitialTriState::kAllIn; | 125 initialTriState = InitialTriState::kAllIn; |
56 break; | 126 break; |
57 } | 127 } |
58 | 128 |
59 bool skippable = false; | 129 bool skippable = false; |
60 bool isFlip = false; // does this op just flip the in/out state of every
point in the bounds | 130 bool isFlip = false; // does this op just flip the in/out state of every
point in the bounds |
61 | 131 |
62 switch (element->getOp()) { | 132 switch (element->getOp()) { |
63 case SkRegion::kDifference_Op: | 133 case SkRegion::kDifference_Op: |
64 // check if the shape subtracted either contains the entire boun
ds (and makes | 134 // check if the shape subtracted either contains the entire boun
ds (and makes |
65 // the clip empty) or is outside the bounds and therefore can be
skipped. | 135 // the clip empty) or is outside the bounds and therefore can be
skipped. |
66 if (element->isInverseFilled()) { | 136 if (element->isInverseFilled()) { |
67 if (element->contains(relaxedQueryBounds)) { | 137 if (element->contains(relaxedQueryBounds)) { |
68 skippable = true; | 138 skippable = true; |
69 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 139 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
70 initialState = InitialTriState::kAllOut; | 140 initialTriState = InitialTriState::kAllOut; |
71 skippable = true; | 141 skippable = true; |
72 } | 142 } |
73 } else { | 143 } else { |
74 if (element->contains(relaxedQueryBounds)) { | 144 if (element->contains(relaxedQueryBounds)) { |
75 initialState = InitialTriState::kAllOut; | 145 initialTriState = InitialTriState::kAllOut; |
76 skippable = true; | 146 skippable = true; |
77 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 147 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
78 skippable = true; | 148 skippable = true; |
79 } | 149 } |
80 } | 150 } |
81 if (!skippable) { | 151 if (!skippable) { |
82 emsmallens = true; | 152 emsmallens = true; |
83 } | 153 } |
84 break; | 154 break; |
85 case SkRegion::kIntersect_Op: | 155 case SkRegion::kIntersect_Op: |
86 // check if the shape intersected contains the entire bounds and
therefore can | 156 // check if the shape intersected contains the entire bounds and
therefore can |
87 // be skipped or it is outside the entire bounds and therefore m
akes the clip | 157 // be skipped or it is outside the entire bounds and therefore m
akes the clip |
88 // empty. | 158 // empty. |
89 if (element->isInverseFilled()) { | 159 if (element->isInverseFilled()) { |
90 if (element->contains(relaxedQueryBounds)) { | 160 if (element->contains(relaxedQueryBounds)) { |
91 initialState = InitialTriState::kAllOut; | 161 initialTriState = InitialTriState::kAllOut; |
92 skippable = true; | 162 skippable = true; |
93 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 163 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
94 skippable = true; | 164 skippable = true; |
95 } | 165 } |
96 } else { | 166 } else { |
97 if (element->contains(relaxedQueryBounds)) { | 167 if (element->contains(relaxedQueryBounds)) { |
98 skippable = true; | 168 skippable = true; |
99 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 169 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
100 initialState = InitialTriState::kAllOut; | 170 initialTriState = InitialTriState::kAllOut; |
| 171 skippable = true; |
| 172 } else if (!embiggens && !element->isAA() && |
| 173 Element::kRect_Type == element->getType()) { |
| 174 // fIBounds and queryBounds have already acccounted for
this element via |
| 175 // clip stack bounds; here we just apply the non-aa roun
ding effect. |
| 176 SkIRect nonaaRect; |
| 177 element->getRect().round(&nonaaRect); |
| 178 if (!this->intersectIBounds(nonaaRect)) { |
| 179 return; |
| 180 } |
101 skippable = true; | 181 skippable = true; |
102 } | 182 } |
103 } | 183 } |
104 if (!skippable) { | 184 if (!skippable) { |
105 emsmallens = true; | 185 emsmallens = true; |
106 } | 186 } |
107 break; | 187 break; |
108 case SkRegion::kUnion_Op: | 188 case SkRegion::kUnion_Op: |
109 // If the union-ed shape contains the entire bounds then after t
his element | 189 // If the union-ed shape contains the entire bounds then after t
his element |
110 // the bounds is entirely inside the clip. If the union-ed shape
is outside the | 190 // the bounds is entirely inside the clip. If the union-ed shape
is outside the |
111 // bounds then this op can be skipped. | 191 // bounds then this op can be skipped. |
112 if (element->isInverseFilled()) { | 192 if (element->isInverseFilled()) { |
113 if (element->contains(relaxedQueryBounds)) { | 193 if (element->contains(relaxedQueryBounds)) { |
114 skippable = true; | 194 skippable = true; |
115 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 195 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
116 initialState = InitialTriState::kAllIn; | 196 initialTriState = InitialTriState::kAllIn; |
117 skippable = true; | 197 skippable = true; |
118 } | 198 } |
119 } else { | 199 } else { |
120 if (element->contains(relaxedQueryBounds)) { | 200 if (element->contains(relaxedQueryBounds)) { |
121 initialState = InitialTriState::kAllIn; | 201 initialTriState = InitialTriState::kAllIn; |
122 skippable = true; | 202 skippable = true; |
123 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 203 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
124 skippable = true; | 204 skippable = true; |
125 } | 205 } |
126 } | 206 } |
127 if (!skippable) { | 207 if (!skippable) { |
128 embiggens = true; | 208 embiggens = true; |
129 } | 209 } |
130 break; | 210 break; |
131 case SkRegion::kXOR_Op: | 211 case SkRegion::kXOR_Op: |
(...skipping 18 matching lines...) Expand all Loading... |
150 emsmallens = embiggens = true; | 230 emsmallens = embiggens = true; |
151 } | 231 } |
152 break; | 232 break; |
153 case SkRegion::kReverseDifference_Op: | 233 case SkRegion::kReverseDifference_Op: |
154 // When the bounds is entirely within the rev-diff shape then th
is behaves like xor | 234 // When the bounds is entirely within the rev-diff shape then th
is behaves like xor |
155 // and reverses every point inside the bounds. If the shape is c
ompletely outside | 235 // and reverses every point inside the bounds. If the shape is c
ompletely outside |
156 // the bounds then we know after this element is applied that th
e bounds will be | 236 // the bounds then we know after this element is applied that th
e bounds will be |
157 // all outside the current clip.B | 237 // all outside the current clip.B |
158 if (element->isInverseFilled()) { | 238 if (element->isInverseFilled()) { |
159 if (element->contains(relaxedQueryBounds)) { | 239 if (element->contains(relaxedQueryBounds)) { |
160 initialState = InitialTriState::kAllOut; | 240 initialTriState = InitialTriState::kAllOut; |
161 skippable = true; | 241 skippable = true; |
162 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 242 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
163 isFlip = true; | 243 isFlip = true; |
164 } | 244 } |
165 } else { | 245 } else { |
166 if (element->contains(relaxedQueryBounds)) { | 246 if (element->contains(relaxedQueryBounds)) { |
167 isFlip = true; | 247 isFlip = true; |
168 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 248 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
169 initialState = InitialTriState::kAllOut; | 249 initialTriState = InitialTriState::kAllOut; |
170 skippable = true; | 250 skippable = true; |
171 } | 251 } |
172 } | 252 } |
173 if (!skippable) { | 253 if (!skippable) { |
174 emsmallens = embiggens = true; | 254 emsmallens = embiggens = true; |
175 } | 255 } |
176 break; | 256 break; |
177 | 257 |
178 case SkRegion::kReplace_Op: | 258 case SkRegion::kReplace_Op: |
179 // Replace will always terminate our walk. We will either begin
the forward walk | 259 // Replace will always terminate our walk. We will either begin
the forward walk |
180 // at the replace op or detect here than the shape is either com
pletely inside | 260 // at the replace op or detect here than the shape is either com
pletely inside |
181 // or completely outside the bounds. In this latter case it can
be skipped by | 261 // or completely outside the bounds. In this latter case it can
be skipped by |
182 // setting the correct value for initialState. | 262 // setting the correct value for initialTriState. |
183 if (element->isInverseFilled()) { | 263 if (element->isInverseFilled()) { |
184 if (element->contains(relaxedQueryBounds)) { | 264 if (element->contains(relaxedQueryBounds)) { |
185 initialState = InitialTriState::kAllOut; | 265 initialTriState = InitialTriState::kAllOut; |
186 skippable = true; | 266 skippable = true; |
187 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 267 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
188 initialState = InitialTriState::kAllIn; | 268 initialTriState = InitialTriState::kAllIn; |
189 skippable = true; | 269 skippable = true; |
190 } | 270 } |
191 } else { | 271 } else { |
192 if (element->contains(relaxedQueryBounds)) { | 272 if (element->contains(relaxedQueryBounds)) { |
193 initialState = InitialTriState::kAllIn; | 273 initialTriState = InitialTriState::kAllIn; |
194 skippable = true; | 274 skippable = true; |
195 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { | 275 } else if (GrClip::IsOutsideClip(element->getBounds(), query
Bounds)) { |
196 initialState = InitialTriState::kAllOut; | 276 initialTriState = InitialTriState::kAllOut; |
| 277 skippable = true; |
| 278 } else if (!embiggens && !element->isAA() && |
| 279 Element::kRect_Type == element->getType()) { |
| 280 // fIBounds and queryBounds have already acccounted for
this element via |
| 281 // clip stack bounds; here we just apply the non-aa roun
ding effect. |
| 282 SkIRect nonaaRect; |
| 283 element->getRect().round(&nonaaRect); |
| 284 if (!this->intersectIBounds(nonaaRect)) { |
| 285 return; |
| 286 } |
| 287 initialTriState = InitialTriState::kAllIn; |
197 skippable = true; | 288 skippable = true; |
198 } | 289 } |
199 } | 290 } |
200 if (!skippable) { | 291 if (!skippable) { |
201 initialState = InitialTriState::kAllOut; | 292 initialTriState = InitialTriState::kAllOut; |
202 embiggens = emsmallens = true; | 293 embiggens = emsmallens = true; |
203 } | 294 } |
204 break; | 295 break; |
205 default: | 296 default: |
206 SkDEBUGFAIL("Unexpected op."); | 297 SkDEBUGFAIL("Unexpected op."); |
207 break; | 298 break; |
208 } | 299 } |
209 if (!skippable) { | 300 if (!skippable) { |
210 if (0 == result->count()) { | 301 if (0 == fElements.count()) { |
211 // This will be the last element. Record the stricter genID. | 302 // This will be the last element. Record the stricter genID. |
212 *resultGenID = element->getGenID(); | 303 fElementsGenID = element->getGenID(); |
213 } | 304 } |
214 | 305 |
215 // if it is a flip, change it to a bounds-filling rect | 306 // if it is a flip, change it to a bounds-filling rect |
216 if (isFlip) { | 307 if (isFlip) { |
217 SkASSERT(SkRegion::kXOR_Op == element->getOp() || | 308 SkASSERT(SkRegion::kXOR_Op == element->getOp() || |
218 SkRegion::kReverseDifference_Op == element->getOp()); | 309 SkRegion::kReverseDifference_Op == element->getOp()); |
219 result->addToHead(SkRect::Make(clipIBounds), SkRegion::kReverseD
ifference_Op, | 310 fElements.addToHead(SkRect::Make(fIBounds), SkRegion::kReverseDi
fference_Op, false); |
220 false); | |
221 } else { | 311 } else { |
222 Element* newElement = result->addToHead(*element); | 312 Element* newElement = fElements.addToHead(*element); |
223 if (newElement->isAA()) { | 313 if (newElement->isAA()) { |
224 ++numAAElements; | 314 ++numAAElements; |
225 } | 315 } |
226 // Intersecting an inverse shape is the same as differencing the
non-inverse shape. | 316 // Intersecting an inverse shape is the same as differencing the
non-inverse shape. |
227 // Replacing with an inverse shape is the same as setting initia
lState=kAllIn and | 317 // Replacing with an inverse shape is the same as setting initia
lState=kAllIn and |
228 // differencing the non-inverse shape. | 318 // differencing the non-inverse shape. |
229 bool isReplace = SkRegion::kReplace_Op == newElement->getOp(); | 319 bool isReplace = SkRegion::kReplace_Op == newElement->getOp(); |
230 if (newElement->isInverseFilled() && | 320 if (newElement->isInverseFilled() && |
231 (SkRegion::kIntersect_Op == newElement->getOp() || isReplace
)) { | 321 (SkRegion::kIntersect_Op == newElement->getOp() || isReplace
)) { |
232 newElement->invertShapeFillType(); | 322 newElement->invertShapeFillType(); |
233 newElement->setOp(SkRegion::kDifference_Op); | 323 newElement->setOp(SkRegion::kDifference_Op); |
234 if (isReplace) { | 324 if (isReplace) { |
235 SkASSERT(InitialTriState::kAllOut == initialState); | 325 SkASSERT(InitialTriState::kAllOut == initialTriState); |
236 initialState = InitialTriState::kAllIn; | 326 initialTriState = InitialTriState::kAllIn; |
237 } | 327 } |
238 } | 328 } |
239 } | 329 } |
240 } | 330 } |
241 } | 331 } |
242 | 332 |
243 if ((InitialTriState::kAllOut == initialState && !embiggens) || | 333 if ((InitialTriState::kAllOut == initialTriState && !embiggens) || |
244 (InitialTriState::kAllIn == initialState && !emsmallens)) { | 334 (InitialTriState::kAllIn == initialTriState && !emsmallens)) { |
245 result->reset(); | 335 fElements.reset(); |
246 numAAElements = 0; | 336 numAAElements = 0; |
247 } else { | 337 } else { |
248 Element* element = result->headIter().get(); | 338 Element* element = fElements.headIter().get(); |
249 while (element) { | 339 while (element) { |
250 bool skippable = false; | 340 bool skippable = false; |
251 switch (element->getOp()) { | 341 switch (element->getOp()) { |
252 case SkRegion::kDifference_Op: | 342 case SkRegion::kDifference_Op: |
253 // subtracting from the empty set yields the empty set. | 343 // subtracting from the empty set yields the empty set. |
254 skippable = InitialTriState::kAllOut == initialState; | 344 skippable = InitialTriState::kAllOut == initialTriState; |
255 break; | 345 break; |
256 case SkRegion::kIntersect_Op: | 346 case SkRegion::kIntersect_Op: |
257 // intersecting with the empty set yields the empty set | 347 // intersecting with the empty set yields the empty set |
258 if (InitialTriState::kAllOut == initialState) { | 348 if (InitialTriState::kAllOut == initialTriState) { |
259 skippable = true; | 349 skippable = true; |
260 } else { | 350 } else { |
261 // We can clear to zero and then simply draw the clip el
ement. | 351 // We can clear to zero and then simply draw the clip el
ement. |
262 initialState = InitialTriState::kAllOut; | 352 initialTriState = InitialTriState::kAllOut; |
263 element->setOp(SkRegion::kReplace_Op); | 353 element->setOp(SkRegion::kReplace_Op); |
264 } | 354 } |
265 break; | 355 break; |
266 case SkRegion::kUnion_Op: | 356 case SkRegion::kUnion_Op: |
267 if (InitialTriState::kAllIn == initialState) { | 357 if (InitialTriState::kAllIn == initialTriState) { |
268 // unioning the infinite plane with anything is a no-op. | 358 // unioning the infinite plane with anything is a no-op. |
269 skippable = true; | 359 skippable = true; |
270 } else { | 360 } else { |
271 // unioning the empty set with a shape is the shape. | 361 // unioning the empty set with a shape is the shape. |
272 element->setOp(SkRegion::kReplace_Op); | 362 element->setOp(SkRegion::kReplace_Op); |
273 } | 363 } |
274 break; | 364 break; |
275 case SkRegion::kXOR_Op: | 365 case SkRegion::kXOR_Op: |
276 if (InitialTriState::kAllOut == initialState) { | 366 if (InitialTriState::kAllOut == initialTriState) { |
277 // xor could be changed to diff in the kAllIn case, not
sure it's a win. | 367 // xor could be changed to diff in the kAllIn case, not
sure it's a win. |
278 element->setOp(SkRegion::kReplace_Op); | 368 element->setOp(SkRegion::kReplace_Op); |
279 } | 369 } |
280 break; | 370 break; |
281 case SkRegion::kReverseDifference_Op: | 371 case SkRegion::kReverseDifference_Op: |
282 if (InitialTriState::kAllIn == initialState) { | 372 if (InitialTriState::kAllIn == initialTriState) { |
283 // subtracting the whole plane will yield the empty set. | 373 // subtracting the whole plane will yield the empty set. |
284 skippable = true; | 374 skippable = true; |
285 initialState = InitialTriState::kAllOut; | 375 initialTriState = InitialTriState::kAllOut; |
286 } else { | 376 } else { |
287 // this picks up flips inserted in the backwards pass. | 377 // this picks up flips inserted in the backwards pass. |
288 skippable = element->isInverseFilled() ? | 378 skippable = element->isInverseFilled() ? |
289 GrClip::IsOutsideClip(element->getBounds(), queryBou
nds) : | 379 GrClip::IsOutsideClip(element->getBounds(), queryBou
nds) : |
290 element->contains(relaxedQueryBounds); | 380 element->contains(relaxedQueryBounds); |
291 if (skippable) { | 381 if (skippable) { |
292 initialState = InitialTriState::kAllIn; | 382 initialTriState = InitialTriState::kAllIn; |
293 } else { | 383 } else { |
294 element->setOp(SkRegion::kReplace_Op); | 384 element->setOp(SkRegion::kReplace_Op); |
295 } | 385 } |
296 } | 386 } |
297 break; | 387 break; |
298 case SkRegion::kReplace_Op: | 388 case SkRegion::kReplace_Op: |
299 skippable = false; // we would have skipped it in the backwa
rds walk if we | 389 skippable = false; // we would have skipped it in the backwa
rds walk if we |
300 // could've. | 390 // could've. |
301 break; | 391 break; |
302 default: | 392 default: |
303 SkDEBUGFAIL("Unexpected op."); | 393 SkDEBUGFAIL("Unexpected op."); |
304 break; | 394 break; |
305 } | 395 } |
306 if (!skippable) { | 396 if (!skippable) { |
307 break; | 397 break; |
308 } else { | 398 } else { |
309 if (element->isAA()) { | 399 if (element->isAA()) { |
310 --numAAElements; | 400 --numAAElements; |
311 } | 401 } |
312 result->popHead(); | 402 fElements.popHead(); |
313 element = result->headIter().get(); | 403 element = fElements.headIter().get(); |
314 } | 404 } |
315 } | 405 } |
316 } | 406 } |
317 *requiresAA = numAAElements > 0; | 407 fRequiresAA = numAAElements > 0; |
318 | 408 |
319 SkASSERT(InitialTriState::kUnknown != initialState); | 409 SkASSERT(InitialTriState::kUnknown != initialTriState); |
320 return static_cast<GrReducedClip::InitialState>(initialState); | 410 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState); |
321 } | 411 } |
322 | 412 |
323 /* | 413 inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) { |
324 There are plenty of optimizations that could be added here. Maybe flips could be
folded into | 414 SkASSERT(fHasIBounds); |
325 earlier operations. Or would inserting flips and reversing earlier ops ever be a
win? Perhaps | 415 if (!fIBounds.intersect(irect)) { |
326 for the case where the bounds are kInsideOut_BoundsType. We could restrict earli
er operations | 416 fHasIBounds = false; |
327 based on later intersect operations, and perhaps remove intersect-rects. We coul
d optionally | 417 fElements.reset(); |
328 take a rect in case the caller knows a bound on what is to be drawn through this
clip. | 418 fRequiresAA = false; |
329 */ | 419 fInitialState = InitialState::kAllOut; |
330 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
) { | 420 return false; |
331 SkASSERT(!queryBounds.isEmpty()); | |
332 fHasIBounds = false; | |
333 | |
334 if (stack.isWideOpen()) { | |
335 fInitialState = InitialState::kAllIn; | |
336 return; | |
337 } | 421 } |
338 | 422 return true; |
339 SkClipStack::BoundsType stackBoundsType; | |
340 SkRect stackBounds; | |
341 bool iior; | |
342 stack.getBounds(&stackBounds, &stackBoundsType, &iior); | |
343 | |
344 if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)
) { | |
345 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType; | |
346 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut
; | |
347 return; | |
348 } | |
349 | |
350 if (iior) { | |
351 // "Is intersection of rects" means the clip is a single rect indicated
by the stack bounds. | |
352 // This should only be true if aa/non-aa status matches among all elemen
ts. | |
353 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); | |
354 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | |
355 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) { | |
356 // The clip is a non-aa rect. This is the one spot where we can actu
ally implement the | |
357 // clip (using fIBounds) rather than just telling the caller what it
should be. | |
358 stackBounds.round(&fIBounds); | |
359 fHasIBounds = true; | |
360 fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : Initial
State::kAllIn; | |
361 return; | |
362 } | |
363 if (GrClip::IsInsideClip(stackBounds, queryBounds)) { | |
364 fInitialState = InitialState::kAllIn; | |
365 return; | |
366 } | |
367 | |
368 SkRect tightBounds; | |
369 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds)); | |
370 fIBounds = GrClip::GetPixelIBounds(tightBounds); | |
371 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOu
tsideClip above. | |
372 fHasIBounds = true; | |
373 | |
374 // Implement the clip with an AA rect element. | |
375 fElements.addToHead(stackBounds, SkRegion::kReplace_Op, true/*doAA*/); | |
376 fElementsGenID = stack.getTopmostGenID(); | |
377 fRequiresAA = true; | |
378 | |
379 fInitialState = InitialState::kAllOut; | |
380 return; | |
381 } | |
382 | |
383 SkRect tighterQuery = queryBounds; | |
384 if (SkClipStack::kNormal_BoundsType == stackBoundsType) { | |
385 // Tighten the query by introducing a new clip at the stack's pixel boun
daries. (This new | |
386 // clip will be enforced by the scissor through fIBounds.) | |
387 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds
))); | |
388 } | |
389 | |
390 fIBounds = GrClip::GetPixelIBounds(tighterQuery); | |
391 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsid
eClip above. | |
392 fHasIBounds = true; | |
393 | |
394 // Now that we have determined the bounds to use and filtered out the trivia
l cases, call the | |
395 // helper that actually walks the stack. | |
396 fInitialState = reduced_stack_walker(stack, tighterQuery, fIBounds, &fElemen
ts, &fElementsGenID, | |
397 &fRequiresAA); | |
398 } | 423 } |
OLD | NEW |