OLD | NEW |
---|---|
1 | |
2 /* | 1 /* |
3 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
4 * | 3 * |
5 * 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 |
6 * found in the LICENSE file. | 5 * found in the LICENSE file. |
7 */ | 6 */ |
8 | 7 |
9 #include "GrReducedClip.h" | 8 #include "GrReducedClip.h" |
10 | 9 |
11 typedef SkClipStack::Element Element; | 10 typedef SkClipStack::Element Element; |
12 //////////////////////////////////////////////////////////////////////////////// | |
13 | 11 |
14 namespace GrReducedClip { | 12 static void reduced_stack_walker(const SkClipStack& stack, |
15 | 13 const SkRect& queryBounds, |
16 // helper function | 14 GrReducedClip::ElementList* result, |
17 void reduced_stack_walker(const SkClipStack& stack, | 15 int32_t* resultGenID, |
18 const SkRect& queryBounds, | 16 GrReducedClip::InitialState* initialState, |
19 ElementList* result, | 17 bool* requiresAA) { |
20 int32_t* resultGenID, | |
21 InitialState* initialState, | |
22 bool* requiresAA); | |
23 | |
24 /* | |
25 There are plenty of optimizations that could be added here. Maybe flips could be folded into | |
26 earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps | |
27 for the case where the bounds are kInsideOut_BoundsType. We could restrict earli er operations | |
28 based on later intersect operations, and perhaps remove intersect-rects. We coul d optionally | |
29 take a rect in case the caller knows a bound on what is to be drawn through this clip. | |
30 */ | |
31 void ReduceClipStack(const SkClipStack& stack, | |
32 const SkIRect& queryBounds, | |
33 ElementList* result, | |
34 int32_t* resultGenID, | |
35 InitialState* initialState, | |
36 SkIRect* tighterBounds, | |
37 bool* requiresAA) { | |
38 result->reset(); | |
39 | |
40 // The clip established by the element list might be cached based on the las t | |
41 // generation id. When we make early returns, we do not know what was the ge neration | |
42 // id that lead to the state. Make a conservative guess. | |
43 *resultGenID = stack.getTopmostGenID(); | |
44 | |
45 if (stack.isWideOpen()) { | |
46 *initialState = kAllIn_InitialState; | |
47 return; | |
48 } | |
49 | |
50 | |
51 // We initially look at whether the bounds alone is sufficient. We also use the stack bounds to | |
52 // attempt to compute the tighterBounds. | |
53 | |
54 SkClipStack::BoundsType stackBoundsType; | |
55 SkRect stackBounds; | |
56 bool iior; | |
57 stack.getBounds(&stackBounds, &stackBoundsType, &iior); | |
58 | |
59 const SkIRect* bounds = &queryBounds; | |
60 | |
61 SkRect scalarQueryBounds = SkRect::Make(queryBounds); | |
62 | |
63 if (iior) { | |
64 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); | |
65 SkRect isectRect; | |
66 if (stackBounds.contains(scalarQueryBounds)) { | |
67 *initialState = kAllIn_InitialState; | |
68 if (tighterBounds) { | |
69 *tighterBounds = queryBounds; | |
70 } | |
71 if (requiresAA) { | |
72 *requiresAA = false; | |
73 } | |
74 } else if (isectRect.intersect(stackBounds, scalarQueryBounds)) { | |
75 // If the caller asked for tighter integer bounds we may be able to | |
76 // return kAllIn and give the bounds with no elements | |
77 if (tighterBounds) { | |
78 isectRect.roundOut(tighterBounds); | |
79 SkRect scalarTighterBounds = SkRect::Make(*tighterBounds); | |
80 if (scalarTighterBounds == isectRect) { | |
81 // the round-out didn't add any area outside the clip rect. | |
82 if (requiresAA) { | |
83 *requiresAA = false; | |
84 } | |
85 *initialState = kAllIn_InitialState; | |
86 return; | |
87 } | |
88 } | |
89 *initialState = kAllOut_InitialState; | |
90 // iior should only be true if aa/non-aa status matches among all el ements. | |
91 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | |
92 bool doAA = iter.prev()->isAA(); | |
93 SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kR eplace_Op, doAA)); | |
94 if (requiresAA) { | |
95 *requiresAA = doAA; | |
96 } | |
97 } else { | |
98 *initialState = kAllOut_InitialState; | |
99 if (requiresAA) { | |
100 *requiresAA = false; | |
101 } | |
102 } | |
103 return; | |
104 } else { | |
105 if (SkClipStack::kNormal_BoundsType == stackBoundsType) { | |
106 if (!SkRect::Intersects(stackBounds, scalarQueryBounds)) { | |
107 *initialState = kAllOut_InitialState; | |
108 if (requiresAA) { | |
109 *requiresAA = false; | |
110 } | |
111 return; | |
112 } | |
113 if (tighterBounds) { | |
114 SkIRect stackIBounds; | |
115 stackBounds.roundOut(&stackIBounds); | |
116 tighterBounds->intersect(queryBounds, stackIBounds); | |
117 bounds = tighterBounds; | |
118 } | |
119 } else { | |
120 if (stackBounds.contains(scalarQueryBounds)) { | |
121 *initialState = kAllOut_InitialState; | |
122 if (requiresAA) { | |
123 *requiresAA = false; | |
124 } | |
125 return; | |
126 } | |
127 if (tighterBounds) { | |
128 *tighterBounds = queryBounds; | |
129 } | |
130 } | |
131 } | |
132 | |
133 SkRect scalarBounds = SkRect::Make(*bounds); | |
134 | |
135 // Now that we have determined the bounds to use and filtered out the trivia l cases, call the | |
136 // helper that actually walks the stack. | |
137 reduced_stack_walker(stack, scalarBounds, result, resultGenID, initialState, requiresAA); | |
138 | |
139 // The list that was computed in this function may be cached based on the ge n id of the last | |
140 // element. | |
141 SkASSERT(SkClipStack::kInvalidGenID != *resultGenID); | |
142 } | |
143 | |
144 void reduced_stack_walker(const SkClipStack& stack, | |
145 const SkRect& queryBounds, | |
146 ElementList* result, | |
147 int32_t* resultGenID, | |
148 InitialState* initialState, | |
149 bool* requiresAA) { | |
150 | 18 |
151 // walk backwards until we get to: | 19 // walk backwards until we get to: |
152 // a) the beginning | 20 // a) the beginning |
153 // b) an operation that is known to make the bounds all inside/outside | 21 // b) an operation that is known to make the bounds all inside/outside |
154 // c) a replace operation | 22 // c) a replace operation |
155 | 23 |
robertphillips
2014/10/23 12:24:17
overlength ?
tfarina
2014/10/24 00:38:57
Done.
| |
156 static const InitialState kUnknown_InitialState = static_cast<InitialState>( -1); | 24 static const GrReducedClip::InitialState kUnknown_InitialState = static_cast <GrReducedClip::InitialState>(-1); |
157 *initialState = kUnknown_InitialState; | 25 *initialState = kUnknown_InitialState; |
158 | 26 |
159 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip. | 27 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip. |
160 // TODO: track these per saved clip so that we can consider them on the forw ard pass. | 28 // TODO: track these per saved clip so that we can consider them on the forw ard pass. |
161 bool embiggens = false; | 29 bool embiggens = false; |
162 bool emsmallens = false; | 30 bool emsmallens = false; |
163 | 31 |
164 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | 32 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); |
165 int numAAElements = 0; | 33 int numAAElements = 0; |
166 while ((kUnknown_InitialState == *initialState)) { | 34 while ((kUnknown_InitialState == *initialState)) { |
167 const Element* element = iter.prev(); | 35 const Element* element = iter.prev(); |
168 if (NULL == element) { | 36 if (NULL == element) { |
169 *initialState = kAllIn_InitialState; | 37 *initialState = GrReducedClip::kAllIn_InitialState; |
170 break; | 38 break; |
171 } | 39 } |
172 if (SkClipStack::kEmptyGenID == element->getGenID()) { | 40 if (SkClipStack::kEmptyGenID == element->getGenID()) { |
173 *initialState = kAllOut_InitialState; | 41 *initialState = GrReducedClip::kAllOut_InitialState; |
174 break; | 42 break; |
175 } | 43 } |
176 if (SkClipStack::kWideOpenGenID == element->getGenID()) { | 44 if (SkClipStack::kWideOpenGenID == element->getGenID()) { |
177 *initialState = kAllIn_InitialState; | 45 *initialState = GrReducedClip::kAllIn_InitialState; |
178 break; | 46 break; |
179 } | 47 } |
180 | 48 |
181 bool skippable = false; | 49 bool skippable = false; |
182 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds | 50 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds |
183 | 51 |
184 switch (element->getOp()) { | 52 switch (element->getOp()) { |
185 case SkRegion::kDifference_Op: | 53 case SkRegion::kDifference_Op: |
186 // check if the shape subtracted either contains the entire boun ds (and makes | 54 // check if the shape subtracted either contains the entire boun ds (and makes |
187 // the clip empty) or is outside the bounds and therefore can be skipped. | 55 // the clip empty) or is outside the bounds and therefore can be skipped. |
188 if (element->isInverseFilled()) { | 56 if (element->isInverseFilled()) { |
189 if (element->contains(queryBounds)) { | 57 if (element->contains(queryBounds)) { |
190 skippable = true; | 58 skippable = true; |
191 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 59 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
192 *initialState = kAllOut_InitialState; | 60 *initialState = GrReducedClip::kAllOut_InitialState; |
193 skippable = true; | 61 skippable = true; |
194 } | 62 } |
195 } else { | 63 } else { |
196 if (element->contains(queryBounds)) { | 64 if (element->contains(queryBounds)) { |
197 *initialState = kAllOut_InitialState; | 65 *initialState = GrReducedClip::kAllOut_InitialState; |
198 skippable = true; | 66 skippable = true; |
199 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 67 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
200 skippable = true; | 68 skippable = true; |
201 } | 69 } |
202 } | 70 } |
203 if (!skippable) { | 71 if (!skippable) { |
204 emsmallens = true; | 72 emsmallens = true; |
205 } | 73 } |
206 break; | 74 break; |
207 case SkRegion::kIntersect_Op: | 75 case SkRegion::kIntersect_Op: |
208 // check if the shape intersected contains the entire bounds and therefore can | 76 // check if the shape intersected contains the entire bounds and therefore can |
209 // be skipped or it is outside the entire bounds and therefore m akes the clip | 77 // be skipped or it is outside the entire bounds and therefore m akes the clip |
210 // empty. | 78 // empty. |
211 if (element->isInverseFilled()) { | 79 if (element->isInverseFilled()) { |
212 if (element->contains(queryBounds)) { | 80 if (element->contains(queryBounds)) { |
213 *initialState = kAllOut_InitialState; | 81 *initialState = GrReducedClip::kAllOut_InitialState; |
214 skippable = true; | 82 skippable = true; |
215 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 83 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
216 skippable = true; | 84 skippable = true; |
217 } | 85 } |
218 } else { | 86 } else { |
219 if (element->contains(queryBounds)) { | 87 if (element->contains(queryBounds)) { |
220 skippable = true; | 88 skippable = true; |
221 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 89 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
222 *initialState = kAllOut_InitialState; | 90 *initialState = GrReducedClip::kAllOut_InitialState; |
223 skippable = true; | 91 skippable = true; |
224 } | 92 } |
225 } | 93 } |
226 if (!skippable) { | 94 if (!skippable) { |
227 emsmallens = true; | 95 emsmallens = true; |
228 } | 96 } |
229 break; | 97 break; |
230 case SkRegion::kUnion_Op: | 98 case SkRegion::kUnion_Op: |
231 // If the union-ed shape contains the entire bounds then after t his element | 99 // If the union-ed shape contains the entire bounds then after t his element |
232 // the bounds is entirely inside the clip. If the union-ed shape is outside the | 100 // the bounds is entirely inside the clip. If the union-ed shape is outside the |
233 // bounds then this op can be skipped. | 101 // bounds then this op can be skipped. |
234 if (element->isInverseFilled()) { | 102 if (element->isInverseFilled()) { |
235 if (element->contains(queryBounds)) { | 103 if (element->contains(queryBounds)) { |
236 skippable = true; | 104 skippable = true; |
237 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 105 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
238 *initialState = kAllIn_InitialState; | 106 *initialState = GrReducedClip::kAllIn_InitialState; |
239 skippable = true; | 107 skippable = true; |
240 } | 108 } |
241 } else { | 109 } else { |
242 if (element->contains(queryBounds)) { | 110 if (element->contains(queryBounds)) { |
243 *initialState = kAllIn_InitialState; | 111 *initialState = GrReducedClip::kAllIn_InitialState; |
244 skippable = true; | 112 skippable = true; |
245 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 113 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
246 skippable = true; | 114 skippable = true; |
247 } | 115 } |
248 } | 116 } |
249 if (!skippable) { | 117 if (!skippable) { |
250 embiggens = true; | 118 embiggens = true; |
251 } | 119 } |
252 break; | 120 break; |
253 case SkRegion::kXOR_Op: | 121 case SkRegion::kXOR_Op: |
(...skipping 18 matching lines...) Expand all Loading... | |
272 emsmallens = embiggens = true; | 140 emsmallens = embiggens = true; |
273 } | 141 } |
274 break; | 142 break; |
275 case SkRegion::kReverseDifference_Op: | 143 case SkRegion::kReverseDifference_Op: |
276 // When the bounds is entirely within the rev-diff shape then th is behaves like xor | 144 // When the bounds is entirely within the rev-diff shape then th is behaves like xor |
277 // and reverses every point inside the bounds. If the shape is c ompletely outside | 145 // and reverses every point inside the bounds. If the shape is c ompletely outside |
278 // the bounds then we know after this element is applied that th e bounds will be | 146 // the bounds then we know after this element is applied that th e bounds will be |
279 // all outside the current clip.B | 147 // all outside the current clip.B |
280 if (element->isInverseFilled()) { | 148 if (element->isInverseFilled()) { |
281 if (element->contains(queryBounds)) { | 149 if (element->contains(queryBounds)) { |
282 *initialState = kAllOut_InitialState; | 150 *initialState = GrReducedClip::kAllOut_InitialState; |
283 skippable = true; | 151 skippable = true; |
284 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 152 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
285 isFlip = true; | 153 isFlip = true; |
286 } | 154 } |
287 } else { | 155 } else { |
288 if (element->contains(queryBounds)) { | 156 if (element->contains(queryBounds)) { |
289 isFlip = true; | 157 isFlip = true; |
290 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 158 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
291 *initialState = kAllOut_InitialState; | 159 *initialState = GrReducedClip::kAllOut_InitialState; |
292 skippable = true; | 160 skippable = true; |
293 } | 161 } |
294 } | 162 } |
295 if (!skippable) { | 163 if (!skippable) { |
296 emsmallens = embiggens = true; | 164 emsmallens = embiggens = true; |
297 } | 165 } |
298 break; | 166 break; |
299 case SkRegion::kReplace_Op: | 167 case SkRegion::kReplace_Op: |
300 // Replace will always terminate our walk. We will either begin the forward walk | 168 // Replace will always terminate our walk. We will either begin the forward walk |
301 // at the replace op or detect here than the shape is either com pletely inside | 169 // at the replace op or detect here than the shape is either com pletely inside |
302 // or completely outside the bounds. In this latter case it can be skipped by | 170 // or completely outside the bounds. In this latter case it can be skipped by |
303 // setting the correct value for initialState. | 171 // setting the correct value for initialState. |
304 if (element->isInverseFilled()) { | 172 if (element->isInverseFilled()) { |
305 if (element->contains(queryBounds)) { | 173 if (element->contains(queryBounds)) { |
306 *initialState = kAllOut_InitialState; | 174 *initialState = GrReducedClip::kAllOut_InitialState; |
307 skippable = true; | 175 skippable = true; |
308 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 176 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
309 *initialState = kAllIn_InitialState; | 177 *initialState = GrReducedClip::kAllIn_InitialState; |
310 skippable = true; | 178 skippable = true; |
311 } | 179 } |
312 } else { | 180 } else { |
313 if (element->contains(queryBounds)) { | 181 if (element->contains(queryBounds)) { |
314 *initialState = kAllIn_InitialState; | 182 *initialState = GrReducedClip::kAllIn_InitialState; |
315 skippable = true; | 183 skippable = true; |
316 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { | 184 } else if (!SkRect::Intersects(element->getBounds(), queryBo unds)) { |
317 *initialState = kAllOut_InitialState; | 185 *initialState = GrReducedClip::kAllOut_InitialState; |
318 skippable = true; | 186 skippable = true; |
319 } | 187 } |
320 } | 188 } |
321 if (!skippable) { | 189 if (!skippable) { |
322 *initialState = kAllOut_InitialState; | 190 *initialState = GrReducedClip::kAllOut_InitialState; |
323 embiggens = emsmallens = true; | 191 embiggens = emsmallens = true; |
324 } | 192 } |
325 break; | 193 break; |
326 default: | 194 default: |
327 SkDEBUGFAIL("Unexpected op."); | 195 SkDEBUGFAIL("Unexpected op."); |
328 break; | 196 break; |
329 } | 197 } |
330 if (!skippable) { | 198 if (!skippable) { |
331 if (0 == result->count()) { | 199 if (0 == result->count()) { |
332 // This will be the last element. Record the stricter genID. | 200 // This will be the last element. Record the stricter genID. |
(...skipping 14 matching lines...) Expand all Loading... | |
347 } | 215 } |
348 // Intersecting an inverse shape is the same as differencing the non-inverse shape. | 216 // Intersecting an inverse shape is the same as differencing the non-inverse shape. |
349 // Replacing with an inverse shape is the same as setting initia lState=kAllIn and | 217 // Replacing with an inverse shape is the same as setting initia lState=kAllIn and |
350 // differencing the non-inverse shape. | 218 // differencing the non-inverse shape. |
351 bool isReplace = SkRegion::kReplace_Op == newElement->getOp(); | 219 bool isReplace = SkRegion::kReplace_Op == newElement->getOp(); |
352 if (newElement->isInverseFilled() && | 220 if (newElement->isInverseFilled() && |
353 (SkRegion::kIntersect_Op == newElement->getOp() || isReplace )) { | 221 (SkRegion::kIntersect_Op == newElement->getOp() || isReplace )) { |
354 newElement->invertShapeFillType(); | 222 newElement->invertShapeFillType(); |
355 newElement->setOp(SkRegion::kDifference_Op); | 223 newElement->setOp(SkRegion::kDifference_Op); |
356 if (isReplace) { | 224 if (isReplace) { |
357 SkASSERT(kAllOut_InitialState == *initialState); | 225 SkASSERT(GrReducedClip::kAllOut_InitialState == *initial State); |
358 *initialState = kAllIn_InitialState; | 226 *initialState = GrReducedClip::kAllIn_InitialState; |
359 } | 227 } |
360 } | 228 } |
361 } | 229 } |
362 } | 230 } |
363 } | 231 } |
364 | 232 |
365 if ((kAllOut_InitialState == *initialState && !embiggens) || | 233 if ((GrReducedClip::kAllOut_InitialState == *initialState && !embiggens) || |
366 (kAllIn_InitialState == *initialState && !emsmallens)) { | 234 (GrReducedClip::kAllIn_InitialState == *initialState && !emsmallens)) { |
367 result->reset(); | 235 result->reset(); |
368 } else { | 236 } else { |
369 Element* element = result->headIter().get(); | 237 Element* element = result->headIter().get(); |
370 while (element) { | 238 while (element) { |
371 bool skippable = false; | 239 bool skippable = false; |
372 switch (element->getOp()) { | 240 switch (element->getOp()) { |
373 case SkRegion::kDifference_Op: | 241 case SkRegion::kDifference_Op: |
374 // subtracting from the empty set yields the empty set. | 242 // subtracting from the empty set yields the empty set. |
375 skippable = kAllOut_InitialState == *initialState; | 243 skippable = GrReducedClip::kAllOut_InitialState == *initialS tate; |
376 break; | 244 break; |
377 case SkRegion::kIntersect_Op: | 245 case SkRegion::kIntersect_Op: |
378 // intersecting with the empty set yields the empty set | 246 // intersecting with the empty set yields the empty set |
379 if (kAllOut_InitialState == *initialState) { | 247 if (GrReducedClip::kAllOut_InitialState == *initialState) { |
380 skippable = true; | 248 skippable = true; |
381 } else { | 249 } else { |
382 // We can clear to zero and then simply draw the clip el ement. | 250 // We can clear to zero and then simply draw the clip el ement. |
383 *initialState = kAllOut_InitialState; | 251 *initialState = GrReducedClip::kAllOut_InitialState; |
384 element->setOp(SkRegion::kReplace_Op); | 252 element->setOp(SkRegion::kReplace_Op); |
385 } | 253 } |
386 break; | 254 break; |
387 case SkRegion::kUnion_Op: | 255 case SkRegion::kUnion_Op: |
388 if (kAllIn_InitialState == *initialState) { | 256 if (GrReducedClip::kAllIn_InitialState == *initialState) { |
389 // unioning the infinite plane with anything is a no-op. | 257 // unioning the infinite plane with anything is a no-op. |
390 skippable = true; | 258 skippable = true; |
391 } else { | 259 } else { |
392 // unioning the empty set with a shape is the shape. | 260 // unioning the empty set with a shape is the shape. |
393 element->setOp(SkRegion::kReplace_Op); | 261 element->setOp(SkRegion::kReplace_Op); |
394 } | 262 } |
395 break; | 263 break; |
396 case SkRegion::kXOR_Op: | 264 case SkRegion::kXOR_Op: |
397 if (kAllOut_InitialState == *initialState) { | 265 if (GrReducedClip::kAllOut_InitialState == *initialState) { |
398 // xor could be changed to diff in the kAllIn case, not sure it's a win. | 266 // xor could be changed to diff in the kAllIn case, not sure it's a win. |
399 element->setOp(SkRegion::kReplace_Op); | 267 element->setOp(SkRegion::kReplace_Op); |
400 } | 268 } |
401 break; | 269 break; |
402 case SkRegion::kReverseDifference_Op: | 270 case SkRegion::kReverseDifference_Op: |
403 if (kAllIn_InitialState == *initialState) { | 271 if (GrReducedClip::kAllIn_InitialState == *initialState) { |
404 // subtracting the whole plane will yield the empty set. | 272 // subtracting the whole plane will yield the empty set. |
405 skippable = true; | 273 skippable = true; |
406 *initialState = kAllOut_InitialState; | 274 *initialState = GrReducedClip::kAllOut_InitialState; |
407 } else { | 275 } else { |
408 // this picks up flips inserted in the backwards pass. | 276 // this picks up flips inserted in the backwards pass. |
409 skippable = element->isInverseFilled() ? | 277 skippable = element->isInverseFilled() ? |
410 !SkRect::Intersects(element->getBounds(), queryBound s) : | 278 !SkRect::Intersects(element->getBounds(), queryBound s) : |
411 element->contains(queryBounds); | 279 element->contains(queryBounds); |
412 if (skippable) { | 280 if (skippable) { |
413 *initialState = kAllIn_InitialState; | 281 *initialState = GrReducedClip::kAllIn_InitialState; |
414 } else { | 282 } else { |
415 element->setOp(SkRegion::kReplace_Op); | 283 element->setOp(SkRegion::kReplace_Op); |
416 } | 284 } |
417 } | 285 } |
418 break; | 286 break; |
419 case SkRegion::kReplace_Op: | 287 case SkRegion::kReplace_Op: |
420 skippable = false; // we would have skipped it in the backwa rds walk if we | 288 skippable = false; // we would have skipped it in the backwa rds walk if we |
421 // could've. | 289 // could've. |
422 break; | 290 break; |
423 default: | 291 default: |
424 SkDEBUGFAIL("Unexpected op."); | 292 SkDEBUGFAIL("Unexpected op."); |
425 break; | 293 break; |
426 } | 294 } |
427 if (!skippable) { | 295 if (!skippable) { |
428 break; | 296 break; |
429 } else { | 297 } else { |
430 if (element->isAA()) { | 298 if (element->isAA()) { |
431 --numAAElements; | 299 --numAAElements; |
432 } | 300 } |
433 result->popHead(); | 301 result->popHead(); |
434 element = result->headIter().get(); | 302 element = result->headIter().get(); |
435 } | 303 } |
436 } | 304 } |
437 } | 305 } |
438 if (requiresAA) { | 306 if (requiresAA) { |
439 *requiresAA = numAAElements > 0; | 307 *requiresAA = numAAElements > 0; |
440 } | 308 } |
441 | 309 |
442 if (0 == result->count()) { | 310 if (0 == result->count()) { |
443 if (*initialState == kAllIn_InitialState) { | 311 if (*initialState == GrReducedClip::kAllIn_InitialState) { |
444 *resultGenID = SkClipStack::kWideOpenGenID; | 312 *resultGenID = SkClipStack::kWideOpenGenID; |
445 } else { | 313 } else { |
446 *resultGenID = SkClipStack::kEmptyGenID; | 314 *resultGenID = SkClipStack::kEmptyGenID; |
447 } | 315 } |
448 } | 316 } |
449 } | 317 } |
450 } // namespace GrReducedClip | 318 |
319 /* | |
320 There are plenty of optimizations that could be added here. Maybe flips could be folded into | |
321 earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps | |
322 for the case where the bounds are kInsideOut_BoundsType. We could restrict earli er operations | |
323 based on later intersect operations, and perhaps remove intersect-rects. We coul d optionally | |
324 take a rect in case the caller knows a bound on what is to be drawn through this clip. | |
325 */ | |
326 void GrReducedClip::ReduceClipStack(const SkClipStack& stack, | |
tfarina
2014/10/23 04:03:08
Sorry for this big diff. I can revert if you want.
| |
327 const SkIRect& queryBounds, | |
328 ElementList* result, | |
329 int32_t* resultGenID, | |
330 InitialState* initialState, | |
331 SkIRect* tighterBounds, | |
332 bool* requiresAA) { | |
333 result->reset(); | |
334 | |
335 // The clip established by the element list might be cached based on the las t | |
336 // generation id. When we make early returns, we do not know what was the ge neration | |
337 // id that lead to the state. Make a conservative guess. | |
338 *resultGenID = stack.getTopmostGenID(); | |
339 | |
340 if (stack.isWideOpen()) { | |
341 *initialState = kAllIn_InitialState; | |
342 return; | |
343 } | |
344 | |
345 | |
346 // We initially look at whether the bounds alone is sufficient. We also use the stack bounds to | |
347 // attempt to compute the tighterBounds. | |
348 | |
349 SkClipStack::BoundsType stackBoundsType; | |
350 SkRect stackBounds; | |
351 bool iior; | |
352 stack.getBounds(&stackBounds, &stackBoundsType, &iior); | |
353 | |
354 const SkIRect* bounds = &queryBounds; | |
355 | |
356 SkRect scalarQueryBounds = SkRect::Make(queryBounds); | |
357 | |
358 if (iior) { | |
359 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); | |
360 SkRect isectRect; | |
361 if (stackBounds.contains(scalarQueryBounds)) { | |
362 *initialState = GrReducedClip::kAllIn_InitialState; | |
363 if (tighterBounds) { | |
364 *tighterBounds = queryBounds; | |
365 } | |
366 if (requiresAA) { | |
367 *requiresAA = false; | |
368 } | |
369 } else if (isectRect.intersect(stackBounds, scalarQueryBounds)) { | |
370 // If the caller asked for tighter integer bounds we may be able to | |
371 // return kAllIn and give the bounds with no elements | |
372 if (tighterBounds) { | |
373 isectRect.roundOut(tighterBounds); | |
374 SkRect scalarTighterBounds = SkRect::Make(*tighterBounds); | |
375 if (scalarTighterBounds == isectRect) { | |
376 // the round-out didn't add any area outside the clip rect. | |
377 if (requiresAA) { | |
378 *requiresAA = false; | |
379 } | |
380 *initialState = GrReducedClip::kAllIn_InitialState; | |
381 return; | |
382 } | |
383 } | |
384 *initialState = kAllOut_InitialState; | |
385 // iior should only be true if aa/non-aa status matches among all el ements. | |
386 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); | |
387 bool doAA = iter.prev()->isAA(); | |
388 SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kR eplace_Op, doAA)); | |
389 if (requiresAA) { | |
390 *requiresAA = doAA; | |
391 } | |
392 } else { | |
393 *initialState = kAllOut_InitialState; | |
394 if (requiresAA) { | |
395 *requiresAA = false; | |
396 } | |
397 } | |
398 return; | |
399 } else { | |
400 if (SkClipStack::kNormal_BoundsType == stackBoundsType) { | |
401 if (!SkRect::Intersects(stackBounds, scalarQueryBounds)) { | |
402 *initialState = kAllOut_InitialState; | |
403 if (requiresAA) { | |
404 *requiresAA = false; | |
405 } | |
406 return; | |
407 } | |
408 if (tighterBounds) { | |
409 SkIRect stackIBounds; | |
410 stackBounds.roundOut(&stackIBounds); | |
411 tighterBounds->intersect(queryBounds, stackIBounds); | |
412 bounds = tighterBounds; | |
413 } | |
414 } else { | |
415 if (stackBounds.contains(scalarQueryBounds)) { | |
416 *initialState = kAllOut_InitialState; | |
417 if (requiresAA) { | |
418 *requiresAA = false; | |
419 } | |
420 return; | |
421 } | |
422 if (tighterBounds) { | |
423 *tighterBounds = queryBounds; | |
424 } | |
425 } | |
426 } | |
427 | |
428 SkRect scalarBounds = SkRect::Make(*bounds); | |
429 | |
430 // Now that we have determined the bounds to use and filtered out the trivia l cases, call the | |
431 // helper that actually walks the stack. | |
432 reduced_stack_walker(stack, scalarBounds, result, resultGenID, initialState, requiresAA); | |
433 | |
434 // The list that was computed in this function may be cached based on the ge n id of the last | |
435 // element. | |
436 SkASSERT(SkClipStack::kInvalidGenID != *resultGenID); | |
437 } | |
OLD | NEW |