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