bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 1 | /* |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 2 | * Copyright 2016 Google Inc. |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "GrReducedClip.h" |
| 9 | |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 10 | #include "GrClip.h" |
| 11 | |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 12 | typedef SkClipStack::Element Element; |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 13 | |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 14 | static GrReducedClip::InitialState reduced_stack_walker(const SkClipStack& stack, |
| 15 | const SkRect& queryBounds, |
| 16 | const SkIRect& clipIBounds, |
| 17 | GrReducedClip::ElementList* result, |
| 18 | int32_t* resultGenID, |
| 19 | bool* requiresAA) { |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 20 | |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 21 | // walk backwards until we get to: |
| 22 | // a) the beginning |
| 23 | // b) an operation that is known to make the bounds all inside/outside |
| 24 | // c) a replace operation |
| 25 | |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 26 | enum class InitialTriState { |
| 27 | kUnknown = -1, |
| 28 | kAllIn = (int)GrReducedClip::InitialState::kAllIn, |
| 29 | kAllOut = (int)GrReducedClip::InitialState::kAllOut |
| 30 | } initialState = InitialTriState::kUnknown; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 31 | |
| 32 | // During our backwards walk, track whether we've seen ops that either grow or shrink the clip. |
| 33 | // TODO: track these per saved clip so that we can consider them on the forward pass. |
| 34 | bool embiggens = false; |
| 35 | bool emsmallens = false; |
| 36 | |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 37 | // We use a slightly relaxed set of query bounds for element containment tests. This is to |
| 38 | // account for floating point rounding error that may have occurred during coord transforms. |
| 39 | SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance, |
| 40 | GrClip::kBoundsTolerance); |
| 41 | |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 42 | SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); |
| 43 | int numAAElements = 0; |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 44 | while (InitialTriState::kUnknown == initialState) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 45 | const Element* element = iter.prev(); |
halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 46 | if (nullptr == element) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 47 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 48 | break; |
| 49 | } |
| 50 | if (SkClipStack::kEmptyGenID == element->getGenID()) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 51 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 52 | break; |
| 53 | } |
| 54 | if (SkClipStack::kWideOpenGenID == element->getGenID()) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 55 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 56 | break; |
| 57 | } |
| 58 | |
| 59 | bool skippable = false; |
| 60 | bool isFlip = false; // does this op just flip the in/out state of every point in the bounds |
| 61 | |
| 62 | switch (element->getOp()) { |
| 63 | case SkRegion::kDifference_Op: |
| 64 | // check if the shape subtracted either contains the entire bounds (and makes |
| 65 | // the clip empty) or is outside the bounds and therefore can be skipped. |
| 66 | if (element->isInverseFilled()) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 67 | if (element->contains(relaxedQueryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 68 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 69 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 70 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 71 | skippable = true; |
| 72 | } |
| 73 | } else { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 74 | if (element->contains(relaxedQueryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 75 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 76 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 77 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 78 | skippable = true; |
| 79 | } |
| 80 | } |
| 81 | if (!skippable) { |
| 82 | emsmallens = true; |
| 83 | } |
| 84 | break; |
| 85 | case SkRegion::kIntersect_Op: |
| 86 | // check if the shape intersected contains the entire bounds and therefore can |
| 87 | // be skipped or it is outside the entire bounds and therefore makes the clip |
| 88 | // empty. |
| 89 | if (element->isInverseFilled()) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 90 | if (element->contains(relaxedQueryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 91 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 92 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 93 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 94 | skippable = true; |
| 95 | } |
| 96 | } else { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 97 | if (element->contains(relaxedQueryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 98 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 99 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 100 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 101 | skippable = true; |
| 102 | } |
| 103 | } |
| 104 | if (!skippable) { |
| 105 | emsmallens = true; |
| 106 | } |
| 107 | break; |
| 108 | case SkRegion::kUnion_Op: |
| 109 | // If the union-ed shape contains the entire bounds then after this element |
| 110 | // the bounds is entirely inside the clip. If the union-ed shape is outside the |
| 111 | // bounds then this op can be skipped. |
| 112 | if (element->isInverseFilled()) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 113 | if (element->contains(relaxedQueryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 114 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 115 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 116 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 117 | skippable = true; |
| 118 | } |
| 119 | } else { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 120 | if (element->contains(relaxedQueryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 121 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 122 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 123 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 124 | skippable = true; |
| 125 | } |
| 126 | } |
| 127 | if (!skippable) { |
| 128 | embiggens = true; |
| 129 | } |
| 130 | break; |
| 131 | case SkRegion::kXOR_Op: |
| 132 | // If the bounds is entirely inside the shape being xor-ed then the effect is |
| 133 | // to flip the inside/outside state of every point in the bounds. We may be |
| 134 | // able to take advantage of this in the forward pass. If the xor-ed shape |
| 135 | // doesn't intersect the bounds then it can be skipped. |
| 136 | if (element->isInverseFilled()) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 137 | if (element->contains(relaxedQueryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 138 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 139 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 140 | isFlip = true; |
| 141 | } |
| 142 | } else { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 143 | if (element->contains(relaxedQueryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 144 | isFlip = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 145 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 146 | skippable = true; |
| 147 | } |
| 148 | } |
| 149 | if (!skippable) { |
| 150 | emsmallens = embiggens = true; |
| 151 | } |
| 152 | break; |
| 153 | case SkRegion::kReverseDifference_Op: |
| 154 | // When the bounds is entirely within the rev-diff shape then this behaves like xor |
| 155 | // and reverses every point inside the bounds. If the shape is completely outside |
| 156 | // the bounds then we know after this element is applied that the bounds will be |
| 157 | // all outside the current clip.B |
| 158 | if (element->isInverseFilled()) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 159 | if (element->contains(relaxedQueryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 160 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 161 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 162 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 163 | isFlip = true; |
| 164 | } |
| 165 | } else { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 166 | if (element->contains(relaxedQueryBounds)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 167 | isFlip = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 168 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 169 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 170 | skippable = true; |
| 171 | } |
| 172 | } |
| 173 | if (!skippable) { |
| 174 | emsmallens = embiggens = true; |
| 175 | } |
| 176 | break; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 177 | |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 178 | case SkRegion::kReplace_Op: |
| 179 | // Replace will always terminate our walk. We will either begin the forward walk |
| 180 | // at the replace op or detect here than the shape is either completely inside |
| 181 | // or completely outside the bounds. In this latter case it can be skipped by |
| 182 | // setting the correct value for initialState. |
| 183 | if (element->isInverseFilled()) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 184 | if (element->contains(relaxedQueryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 185 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 186 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 187 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 188 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 189 | skippable = true; |
| 190 | } |
| 191 | } else { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 192 | if (element->contains(relaxedQueryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 193 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 194 | skippable = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 195 | } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 196 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 197 | skippable = true; |
| 198 | } |
| 199 | } |
| 200 | if (!skippable) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 201 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 202 | embiggens = emsmallens = true; |
| 203 | } |
| 204 | break; |
| 205 | default: |
| 206 | SkDEBUGFAIL("Unexpected op."); |
| 207 | break; |
| 208 | } |
| 209 | if (!skippable) { |
| 210 | if (0 == result->count()) { |
| 211 | // This will be the last element. Record the stricter genID. |
| 212 | *resultGenID = element->getGenID(); |
| 213 | } |
| 214 | |
| 215 | // if it is a flip, change it to a bounds-filling rect |
| 216 | if (isFlip) { |
| 217 | SkASSERT(SkRegion::kXOR_Op == element->getOp() || |
| 218 | SkRegion::kReverseDifference_Op == element->getOp()); |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 219 | result->addToHead(SkRect::Make(clipIBounds), SkRegion::kReverseDifference_Op, |
| 220 | false); |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 221 | } else { |
| 222 | Element* newElement = result->addToHead(*element); |
| 223 | if (newElement->isAA()) { |
| 224 | ++numAAElements; |
| 225 | } |
| 226 | // Intersecting an inverse shape is the same as differencing the non-inverse shape. |
| 227 | // Replacing with an inverse shape is the same as setting initialState=kAllIn and |
| 228 | // differencing the non-inverse shape. |
| 229 | bool isReplace = SkRegion::kReplace_Op == newElement->getOp(); |
| 230 | if (newElement->isInverseFilled() && |
| 231 | (SkRegion::kIntersect_Op == newElement->getOp() || isReplace)) { |
| 232 | newElement->invertShapeFillType(); |
| 233 | newElement->setOp(SkRegion::kDifference_Op); |
| 234 | if (isReplace) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 235 | SkASSERT(InitialTriState::kAllOut == initialState); |
| 236 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 237 | } |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 243 | if ((InitialTriState::kAllOut == initialState && !embiggens) || |
| 244 | (InitialTriState::kAllIn == initialState && !emsmallens)) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 245 | result->reset(); |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 246 | numAAElements = 0; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 247 | } else { |
| 248 | Element* element = result->headIter().get(); |
| 249 | while (element) { |
| 250 | bool skippable = false; |
| 251 | switch (element->getOp()) { |
| 252 | case SkRegion::kDifference_Op: |
| 253 | // subtracting from the empty set yields the empty set. |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 254 | skippable = InitialTriState::kAllOut == initialState; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 255 | break; |
| 256 | case SkRegion::kIntersect_Op: |
| 257 | // intersecting with the empty set yields the empty set |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 258 | if (InitialTriState::kAllOut == initialState) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 259 | skippable = true; |
| 260 | } else { |
| 261 | // We can clear to zero and then simply draw the clip element. |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 262 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 263 | element->setOp(SkRegion::kReplace_Op); |
| 264 | } |
| 265 | break; |
| 266 | case SkRegion::kUnion_Op: |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 267 | if (InitialTriState::kAllIn == initialState) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 268 | // unioning the infinite plane with anything is a no-op. |
| 269 | skippable = true; |
| 270 | } else { |
| 271 | // unioning the empty set with a shape is the shape. |
| 272 | element->setOp(SkRegion::kReplace_Op); |
| 273 | } |
| 274 | break; |
| 275 | case SkRegion::kXOR_Op: |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 276 | if (InitialTriState::kAllOut == initialState) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 277 | // xor could be changed to diff in the kAllIn case, not sure it's a win. |
| 278 | element->setOp(SkRegion::kReplace_Op); |
| 279 | } |
| 280 | break; |
| 281 | case SkRegion::kReverseDifference_Op: |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 282 | if (InitialTriState::kAllIn == initialState) { |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 283 | // subtracting the whole plane will yield the empty set. |
| 284 | skippable = true; |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 285 | initialState = InitialTriState::kAllOut; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 286 | } else { |
| 287 | // this picks up flips inserted in the backwards pass. |
| 288 | skippable = element->isInverseFilled() ? |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 289 | GrClip::IsOutsideClip(element->getBounds(), queryBounds) : |
| 290 | element->contains(relaxedQueryBounds); |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 291 | if (skippable) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 292 | initialState = InitialTriState::kAllIn; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 293 | } else { |
| 294 | element->setOp(SkRegion::kReplace_Op); |
| 295 | } |
| 296 | } |
| 297 | break; |
| 298 | case SkRegion::kReplace_Op: |
| 299 | skippable = false; // we would have skipped it in the backwards walk if we |
| 300 | // could've. |
| 301 | break; |
| 302 | default: |
| 303 | SkDEBUGFAIL("Unexpected op."); |
| 304 | break; |
| 305 | } |
| 306 | if (!skippable) { |
| 307 | break; |
| 308 | } else { |
| 309 | if (element->isAA()) { |
| 310 | --numAAElements; |
| 311 | } |
| 312 | result->popHead(); |
| 313 | element = result->headIter().get(); |
| 314 | } |
| 315 | } |
| 316 | } |
bsalomon | 00ee2a8 | 2016-07-08 03:28:34 -0700 | [diff] [blame] | 317 | *requiresAA = numAAElements > 0; |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 318 | |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 319 | SkASSERT(InitialTriState::kUnknown != initialState); |
| 320 | return static_cast<GrReducedClip::InitialState>(initialState); |
tfarina | bf54e49 | 2014-10-23 17:47:18 -0700 | [diff] [blame] | 321 | } |
bsalomon@google.com | 34cd70a | 2012-12-06 14:23:20 +0000 | [diff] [blame] | 322 | |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 323 | /* |
bsalomon@google.com | c6b3e48 | 2012-12-07 20:43:52 +0000 | [diff] [blame] | 324 | There are plenty of optimizations that could be added here. Maybe flips could be folded into |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 325 | earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps |
| 326 | for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations |
| 327 | based on later intersect operations, and perhaps remove intersect-rects. We could optionally |
| 328 | take a rect in case the caller knows a bound on what is to be drawn through this clip. |
| 329 | */ |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 330 | GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds) { |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 331 | SkASSERT(!queryBounds.isEmpty()); |
csmartdalton | d211e78 | 2016-08-15 11:17:19 -0700 | [diff] [blame] | 332 | fHasIBounds = false; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 333 | |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 334 | if (stack.isWideOpen()) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 335 | fInitialState = InitialState::kAllIn; |
| 336 | return; |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | SkClipStack::BoundsType stackBoundsType; |
| 340 | SkRect stackBounds; |
| 341 | bool iior; |
| 342 | stack.getBounds(&stackBounds, &stackBoundsType, &iior); |
| 343 | |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 344 | if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)) { |
| 345 | bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType; |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 346 | fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut; |
| 347 | return; |
bsalomon@google.com | 170bd79 | 2012-12-05 22:26:11 +0000 | [diff] [blame] | 348 | } |
| 349 | |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 350 | if (iior) { |
| 351 | // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds. |
| 352 | // This should only be true if aa/non-aa status matches among all elements. |
| 353 | SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); |
| 354 | SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); |
| 355 | if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) { |
| 356 | // The clip is a non-aa rect. This is the one spot where we can actually implement the |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 357 | // clip (using fIBounds) rather than just telling the caller what it should be. |
| 358 | stackBounds.round(&fIBounds); |
csmartdalton | d211e78 | 2016-08-15 11:17:19 -0700 | [diff] [blame] | 359 | fHasIBounds = true; |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 360 | fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn; |
| 361 | return; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 362 | } |
| 363 | if (GrClip::IsInsideClip(stackBounds, queryBounds)) { |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 364 | fInitialState = InitialState::kAllIn; |
| 365 | return; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 366 | } |
| 367 | |
csmartdalton | d211e78 | 2016-08-15 11:17:19 -0700 | [diff] [blame] | 368 | SkRect tightBounds; |
| 369 | SkAssertResult(tightBounds.intersect(stackBounds, queryBounds)); |
| 370 | fIBounds = GrClip::GetPixelIBounds(tightBounds); |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 371 | SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above. |
csmartdalton | d211e78 | 2016-08-15 11:17:19 -0700 | [diff] [blame] | 372 | fHasIBounds = true; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 373 | |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 374 | // Implement the clip with an AA rect element. |
| 375 | fElements.addToHead(stackBounds, SkRegion::kReplace_Op, true/*doAA*/); |
csmartdalton | 8d3f92a | 2016-08-17 09:39:38 -0700 | [diff] [blame] | 376 | fElementsGenID = stack.getTopmostGenID(); |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 377 | fRequiresAA = true; |
| 378 | |
| 379 | fInitialState = InitialState::kAllOut; |
| 380 | return; |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 381 | } |
| 382 | |
| 383 | SkRect tighterQuery = queryBounds; |
| 384 | if (SkClipStack::kNormal_BoundsType == stackBoundsType) { |
| 385 | // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This new |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 386 | // clip will be enforced by the scissor through fIBounds.) |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 387 | SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds))); |
csmartdalton | cbecb08 | 2016-07-22 08:59:08 -0700 | [diff] [blame] | 388 | } |
skia.committer@gmail.com | d21444a | 2012-12-07 02:01:25 +0000 | [diff] [blame] | 389 | |
csmartdalton | d211e78 | 2016-08-15 11:17:19 -0700 | [diff] [blame] | 390 | fIBounds = GrClip::GetPixelIBounds(tighterQuery); |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 391 | SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above. |
csmartdalton | d211e78 | 2016-08-15 11:17:19 -0700 | [diff] [blame] | 392 | fHasIBounds = true; |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 393 | |
bsalomon@google.com | 34cd70a | 2012-12-06 14:23:20 +0000 | [diff] [blame] | 394 | // Now that we have determined the bounds to use and filtered out the trivial cases, call the |
| 395 | // helper that actually walks the stack. |
csmartdalton | 8d3f92a | 2016-08-17 09:39:38 -0700 | [diff] [blame] | 396 | fInitialState = reduced_stack_walker(stack, tighterQuery, fIBounds, &fElements, &fElementsGenID, |
csmartdalton | 77f2fae | 2016-08-08 09:55:06 -0700 | [diff] [blame] | 397 | &fRequiresAA); |
bsalomon@google.com | 34cd70a | 2012-12-06 14:23:20 +0000 | [diff] [blame] | 398 | } |