gfx/skia/trunk/src/gpu/GrReducedClip.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial