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