gfx/skia/trunk/src/core/SkClipStack.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 2011 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 #include "SkClipStack.h"
michael@0 9 #include "SkPath.h"
michael@0 10 #include "SkThread.h"
michael@0 11
michael@0 12 #include <new>
michael@0 13
michael@0 14
michael@0 15 // 0-2 are reserved for invalid, empty & wide-open
michael@0 16 static const int32_t kFirstUnreservedGenID = 3;
michael@0 17 int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
michael@0 18
michael@0 19 SkClipStack::Element::Element(const Element& that) {
michael@0 20 switch (that.getType()) {
michael@0 21 case kEmpty_Type:
michael@0 22 fPath.reset();
michael@0 23 break;
michael@0 24 case kRect_Type: // Rect uses rrect
michael@0 25 case kRRect_Type:
michael@0 26 fPath.reset();
michael@0 27 fRRect = that.fRRect;
michael@0 28 break;
michael@0 29 case kPath_Type:
michael@0 30 fPath.set(that.getPath());
michael@0 31 break;
michael@0 32 }
michael@0 33
michael@0 34 fSaveCount = that.fSaveCount;
michael@0 35 fOp = that.fOp;
michael@0 36 fType = that.fType;
michael@0 37 fDoAA = that.fDoAA;
michael@0 38 fFiniteBoundType = that.fFiniteBoundType;
michael@0 39 fFiniteBound = that.fFiniteBound;
michael@0 40 fIsIntersectionOfRects = that.fIsIntersectionOfRects;
michael@0 41 fGenID = that.fGenID;
michael@0 42 }
michael@0 43
michael@0 44 bool SkClipStack::Element::operator== (const Element& element) const {
michael@0 45 if (this == &element) {
michael@0 46 return true;
michael@0 47 }
michael@0 48 if (fOp != element.fOp ||
michael@0 49 fType != element.fType ||
michael@0 50 fDoAA != element.fDoAA ||
michael@0 51 fSaveCount != element.fSaveCount) {
michael@0 52 return false;
michael@0 53 }
michael@0 54 switch (fType) {
michael@0 55 case kPath_Type:
michael@0 56 return this->getPath() == element.getPath();
michael@0 57 case kRRect_Type:
michael@0 58 return fRRect == element.fRRect;
michael@0 59 case kRect_Type:
michael@0 60 return this->getRect() == element.getRect();
michael@0 61 case kEmpty_Type:
michael@0 62 return true;
michael@0 63 default:
michael@0 64 SkDEBUGFAIL("Unexpected type.");
michael@0 65 return false;
michael@0 66 }
michael@0 67 }
michael@0 68
michael@0 69 void SkClipStack::Element::invertShapeFillType() {
michael@0 70 switch (fType) {
michael@0 71 case kRect_Type:
michael@0 72 fPath.init();
michael@0 73 fPath.get()->addRect(this->getRect());
michael@0 74 fPath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
michael@0 75 fType = kPath_Type;
michael@0 76 break;
michael@0 77 case kRRect_Type:
michael@0 78 fPath.init();
michael@0 79 fPath.get()->addRRect(fRRect);
michael@0 80 fPath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
michael@0 81 fType = kPath_Type;
michael@0 82 break;
michael@0 83 case kPath_Type:
michael@0 84 fPath.get()->toggleInverseFillType();
michael@0 85 break;
michael@0 86 case kEmpty_Type:
michael@0 87 // Should this set to an empty, inverse filled path?
michael@0 88 break;
michael@0 89 }
michael@0 90 }
michael@0 91
michael@0 92 void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkRegion::Op op,
michael@0 93 bool doAA) {
michael@0 94 if (!path.isInverseFillType()) {
michael@0 95 if (SkPath::kNone_PathAsRect != path.asRect()) {
michael@0 96 this->initRect(saveCount, path.getBounds(), op, doAA);
michael@0 97 return;
michael@0 98 }
michael@0 99 SkRect ovalRect;
michael@0 100 if (path.isOval(&ovalRect)) {
michael@0 101 SkRRect rrect;
michael@0 102 rrect.setOval(ovalRect);
michael@0 103 this->initRRect(saveCount, rrect, op, doAA);
michael@0 104 return;
michael@0 105 }
michael@0 106 }
michael@0 107 fPath.set(path);
michael@0 108 fType = kPath_Type;
michael@0 109 this->initCommon(saveCount, op, doAA);
michael@0 110 }
michael@0 111
michael@0 112 void SkClipStack::Element::asPath(SkPath* path) const {
michael@0 113 switch (fType) {
michael@0 114 case kEmpty_Type:
michael@0 115 path->reset();
michael@0 116 break;
michael@0 117 case kRect_Type:
michael@0 118 path->reset();
michael@0 119 path->addRect(this->getRect());
michael@0 120 break;
michael@0 121 case kRRect_Type:
michael@0 122 path->reset();
michael@0 123 path->addRRect(fRRect);
michael@0 124 break;
michael@0 125 case kPath_Type:
michael@0 126 *path = *fPath.get();
michael@0 127 break;
michael@0 128 }
michael@0 129 }
michael@0 130
michael@0 131 void SkClipStack::Element::setEmpty() {
michael@0 132 fType = kEmpty_Type;
michael@0 133 fFiniteBound.setEmpty();
michael@0 134 fFiniteBoundType = kNormal_BoundsType;
michael@0 135 fIsIntersectionOfRects = false;
michael@0 136 fRRect.setEmpty();
michael@0 137 fPath.reset();
michael@0 138 fGenID = kEmptyGenID;
michael@0 139 SkDEBUGCODE(this->checkEmpty();)
michael@0 140 }
michael@0 141
michael@0 142 void SkClipStack::Element::checkEmpty() const {
michael@0 143 SkASSERT(fFiniteBound.isEmpty());
michael@0 144 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
michael@0 145 SkASSERT(!fIsIntersectionOfRects);
michael@0 146 SkASSERT(kEmptyGenID == fGenID);
michael@0 147 SkASSERT(!fPath.isValid());
michael@0 148 }
michael@0 149
michael@0 150 bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
michael@0 151 if (kEmpty_Type == fType &&
michael@0 152 (SkRegion::kDifference_Op == op || SkRegion::kIntersect_Op == op)) {
michael@0 153 return true;
michael@0 154 }
michael@0 155 // Only clips within the same save/restore frame (as captured by
michael@0 156 // the save count) can be merged
michael@0 157 return fSaveCount == saveCount &&
michael@0 158 SkRegion::kIntersect_Op == op &&
michael@0 159 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
michael@0 160 }
michael@0 161
michael@0 162 bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
michael@0 163 SkASSERT(kRect_Type == fType);
michael@0 164
michael@0 165 if (fDoAA == newAA) {
michael@0 166 // if the AA setting is the same there is no issue
michael@0 167 return true;
michael@0 168 }
michael@0 169
michael@0 170 if (!SkRect::Intersects(this->getRect(), newR)) {
michael@0 171 // The calling code will correctly set the result to the empty clip
michael@0 172 return true;
michael@0 173 }
michael@0 174
michael@0 175 if (this->getRect().contains(newR)) {
michael@0 176 // if the new rect carves out a portion of the old one there is no
michael@0 177 // issue
michael@0 178 return true;
michael@0 179 }
michael@0 180
michael@0 181 // So either the two overlap in some complex manner or newR contains oldR.
michael@0 182 // In the first, case the edges will require different AA. In the second,
michael@0 183 // the AA setting that would be carried forward is incorrect (e.g., oldR
michael@0 184 // is AA while newR is BW but since newR contains oldR, oldR will be
michael@0 185 // drawn BW) since the new AA setting will predominate.
michael@0 186 return false;
michael@0 187 }
michael@0 188
michael@0 189 // a mirror of combineBoundsRevDiff
michael@0 190 void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
michael@0 191 switch (combination) {
michael@0 192 case kInvPrev_InvCur_FillCombo:
michael@0 193 // In this case the only pixels that can remain set
michael@0 194 // are inside the current clip rect since the extensions
michael@0 195 // to infinity of both clips cancel out and whatever
michael@0 196 // is outside of the current clip is removed
michael@0 197 fFiniteBoundType = kNormal_BoundsType;
michael@0 198 break;
michael@0 199 case kInvPrev_Cur_FillCombo:
michael@0 200 // In this case the current op is finite so the only pixels
michael@0 201 // that aren't set are whatever isn't set in the previous
michael@0 202 // clip and whatever this clip carves out
michael@0 203 fFiniteBound.join(prevFinite);
michael@0 204 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 205 break;
michael@0 206 case kPrev_InvCur_FillCombo:
michael@0 207 // In this case everything outside of this clip's bound
michael@0 208 // is erased, so the only pixels that can remain set
michael@0 209 // occur w/in the intersection of the two finite bounds
michael@0 210 if (!fFiniteBound.intersect(prevFinite)) {
michael@0 211 this->setEmpty();
michael@0 212 } else {
michael@0 213 fFiniteBoundType = kNormal_BoundsType;
michael@0 214 }
michael@0 215 break;
michael@0 216 case kPrev_Cur_FillCombo:
michael@0 217 // The most conservative result bound is that of the
michael@0 218 // prior clip. This could be wildly incorrect if the
michael@0 219 // second clip either exactly matches the first clip
michael@0 220 // (which should yield the empty set) or reduces the
michael@0 221 // size of the prior bound (e.g., if the second clip
michael@0 222 // exactly matched the bottom half of the prior clip).
michael@0 223 // We ignore these two possibilities.
michael@0 224 fFiniteBound = prevFinite;
michael@0 225 break;
michael@0 226 default:
michael@0 227 SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
michael@0 228 break;
michael@0 229 }
michael@0 230 }
michael@0 231
michael@0 232 void SkClipStack::Element::combineBoundsXOR(int combination, const SkRect& prevFinite) {
michael@0 233
michael@0 234 switch (combination) {
michael@0 235 case kInvPrev_Cur_FillCombo: // fall through
michael@0 236 case kPrev_InvCur_FillCombo:
michael@0 237 // With only one of the clips inverted the result will always
michael@0 238 // extend to infinity. The only pixels that may be un-writeable
michael@0 239 // lie within the union of the two finite bounds
michael@0 240 fFiniteBound.join(prevFinite);
michael@0 241 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 242 break;
michael@0 243 case kInvPrev_InvCur_FillCombo:
michael@0 244 // The only pixels that can survive are within the
michael@0 245 // union of the two bounding boxes since the extensions
michael@0 246 // to infinity of both clips cancel out
michael@0 247 // fall through!
michael@0 248 case kPrev_Cur_FillCombo:
michael@0 249 // The most conservative bound for xor is the
michael@0 250 // union of the two bounds. If the two clips exactly overlapped
michael@0 251 // the xor could yield the empty set. Similarly the xor
michael@0 252 // could reduce the size of the original clip's bound (e.g.,
michael@0 253 // if the second clip exactly matched the bottom half of the
michael@0 254 // first clip). We ignore these two cases.
michael@0 255 fFiniteBound.join(prevFinite);
michael@0 256 fFiniteBoundType = kNormal_BoundsType;
michael@0 257 break;
michael@0 258 default:
michael@0 259 SkDEBUGFAIL("SkClipStack::Element::combineBoundsXOR Invalid fill combination");
michael@0 260 break;
michael@0 261 }
michael@0 262 }
michael@0 263
michael@0 264 // a mirror of combineBoundsIntersection
michael@0 265 void SkClipStack::Element::combineBoundsUnion(int combination, const SkRect& prevFinite) {
michael@0 266
michael@0 267 switch (combination) {
michael@0 268 case kInvPrev_InvCur_FillCombo:
michael@0 269 if (!fFiniteBound.intersect(prevFinite)) {
michael@0 270 fFiniteBound.setEmpty();
michael@0 271 fGenID = kWideOpenGenID;
michael@0 272 }
michael@0 273 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 274 break;
michael@0 275 case kInvPrev_Cur_FillCombo:
michael@0 276 // The only pixels that won't be drawable are inside
michael@0 277 // the prior clip's finite bound
michael@0 278 fFiniteBound = prevFinite;
michael@0 279 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 280 break;
michael@0 281 case kPrev_InvCur_FillCombo:
michael@0 282 // The only pixels that won't be drawable are inside
michael@0 283 // this clip's finite bound
michael@0 284 break;
michael@0 285 case kPrev_Cur_FillCombo:
michael@0 286 fFiniteBound.join(prevFinite);
michael@0 287 break;
michael@0 288 default:
michael@0 289 SkDEBUGFAIL("SkClipStack::Element::combineBoundsUnion Invalid fill combination");
michael@0 290 break;
michael@0 291 }
michael@0 292 }
michael@0 293
michael@0 294 // a mirror of combineBoundsUnion
michael@0 295 void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
michael@0 296
michael@0 297 switch (combination) {
michael@0 298 case kInvPrev_InvCur_FillCombo:
michael@0 299 // The only pixels that aren't writable in this case
michael@0 300 // occur in the union of the two finite bounds
michael@0 301 fFiniteBound.join(prevFinite);
michael@0 302 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 303 break;
michael@0 304 case kInvPrev_Cur_FillCombo:
michael@0 305 // In this case the only pixels that will remain writeable
michael@0 306 // are within the current clip
michael@0 307 break;
michael@0 308 case kPrev_InvCur_FillCombo:
michael@0 309 // In this case the only pixels that will remain writeable
michael@0 310 // are with the previous clip
michael@0 311 fFiniteBound = prevFinite;
michael@0 312 fFiniteBoundType = kNormal_BoundsType;
michael@0 313 break;
michael@0 314 case kPrev_Cur_FillCombo:
michael@0 315 if (!fFiniteBound.intersect(prevFinite)) {
michael@0 316 this->setEmpty();
michael@0 317 }
michael@0 318 break;
michael@0 319 default:
michael@0 320 SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
michael@0 321 break;
michael@0 322 }
michael@0 323 }
michael@0 324
michael@0 325 // a mirror of combineBoundsDiff
michael@0 326 void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& prevFinite) {
michael@0 327
michael@0 328 switch (combination) {
michael@0 329 case kInvPrev_InvCur_FillCombo:
michael@0 330 // The only pixels that can survive are in the
michael@0 331 // previous bound since the extensions to infinity in
michael@0 332 // both clips cancel out
michael@0 333 fFiniteBound = prevFinite;
michael@0 334 fFiniteBoundType = kNormal_BoundsType;
michael@0 335 break;
michael@0 336 case kInvPrev_Cur_FillCombo:
michael@0 337 if (!fFiniteBound.intersect(prevFinite)) {
michael@0 338 this->setEmpty();
michael@0 339 } else {
michael@0 340 fFiniteBoundType = kNormal_BoundsType;
michael@0 341 }
michael@0 342 break;
michael@0 343 case kPrev_InvCur_FillCombo:
michael@0 344 fFiniteBound.join(prevFinite);
michael@0 345 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 346 break;
michael@0 347 case kPrev_Cur_FillCombo:
michael@0 348 // Fall through - as with the kDifference_Op case, the
michael@0 349 // most conservative result bound is the bound of the
michael@0 350 // current clip. The prior clip could reduce the size of this
michael@0 351 // bound (as in the kDifference_Op case) but we are ignoring
michael@0 352 // those cases.
michael@0 353 break;
michael@0 354 default:
michael@0 355 SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination");
michael@0 356 break;
michael@0 357 }
michael@0 358 }
michael@0 359
michael@0 360 void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
michael@0 361 // We set this first here but we may overwrite it later if we determine that the clip is
michael@0 362 // either wide-open or empty.
michael@0 363 fGenID = GetNextGenID();
michael@0 364
michael@0 365 // First, optimistically update the current Element's bound information
michael@0 366 // with the current clip's bound
michael@0 367 fIsIntersectionOfRects = false;
michael@0 368 switch (fType) {
michael@0 369 case kRect_Type:
michael@0 370 fFiniteBound = this->getRect();
michael@0 371 fFiniteBoundType = kNormal_BoundsType;
michael@0 372
michael@0 373 if (SkRegion::kReplace_Op == fOp ||
michael@0 374 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
michael@0 375 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
michael@0 376 prior->rectRectIntersectAllowed(this->getRect(), fDoAA))) {
michael@0 377 fIsIntersectionOfRects = true;
michael@0 378 }
michael@0 379 break;
michael@0 380 case kRRect_Type:
michael@0 381 fFiniteBound = fRRect.getBounds();
michael@0 382 fFiniteBoundType = kNormal_BoundsType;
michael@0 383 break;
michael@0 384 case kPath_Type:
michael@0 385 fFiniteBound = fPath.get()->getBounds();
michael@0 386
michael@0 387 if (fPath.get()->isInverseFillType()) {
michael@0 388 fFiniteBoundType = kInsideOut_BoundsType;
michael@0 389 } else {
michael@0 390 fFiniteBoundType = kNormal_BoundsType;
michael@0 391 }
michael@0 392 break;
michael@0 393 case kEmpty_Type:
michael@0 394 SkDEBUGFAIL("We shouldn't get here with an empty element.");
michael@0 395 break;
michael@0 396 }
michael@0 397
michael@0 398 if (!fDoAA) {
michael@0 399 // Here we mimic a non-anti-aliased scanline system. If there is
michael@0 400 // no anti-aliasing we can integerize the bounding box to exclude
michael@0 401 // fractional parts that won't be rendered.
michael@0 402 // Note: the left edge is handled slightly differently below. We
michael@0 403 // are a bit more generous in the rounding since we don't want to
michael@0 404 // risk missing the left pixels when fLeft is very close to .5
michael@0 405 fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f),
michael@0 406 SkScalarRoundToScalar(fFiniteBound.fTop),
michael@0 407 SkScalarRoundToScalar(fFiniteBound.fRight),
michael@0 408 SkScalarRoundToScalar(fFiniteBound.fBottom));
michael@0 409 }
michael@0 410
michael@0 411 // Now determine the previous Element's bound information taking into
michael@0 412 // account that there may be no previous clip
michael@0 413 SkRect prevFinite;
michael@0 414 SkClipStack::BoundsType prevType;
michael@0 415
michael@0 416 if (NULL == prior) {
michael@0 417 // no prior clip means the entire plane is writable
michael@0 418 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
michael@0 419 prevType = kInsideOut_BoundsType;
michael@0 420 } else {
michael@0 421 prevFinite = prior->fFiniteBound;
michael@0 422 prevType = prior->fFiniteBoundType;
michael@0 423 }
michael@0 424
michael@0 425 FillCombo combination = kPrev_Cur_FillCombo;
michael@0 426 if (kInsideOut_BoundsType == fFiniteBoundType) {
michael@0 427 combination = (FillCombo) (combination | 0x01);
michael@0 428 }
michael@0 429 if (kInsideOut_BoundsType == prevType) {
michael@0 430 combination = (FillCombo) (combination | 0x02);
michael@0 431 }
michael@0 432
michael@0 433 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
michael@0 434 kInvPrev_Cur_FillCombo == combination ||
michael@0 435 kPrev_InvCur_FillCombo == combination ||
michael@0 436 kPrev_Cur_FillCombo == combination);
michael@0 437
michael@0 438 // Now integrate with clip with the prior clips
michael@0 439 switch (fOp) {
michael@0 440 case SkRegion::kDifference_Op:
michael@0 441 this->combineBoundsDiff(combination, prevFinite);
michael@0 442 break;
michael@0 443 case SkRegion::kXOR_Op:
michael@0 444 this->combineBoundsXOR(combination, prevFinite);
michael@0 445 break;
michael@0 446 case SkRegion::kUnion_Op:
michael@0 447 this->combineBoundsUnion(combination, prevFinite);
michael@0 448 break;
michael@0 449 case SkRegion::kIntersect_Op:
michael@0 450 this->combineBoundsIntersection(combination, prevFinite);
michael@0 451 break;
michael@0 452 case SkRegion::kReverseDifference_Op:
michael@0 453 this->combineBoundsRevDiff(combination, prevFinite);
michael@0 454 break;
michael@0 455 case SkRegion::kReplace_Op:
michael@0 456 // Replace just ignores everything prior
michael@0 457 // The current clip's bound information is already filled in
michael@0 458 // so nothing to do
michael@0 459 break;
michael@0 460 default:
michael@0 461 SkDebugf("SkRegion::Op error\n");
michael@0 462 SkASSERT(0);
michael@0 463 break;
michael@0 464 }
michael@0 465 }
michael@0 466
michael@0 467 // This constant determines how many Element's are allocated together as a block in
michael@0 468 // the deque. As such it needs to balance allocating too much memory vs.
michael@0 469 // incurring allocation/deallocation thrashing. It should roughly correspond to
michael@0 470 // the deepest save/restore stack we expect to see.
michael@0 471 static const int kDefaultElementAllocCnt = 8;
michael@0 472
michael@0 473 SkClipStack::SkClipStack()
michael@0 474 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
michael@0 475 , fSaveCount(0) {
michael@0 476 }
michael@0 477
michael@0 478 SkClipStack::SkClipStack(const SkClipStack& b)
michael@0 479 : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
michael@0 480 *this = b;
michael@0 481 }
michael@0 482
michael@0 483 SkClipStack::SkClipStack(const SkRect& r)
michael@0 484 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
michael@0 485 , fSaveCount(0) {
michael@0 486 if (!r.isEmpty()) {
michael@0 487 this->clipDevRect(r, SkRegion::kReplace_Op, false);
michael@0 488 }
michael@0 489 }
michael@0 490
michael@0 491 SkClipStack::SkClipStack(const SkIRect& r)
michael@0 492 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
michael@0 493 , fSaveCount(0) {
michael@0 494 if (!r.isEmpty()) {
michael@0 495 SkRect temp;
michael@0 496 temp.set(r);
michael@0 497 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
michael@0 498 }
michael@0 499 }
michael@0 500
michael@0 501 SkClipStack::~SkClipStack() {
michael@0 502 reset();
michael@0 503 }
michael@0 504
michael@0 505 SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
michael@0 506 if (this == &b) {
michael@0 507 return *this;
michael@0 508 }
michael@0 509 reset();
michael@0 510
michael@0 511 fSaveCount = b.fSaveCount;
michael@0 512 SkDeque::F2BIter recIter(b.fDeque);
michael@0 513 for (const Element* element = (const Element*)recIter.next();
michael@0 514 element != NULL;
michael@0 515 element = (const Element*)recIter.next()) {
michael@0 516 new (fDeque.push_back()) Element(*element);
michael@0 517 }
michael@0 518
michael@0 519 return *this;
michael@0 520 }
michael@0 521
michael@0 522 bool SkClipStack::operator==(const SkClipStack& b) const {
michael@0 523 if (this->getTopmostGenID() == b.getTopmostGenID()) {
michael@0 524 return true;
michael@0 525 }
michael@0 526 if (fSaveCount != b.fSaveCount ||
michael@0 527 fDeque.count() != b.fDeque.count()) {
michael@0 528 return false;
michael@0 529 }
michael@0 530 SkDeque::F2BIter myIter(fDeque);
michael@0 531 SkDeque::F2BIter bIter(b.fDeque);
michael@0 532 const Element* myElement = (const Element*)myIter.next();
michael@0 533 const Element* bElement = (const Element*)bIter.next();
michael@0 534
michael@0 535 while (myElement != NULL && bElement != NULL) {
michael@0 536 if (*myElement != *bElement) {
michael@0 537 return false;
michael@0 538 }
michael@0 539 myElement = (const Element*)myIter.next();
michael@0 540 bElement = (const Element*)bIter.next();
michael@0 541 }
michael@0 542 return myElement == NULL && bElement == NULL;
michael@0 543 }
michael@0 544
michael@0 545 void SkClipStack::reset() {
michael@0 546 // We used a placement new for each object in fDeque, so we're responsible
michael@0 547 // for calling the destructor on each of them as well.
michael@0 548 while (!fDeque.empty()) {
michael@0 549 Element* element = (Element*)fDeque.back();
michael@0 550 element->~Element();
michael@0 551 fDeque.pop_back();
michael@0 552 }
michael@0 553
michael@0 554 fSaveCount = 0;
michael@0 555 }
michael@0 556
michael@0 557 void SkClipStack::save() {
michael@0 558 fSaveCount += 1;
michael@0 559 }
michael@0 560
michael@0 561 void SkClipStack::restore() {
michael@0 562 fSaveCount -= 1;
michael@0 563 restoreTo(fSaveCount);
michael@0 564 }
michael@0 565
michael@0 566 void SkClipStack::restoreTo(int saveCount) {
michael@0 567 while (!fDeque.empty()) {
michael@0 568 Element* element = (Element*)fDeque.back();
michael@0 569 if (element->fSaveCount <= saveCount) {
michael@0 570 break;
michael@0 571 }
michael@0 572 element->~Element();
michael@0 573 fDeque.pop_back();
michael@0 574 }
michael@0 575 }
michael@0 576
michael@0 577 void SkClipStack::getBounds(SkRect* canvFiniteBound,
michael@0 578 BoundsType* boundType,
michael@0 579 bool* isIntersectionOfRects) const {
michael@0 580 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
michael@0 581
michael@0 582 Element* element = (Element*)fDeque.back();
michael@0 583
michael@0 584 if (NULL == element) {
michael@0 585 // the clip is wide open - the infinite plane w/ no pixels un-writeable
michael@0 586 canvFiniteBound->setEmpty();
michael@0 587 *boundType = kInsideOut_BoundsType;
michael@0 588 if (NULL != isIntersectionOfRects) {
michael@0 589 *isIntersectionOfRects = false;
michael@0 590 }
michael@0 591 return;
michael@0 592 }
michael@0 593
michael@0 594 *canvFiniteBound = element->fFiniteBound;
michael@0 595 *boundType = element->fFiniteBoundType;
michael@0 596 if (NULL != isIntersectionOfRects) {
michael@0 597 *isIntersectionOfRects = element->fIsIntersectionOfRects;
michael@0 598 }
michael@0 599 }
michael@0 600
michael@0 601 bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
michael@0 602 SkASSERT(NULL != rect);
michael@0 603
michael@0 604 SkRect bounds;
michael@0 605 SkClipStack::BoundsType bt;
michael@0 606 this->getBounds(&bounds, &bt);
michael@0 607 if (bt == SkClipStack::kInsideOut_BoundsType) {
michael@0 608 if (bounds.contains(*rect)) {
michael@0 609 return false;
michael@0 610 } else {
michael@0 611 // If rect's x values are both within bound's x range we
michael@0 612 // could clip here. Same for y. But we don't bother to check.
michael@0 613 return true;
michael@0 614 }
michael@0 615 } else {
michael@0 616 return rect->intersect(bounds);
michael@0 617 }
michael@0 618 }
michael@0 619
michael@0 620 bool SkClipStack::quickContains(const SkRect& rect) const {
michael@0 621
michael@0 622 Iter iter(*this, Iter::kTop_IterStart);
michael@0 623 const Element* element = iter.prev();
michael@0 624 while (element != NULL) {
michael@0 625 if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
michael@0 626 return false;
michael@0 627 if (element->isInverseFilled()) {
michael@0 628 // Part of 'rect' could be trimmed off by the inverse-filled clip element
michael@0 629 if (SkRect::Intersects(element->getBounds(), rect)) {
michael@0 630 return false;
michael@0 631 }
michael@0 632 } else {
michael@0 633 if (!element->contains(rect)) {
michael@0 634 return false;
michael@0 635 }
michael@0 636 }
michael@0 637 if (SkRegion::kReplace_Op == element->getOp()) {
michael@0 638 break;
michael@0 639 }
michael@0 640 element = iter.prev();
michael@0 641 }
michael@0 642 return true;
michael@0 643 }
michael@0 644
michael@0 645 void SkClipStack::pushElement(const Element& element) {
michael@0 646 // Use reverse iterator instead of back because Rect path may need previous
michael@0 647 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
michael@0 648 Element* prior = (Element*) iter.prev();
michael@0 649
michael@0 650 if (NULL != prior) {
michael@0 651 if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
michael@0 652 switch (prior->fType) {
michael@0 653 case Element::kEmpty_Type:
michael@0 654 SkDEBUGCODE(prior->checkEmpty();)
michael@0 655 return;
michael@0 656 case Element::kRect_Type:
michael@0 657 if (Element::kRect_Type == element.getType()) {
michael@0 658 if (prior->rectRectIntersectAllowed(element.getRect(), element.isAA())) {
michael@0 659 SkRect isectRect;
michael@0 660 if (!isectRect.intersect(prior->getRect(), element.getRect())) {
michael@0 661 prior->setEmpty();
michael@0 662 return;
michael@0 663 }
michael@0 664
michael@0 665 prior->fRRect.setRect(isectRect);
michael@0 666 prior->fDoAA = element.isAA();
michael@0 667 Element* priorPrior = (Element*) iter.prev();
michael@0 668 prior->updateBoundAndGenID(priorPrior);
michael@0 669 return;
michael@0 670 }
michael@0 671 break;
michael@0 672 }
michael@0 673 // fallthrough
michael@0 674 default:
michael@0 675 if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
michael@0 676 prior->setEmpty();
michael@0 677 return;
michael@0 678 }
michael@0 679 break;
michael@0 680 }
michael@0 681 } else if (SkRegion::kReplace_Op == element.getOp()) {
michael@0 682 this->restoreTo(fSaveCount - 1);
michael@0 683 prior = (Element*) fDeque.back();
michael@0 684 }
michael@0 685 }
michael@0 686 Element* newElement = SkNEW_PLACEMENT_ARGS(fDeque.push_back(), Element, (element));
michael@0 687 newElement->updateBoundAndGenID(prior);
michael@0 688 }
michael@0 689
michael@0 690 void SkClipStack::clipDevRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
michael@0 691 Element element(fSaveCount, rrect, op, doAA);
michael@0 692 this->pushElement(element);
michael@0 693 }
michael@0 694
michael@0 695 void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
michael@0 696 Element element(fSaveCount, rect, op, doAA);
michael@0 697 this->pushElement(element);
michael@0 698 }
michael@0 699
michael@0 700 void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
michael@0 701 Element element(fSaveCount, path, op, doAA);
michael@0 702 this->pushElement(element);
michael@0 703 }
michael@0 704
michael@0 705 void SkClipStack::clipEmpty() {
michael@0 706 Element* element = (Element*) fDeque.back();
michael@0 707
michael@0 708 if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
michael@0 709 element->setEmpty();
michael@0 710 }
michael@0 711 new (fDeque.push_back()) Element(fSaveCount);
michael@0 712
michael@0 713 ((Element*)fDeque.back())->fGenID = kEmptyGenID;
michael@0 714 }
michael@0 715
michael@0 716 bool SkClipStack::isWideOpen() const {
michael@0 717 return this->getTopmostGenID() == kWideOpenGenID;
michael@0 718 }
michael@0 719
michael@0 720 ///////////////////////////////////////////////////////////////////////////////
michael@0 721
michael@0 722 SkClipStack::Iter::Iter() : fStack(NULL) {
michael@0 723 }
michael@0 724
michael@0 725 SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
michael@0 726 : fStack(&stack) {
michael@0 727 this->reset(stack, startLoc);
michael@0 728 }
michael@0 729
michael@0 730 const SkClipStack::Element* SkClipStack::Iter::next() {
michael@0 731 return (const SkClipStack::Element*)fIter.next();
michael@0 732 }
michael@0 733
michael@0 734 const SkClipStack::Element* SkClipStack::Iter::prev() {
michael@0 735 return (const SkClipStack::Element*)fIter.prev();
michael@0 736 }
michael@0 737
michael@0 738 const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
michael@0 739
michael@0 740 if (NULL == fStack) {
michael@0 741 return NULL;
michael@0 742 }
michael@0 743
michael@0 744 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
michael@0 745
michael@0 746 const SkClipStack::Element* element = NULL;
michael@0 747
michael@0 748 for (element = (const SkClipStack::Element*) fIter.prev();
michael@0 749 NULL != element;
michael@0 750 element = (const SkClipStack::Element*) fIter.prev()) {
michael@0 751
michael@0 752 if (op == element->fOp) {
michael@0 753 // The Deque's iterator is actually one pace ahead of the
michael@0 754 // returned value. So while "element" is the element we want to
michael@0 755 // return, the iterator is actually pointing at (and will
michael@0 756 // return on the next "next" or "prev" call) the element
michael@0 757 // in front of it in the deque. Bump the iterator forward a
michael@0 758 // step so we get the expected result.
michael@0 759 if (NULL == fIter.next()) {
michael@0 760 // The reverse iterator has run off the front of the deque
michael@0 761 // (i.e., the "op" clip is the first clip) and can't
michael@0 762 // recover. Reset the iterator to start at the front.
michael@0 763 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
michael@0 764 }
michael@0 765 break;
michael@0 766 }
michael@0 767 }
michael@0 768
michael@0 769 if (NULL == element) {
michael@0 770 // There were no "op" clips
michael@0 771 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
michael@0 772 }
michael@0 773
michael@0 774 return this->next();
michael@0 775 }
michael@0 776
michael@0 777 void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
michael@0 778 fStack = &stack;
michael@0 779 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
michael@0 780 }
michael@0 781
michael@0 782 // helper method
michael@0 783 void SkClipStack::getConservativeBounds(int offsetX,
michael@0 784 int offsetY,
michael@0 785 int maxWidth,
michael@0 786 int maxHeight,
michael@0 787 SkRect* devBounds,
michael@0 788 bool* isIntersectionOfRects) const {
michael@0 789 SkASSERT(NULL != devBounds);
michael@0 790
michael@0 791 devBounds->setLTRB(0, 0,
michael@0 792 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
michael@0 793
michael@0 794 SkRect temp;
michael@0 795 SkClipStack::BoundsType boundType;
michael@0 796
michael@0 797 // temp starts off in canvas space here
michael@0 798 this->getBounds(&temp, &boundType, isIntersectionOfRects);
michael@0 799 if (SkClipStack::kInsideOut_BoundsType == boundType) {
michael@0 800 return;
michael@0 801 }
michael@0 802
michael@0 803 // but is converted to device space here
michael@0 804 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
michael@0 805
michael@0 806 if (!devBounds->intersect(temp)) {
michael@0 807 devBounds->setEmpty();
michael@0 808 }
michael@0 809 }
michael@0 810
michael@0 811 int32_t SkClipStack::GetNextGenID() {
michael@0 812 // TODO: handle overflow.
michael@0 813 return sk_atomic_inc(&gGenID);
michael@0 814 }
michael@0 815
michael@0 816 int32_t SkClipStack::getTopmostGenID() const {
michael@0 817 if (fDeque.empty()) {
michael@0 818 return kWideOpenGenID;
michael@0 819 }
michael@0 820
michael@0 821 const Element* back = static_cast<const Element*>(fDeque.back());
michael@0 822 if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty()) {
michael@0 823 return kWideOpenGenID;
michael@0 824 }
michael@0 825
michael@0 826 return back->getGenID();
michael@0 827 }

mercurial