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 } |
csmartdalton
2016/08/23 00:32:38
I decided it wasn't worth it to call SkPath.isRect
bsalomon
2016/08/23 13:20:59
Rect/RRect paths should already be detected by now
csmartdalton
2016/08/23 20:04:17
You mean something upstack un-inverts the path and
bsalomon
2016/08/23 20:19:12
Oh right, inverse fill. No, not now. Though, I pla
| |
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()) { | |
csmartdalton
2016/08/23 00:32:38
I suppose we could do:
isAA() || IsPixelAligned()
| |
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 |