| 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 |