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