1/* 2 * Copyright 2016 Google Inc. 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 10#include "GrAppliedClip.h" 11#include "GrClip.h" 12#include "GrColor.h" 13#include "GrContextPriv.h" 14#include "GrRenderTargetContext.h" 15#include "GrRenderTargetContextPriv.h" 16#include "GrDrawingManager.h" 17#include "GrFixedClip.h" 18#include "GrPathRenderer.h" 19#include "GrStencilSettings.h" 20#include "GrStyle.h" 21#include "GrUserStencilSettings.h" 22#include "SkClipOpPriv.h" 23 24typedef SkClipStack::Element Element; 25 26/** 27 * There are plenty of optimizations that could be added here. Maybe flips could be folded into 28 * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps 29 * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations 30 * based on later intersect operations, and perhaps remove intersect-rects. We could optionally 31 * take a rect in case the caller knows a bound on what is to be drawn through this clip. 32 */ 33GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds, 34 int maxWindowRectangles) { 35 SkASSERT(!queryBounds.isEmpty()); 36 fHasIBounds = false; 37 38 if (stack.isWideOpen()) { 39 fInitialState = InitialState::kAllIn; 40 return; 41 } 42 43 SkClipStack::BoundsType stackBoundsType; 44 SkRect stackBounds; 45 bool iior; 46 stack.getBounds(&stackBounds, &stackBoundsType, &iior); 47 48 if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)) { 49 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType; 50 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut; 51 return; 52 } 53 54 if (iior) { 55 // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds. 56 // This should only be true if aa/non-aa status matches among all elements. 57 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType); 58 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 59 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) { 60 // The clip is a non-aa rect. This is the one spot where we can actually implement the 61 // clip (using fIBounds) rather than just telling the caller what it should be. 62 stackBounds.round(&fIBounds); 63 fHasIBounds = true; 64 fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn; 65 return; 66 } 67 if (GrClip::IsInsideClip(stackBounds, queryBounds)) { 68 fInitialState = InitialState::kAllIn; 69 return; 70 } 71 72 SkRect tightBounds; 73 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds)); 74 fIBounds = GrClip::GetPixelIBounds(tightBounds); 75 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above. 76 fHasIBounds = true; 77 78 // Implement the clip with an AA rect element. 79 fElements.addToHead(stackBounds, kReplace_SkClipOp, true/*doAA*/); 80 fElementsGenID = stack.getTopmostGenID(); 81 fRequiresAA = true; 82 83 fInitialState = InitialState::kAllOut; 84 return; 85 } 86 87 SkRect tighterQuery = queryBounds; 88 if (SkClipStack::kNormal_BoundsType == stackBoundsType) { 89 // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This new 90 // clip will be enforced by the scissor through fIBounds.) 91 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds))); 92 } 93 94 fIBounds = GrClip::GetPixelIBounds(tighterQuery); 95 SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above. 96 fHasIBounds = true; 97 98 // Now that we have determined the bounds to use and filtered out the trivial cases, call the 99 // helper that actually walks the stack. 100 this->walkStack(stack, tighterQuery, maxWindowRectangles); 101 102 if (fWindowRects.count() < maxWindowRectangles) { 103 this->addInteriorWindowRectangles(maxWindowRectangles); 104 } 105} 106 107void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds, 108 int maxWindowRectangles) { 109 // walk backwards until we get to: 110 // a) the beginning 111 // b) an operation that is known to make the bounds all inside/outside 112 // c) a replace operation 113 114 enum class InitialTriState { 115 kUnknown = -1, 116 kAllIn = (int)GrReducedClip::InitialState::kAllIn, 117 kAllOut = (int)GrReducedClip::InitialState::kAllOut 118 } initialTriState = InitialTriState::kUnknown; 119 120 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip. 121 // TODO: track these per saved clip so that we can consider them on the forward pass. 122 bool embiggens = false; 123 bool emsmallens = false; 124 125 // We use a slightly relaxed set of query bounds for element containment tests. This is to 126 // account for floating point rounding error that may have occurred during coord transforms. 127 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance, 128 GrClip::kBoundsTolerance); 129 130 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 131 int numAAElements = 0; 132 while (InitialTriState::kUnknown == initialTriState) { 133 const Element* element = iter.prev(); 134 if (nullptr == element) { 135 initialTriState = InitialTriState::kAllIn; 136 break; 137 } 138 if (SkClipStack::kEmptyGenID == element->getGenID()) { 139 initialTriState = InitialTriState::kAllOut; 140 break; 141 } 142 if (SkClipStack::kWideOpenGenID == element->getGenID()) { 143 initialTriState = InitialTriState::kAllIn; 144 break; 145 } 146 147 bool skippable = false; 148 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds 149 150 switch (element->getOp()) { 151 case kDifference_SkClipOp: 152 // check if the shape subtracted either contains the entire bounds (and makes 153 // the clip empty) or is outside the bounds and therefore can be skipped. 154 if (element->isInverseFilled()) { 155 if (element->contains(relaxedQueryBounds)) { 156 skippable = true; 157 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 158 initialTriState = InitialTriState::kAllOut; 159 skippable = true; 160 } 161 } else { 162 if (element->contains(relaxedQueryBounds)) { 163 initialTriState = InitialTriState::kAllOut; 164 skippable = true; 165 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 166 skippable = true; 167 } else if (fWindowRects.count() < maxWindowRectangles && !embiggens && 168 !element->isAA() && Element::kRect_Type == element->getType()) { 169 this->addWindowRectangle(element->getRect(), false); 170 skippable = true; 171 } 172 } 173 if (!skippable) { 174 emsmallens = true; 175 } 176 break; 177 case kIntersect_SkClipOp: 178 // check if the shape intersected contains the entire bounds and therefore can 179 // be skipped or it is outside the entire bounds and therefore makes the clip 180 // empty. 181 if (element->isInverseFilled()) { 182 if (element->contains(relaxedQueryBounds)) { 183 initialTriState = InitialTriState::kAllOut; 184 skippable = true; 185 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 186 skippable = true; 187 } 188 } else { 189 if (element->contains(relaxedQueryBounds)) { 190 skippable = true; 191 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 192 initialTriState = InitialTriState::kAllOut; 193 skippable = true; 194 } else if (!embiggens && !element->isAA() && 195 Element::kRect_Type == element->getType()) { 196 // fIBounds and queryBounds have already acccounted for this element via 197 // clip stack bounds; here we just apply the non-aa rounding effect. 198 SkIRect nonaaRect; 199 element->getRect().round(&nonaaRect); 200 if (!this->intersectIBounds(nonaaRect)) { 201 return; 202 } 203 skippable = true; 204 } 205 } 206 if (!skippable) { 207 emsmallens = true; 208 } 209 break; 210 case kUnion_SkClipOp: 211 // If the union-ed shape contains the entire bounds then after this element 212 // the bounds is entirely inside the clip. If the union-ed shape is outside the 213 // bounds then this op can be skipped. 214 if (element->isInverseFilled()) { 215 if (element->contains(relaxedQueryBounds)) { 216 skippable = true; 217 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 218 initialTriState = InitialTriState::kAllIn; 219 skippable = true; 220 } 221 } else { 222 if (element->contains(relaxedQueryBounds)) { 223 initialTriState = InitialTriState::kAllIn; 224 skippable = true; 225 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 226 skippable = true; 227 } 228 } 229 if (!skippable) { 230 embiggens = true; 231 } 232 break; 233 case kXOR_SkClipOp: 234 // If the bounds is entirely inside the shape being xor-ed then the effect is 235 // to flip the inside/outside state of every point in the bounds. We may be 236 // able to take advantage of this in the forward pass. If the xor-ed shape 237 // doesn't intersect the bounds then it can be skipped. 238 if (element->isInverseFilled()) { 239 if (element->contains(relaxedQueryBounds)) { 240 skippable = true; 241 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 242 isFlip = true; 243 } 244 } else { 245 if (element->contains(relaxedQueryBounds)) { 246 isFlip = true; 247 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 248 skippable = true; 249 } 250 } 251 if (!skippable) { 252 emsmallens = embiggens = true; 253 } 254 break; 255 case kReverseDifference_SkClipOp: 256 // When the bounds is entirely within the rev-diff shape then this behaves like xor 257 // and reverses every point inside the bounds. If the shape is completely outside 258 // the bounds then we know after this element is applied that the bounds will be 259 // all outside the current clip.B 260 if (element->isInverseFilled()) { 261 if (element->contains(relaxedQueryBounds)) { 262 initialTriState = InitialTriState::kAllOut; 263 skippable = true; 264 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 265 isFlip = true; 266 } 267 } else { 268 if (element->contains(relaxedQueryBounds)) { 269 isFlip = true; 270 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 271 initialTriState = InitialTriState::kAllOut; 272 skippable = true; 273 } 274 } 275 if (!skippable) { 276 emsmallens = embiggens = true; 277 } 278 break; 279 280 case kReplace_SkClipOp: 281 // Replace will always terminate our walk. We will either begin the forward walk 282 // at the replace op or detect here than the shape is either completely inside 283 // or completely outside the bounds. In this latter case it can be skipped by 284 // setting the correct value for initialTriState. 285 if (element->isInverseFilled()) { 286 if (element->contains(relaxedQueryBounds)) { 287 initialTriState = InitialTriState::kAllOut; 288 skippable = true; 289 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 290 initialTriState = InitialTriState::kAllIn; 291 skippable = true; 292 } 293 } else { 294 if (element->contains(relaxedQueryBounds)) { 295 initialTriState = InitialTriState::kAllIn; 296 skippable = true; 297 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { 298 initialTriState = InitialTriState::kAllOut; 299 skippable = true; 300 } else if (!embiggens && !element->isAA() && 301 Element::kRect_Type == element->getType()) { 302 // fIBounds and queryBounds have already acccounted for this element via 303 // clip stack bounds; here we just apply the non-aa rounding effect. 304 SkIRect nonaaRect; 305 element->getRect().round(&nonaaRect); 306 if (!this->intersectIBounds(nonaaRect)) { 307 return; 308 } 309 initialTriState = InitialTriState::kAllIn; 310 skippable = true; 311 } 312 } 313 if (!skippable) { 314 initialTriState = InitialTriState::kAllOut; 315 embiggens = emsmallens = true; 316 } 317 break; 318 default: 319 SkDEBUGFAIL("Unexpected op."); 320 break; 321 } 322 if (!skippable) { 323 if (0 == fElements.count()) { 324 // This will be the last element. Record the stricter genID. 325 fElementsGenID = element->getGenID(); 326 } 327 328 // if it is a flip, change it to a bounds-filling rect 329 if (isFlip) { 330 SkASSERT(kXOR_SkClipOp == element->getOp() || 331 kReverseDifference_SkClipOp == element->getOp()); 332 fElements.addToHead(SkRect::Make(fIBounds), kReverseDifference_SkClipOp, false); 333 } else { 334 Element* newElement = fElements.addToHead(*element); 335 if (newElement->isAA()) { 336 ++numAAElements; 337 } 338 // Intersecting an inverse shape is the same as differencing the non-inverse shape. 339 // Replacing with an inverse shape is the same as setting initialState=kAllIn and 340 // differencing the non-inverse shape. 341 bool isReplace = kReplace_SkClipOp == newElement->getOp(); 342 if (newElement->isInverseFilled() && 343 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) { 344 newElement->invertShapeFillType(); 345 newElement->setOp(kDifference_SkClipOp); 346 if (isReplace) { 347 SkASSERT(InitialTriState::kAllOut == initialTriState); 348 initialTriState = InitialTriState::kAllIn; 349 } 350 } 351 } 352 } 353 } 354 355 if ((InitialTriState::kAllOut == initialTriState && !embiggens) || 356 (InitialTriState::kAllIn == initialTriState && !emsmallens)) { 357 fElements.reset(); 358 numAAElements = 0; 359 } else { 360 Element* element = fElements.headIter().get(); 361 while (element) { 362 bool skippable = false; 363 switch (element->getOp()) { 364 case kDifference_SkClipOp: 365 // subtracting from the empty set yields the empty set. 366 skippable = InitialTriState::kAllOut == initialTriState; 367 break; 368 case kIntersect_SkClipOp: 369 // intersecting with the empty set yields the empty set 370 if (InitialTriState::kAllOut == initialTriState) { 371 skippable = true; 372 } else { 373 // We can clear to zero and then simply draw the clip element. 374 initialTriState = InitialTriState::kAllOut; 375 element->setOp(kReplace_SkClipOp); 376 } 377 break; 378 case kUnion_SkClipOp: 379 if (InitialTriState::kAllIn == initialTriState) { 380 // unioning the infinite plane with anything is a no-op. 381 skippable = true; 382 } else { 383 // unioning the empty set with a shape is the shape. 384 element->setOp(kReplace_SkClipOp); 385 } 386 break; 387 case kXOR_SkClipOp: 388 if (InitialTriState::kAllOut == initialTriState) { 389 // xor could be changed to diff in the kAllIn case, not sure it's a win. 390 element->setOp(kReplace_SkClipOp); 391 } 392 break; 393 case kReverseDifference_SkClipOp: 394 if (InitialTriState::kAllIn == initialTriState) { 395 // subtracting the whole plane will yield the empty set. 396 skippable = true; 397 initialTriState = InitialTriState::kAllOut; 398 } else { 399 // this picks up flips inserted in the backwards pass. 400 skippable = element->isInverseFilled() ? 401 GrClip::IsOutsideClip(element->getBounds(), queryBounds) : 402 element->contains(relaxedQueryBounds); 403 if (skippable) { 404 initialTriState = InitialTriState::kAllIn; 405 } else { 406 element->setOp(kReplace_SkClipOp); 407 } 408 } 409 break; 410 case kReplace_SkClipOp: 411 skippable = false; // we would have skipped it in the backwards walk if we 412 // could've. 413 break; 414 default: 415 SkDEBUGFAIL("Unexpected op."); 416 break; 417 } 418 if (!skippable) { 419 break; 420 } else { 421 if (element->isAA()) { 422 --numAAElements; 423 } 424 fElements.popHead(); 425 element = fElements.headIter().get(); 426 } 427 } 428 } 429 fRequiresAA = numAAElements > 0; 430 431 SkASSERT(InitialTriState::kUnknown != initialTriState); 432 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState); 433} 434 435static bool element_is_pure_subtract(SkClipOp op) { 436 SkASSERT(static_cast<int>(op) >= 0); 437 return static_cast<int>(op) <= static_cast<int>(kIntersect_SkClipOp); 438 439 GR_STATIC_ASSERT(0 == static_cast<int>(kDifference_SkClipOp)); 440 GR_STATIC_ASSERT(1 == static_cast<int>(kIntersect_SkClipOp)); 441} 442 443void GrReducedClip::addInteriorWindowRectangles(int maxWindowRectangles) { 444 SkASSERT(fWindowRects.count() < maxWindowRectangles); 445 // Walk backwards through the element list and add window rectangles to the interiors of 446 // "difference" elements. Quit if we encounter an element that may grow the clip. 447 ElementList::Iter iter(fElements, ElementList::Iter::kTail_IterStart); 448 for (; iter.get() && element_is_pure_subtract(iter.get()->getOp()); iter.prev()) { 449 const Element* element = iter.get(); 450 if (kDifference_SkClipOp != element->getOp()) { 451 continue; 452 } 453 454 if (Element::kRect_Type == element->getType()) { 455 SkASSERT(element->isAA()); 456 this->addWindowRectangle(element->getRect(), true); 457 if (fWindowRects.count() >= maxWindowRectangles) { 458 return; 459 } 460 continue; 461 } 462 463 if (Element::kRRect_Type == element->getType()) { 464 // For round rects we add two overlapping windows in the shape of a plus. 465 const SkRRect& clipRRect = element->getRRect(); 466 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner); 467 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner); 468 if (SkRRect::kComplex_Type == clipRRect.getType()) { 469 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner); 470 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner); 471 insetTL.fX = SkTMax(insetTL.x(), insetBL.x()); 472 insetTL.fY = SkTMax(insetTL.y(), insetTR.y()); 473 insetBR.fX = SkTMax(insetBR.x(), insetTR.x()); 474 insetBR.fY = SkTMax(insetBR.y(), insetBL.y()); 475 } 476 const SkRect& bounds = clipRRect.getBounds(); 477 if (insetTL.x() + insetBR.x() >= bounds.width() || 478 insetTL.y() + insetBR.y() >= bounds.height()) { 479 continue; // The interior "plus" is empty. 480 } 481 482 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(), 483 bounds.right(), bounds.bottom() - insetBR.y()); 484 this->addWindowRectangle(horzRect, element->isAA()); 485 if (fWindowRects.count() >= maxWindowRectangles) { 486 return; 487 } 488 489 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(), 490 bounds.right() - insetBR.x(), bounds.bottom()); 491 this->addWindowRectangle(vertRect, element->isAA()); 492 if (fWindowRects.count() >= maxWindowRectangles) { 493 return; 494 } 495 continue; 496 } 497 } 498} 499 500inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) { 501 SkIRect window; 502 if (!elementIsAA) { 503 elementInteriorRect.round(&window); 504 } else { 505 elementInteriorRect.roundIn(&window); 506 } 507 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions. 508 fWindowRects.addWindow(window); 509 } 510} 511 512inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) { 513 SkASSERT(fHasIBounds); 514 if (!fIBounds.intersect(irect)) { 515 fHasIBounds = false; 516 fWindowRects.reset(); 517 fElements.reset(); 518 fRequiresAA = false; 519 fInitialState = InitialState::kAllOut; 520 return false; 521 } 522 return true; 523} 524 525//////////////////////////////////////////////////////////////////////////////// 526// Create a 8-bit clip mask in alpha 527 528static bool stencil_element(GrRenderTargetContext* rtc, 529 const GrFixedClip& clip, 530 const GrUserStencilSettings* ss, 531 const SkMatrix& viewMatrix, 532 const SkClipStack::Element* element) { 533 GrAA aa = GrBoolToAA(element->isAA()); 534 switch (element->getType()) { 535 case Element::kEmpty_Type: 536 SkDEBUGFAIL("Should never get here with an empty element."); 537 break; 538 case Element::kRect_Type: 539 return rtc->priv().drawAndStencilRect(clip, ss, 540 (SkRegion::Op)element->getOp(), 541 element->isInverseFilled(), aa, viewMatrix, 542 element->getRect()); 543 break; 544 default: { 545 SkPath path; 546 element->asPath(&path); 547 if (path.isInverseFillType()) { 548 path.toggleInverseFillType(); 549 } 550 551 return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(), 552 element->isInverseFilled(), aa, viewMatrix, path); 553 break; 554 } 555 } 556 557 return false; 558} 559 560static void draw_element(GrRenderTargetContext* rtc, 561 const GrClip& clip, // TODO: can this just always be WideOpen? 562 GrPaint&& paint, 563 GrAA aa, 564 const SkMatrix& viewMatrix, 565 const SkClipStack::Element* element) { 566 // TODO: Draw rrects directly here. 567 switch (element->getType()) { 568 case Element::kEmpty_Type: 569 SkDEBUGFAIL("Should never get here with an empty element."); 570 break; 571 case Element::kRect_Type: 572 rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getRect()); 573 break; 574 default: { 575 SkPath path; 576 element->asPath(&path); 577 if (path.isInverseFillType()) { 578 path.toggleInverseFillType(); 579 } 580 581 rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill()); 582 break; 583 } 584 } 585} 586 587bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const { 588 // The texture may be larger than necessary, this rect represents the part of the texture 589 // we populate with a rasterization of the clip. 590 GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height())); 591 592 if (!fWindowRects.empty()) { 593 clip.setWindowRectangles(fWindowRects.makeOffset(-fIBounds.left(), -fIBounds.top()), 594 GrWindowRectsState::Mode::kExclusive); 595 } 596 597 // The scratch texture that we are drawing into can be substantially larger than the mask. Only 598 // clear the part that we care about. 599 GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0; 600 rtc->priv().clear(clip, initialCoverage, true); 601 602 // Set the matrix so that rendered clip elements are transformed to mask space from clip space. 603 SkMatrix translate; 604 translate.setTranslate(SkIntToScalar(-fIBounds.left()), SkIntToScalar(-fIBounds.top())); 605 606 // walk through each clip element and perform its set op 607 for (ElementList::Iter iter(fElements); iter.get(); iter.next()) { 608 const Element* element = iter.get(); 609 SkRegion::Op op = (SkRegion::Op)element->getOp(); 610 GrAA aa = GrBoolToAA(element->isAA()); 611 bool invert = element->isInverseFilled(); 612 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 613 // draw directly into the result with the stencil set to make the pixels affected 614 // by the clip shape be non-zero. 615 static constexpr GrUserStencilSettings kStencilInElement( 616 GrUserStencilSettings::StaticInit< 617 0xffff, 618 GrUserStencilTest::kAlways, 619 0xffff, 620 GrUserStencilOp::kReplace, 621 GrUserStencilOp::kReplace, 622 0xffff>() 623 ); 624 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) { 625 return false; 626 } 627 628 // Draw to the exterior pixels (those with a zero stencil value). 629 static constexpr GrUserStencilSettings kDrawOutsideElement( 630 GrUserStencilSettings::StaticInit< 631 0x0000, 632 GrUserStencilTest::kEqual, 633 0xffff, 634 GrUserStencilOp::kZero, 635 GrUserStencilOp::kZero, 636 0xffff>() 637 ); 638 if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo, 639 translate, SkRect::Make(fIBounds))) { 640 return false; 641 } 642 } else { 643 // all the remaining ops can just be directly draw into the accumulation buffer 644 GrPaint paint; 645 paint.setCoverageSetOpXPFactory(op, false); 646 647 draw_element(rtc, clip, std::move(paint), aa, translate, element); 648 } 649 } 650 651 return true; 652} 653 654//////////////////////////////////////////////////////////////////////////////// 655// Create a 1-bit clip mask in the stencil buffer. 656 657class StencilClip final : public GrClip { 658public: 659 StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {} 660 const GrFixedClip& fixedClip() const { return fFixedClip; } 661 662 void setWindowRectangles(const GrWindowRectangles& windows, GrWindowRectsState::Mode mode) { 663 fFixedClip.setWindowRectangles(windows, mode); 664 } 665 666private: 667 bool quickContains(const SkRect&) const override { 668 return false; 669 } 670 void getConservativeBounds(int width, int height, SkIRect* bounds, bool* iior) const override { 671 fFixedClip.getConservativeBounds(width, height, bounds, iior); 672 } 673 bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const override { 674 return false; 675 } 676 bool apply(GrContext* context, GrRenderTargetContext* renderTargetContext, bool useHWAA, 677 bool hasUserStencilSettings, GrAppliedClip* out, SkRect* bounds) const override { 678 if (!fFixedClip.apply(context, renderTargetContext, useHWAA, hasUserStencilSettings, out, 679 bounds)) { 680 return false; 681 } 682 out->addStencilClip(); 683 return true; 684 } 685 686 GrFixedClip fFixedClip; 687 688 typedef GrClip INHERITED; 689}; 690 691bool GrReducedClip::drawStencilClipMask(GrContext* context, 692 GrRenderTargetContext* renderTargetContext) const { 693 // We set the current clip to the bounds so that our recursive draws are scissored to them. 694 StencilClip stencilClip(fIBounds); 695 696 if (!fWindowRects.empty()) { 697 stencilClip.setWindowRectangles(fWindowRects, GrWindowRectsState::Mode::kExclusive); 698 } 699 700 bool initialState = InitialState::kAllIn == this->initialState(); 701 renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState); 702 703 // walk through each clip element and perform its set op with the existing clip. 704 for (ElementList::Iter iter(fElements); iter.get(); iter.next()) { 705 const Element* element = iter.get(); 706 GrAAType aaType = GrAAType::kNone; 707 if (element->isAA() && renderTargetContext->isStencilBufferMultisampled()) { 708 aaType = GrAAType::kMSAA; 709 } 710 711 bool fillInverted = false; 712 713 // This will be used to determine whether the clip shape can be rendered into the 714 // stencil with arbitrary stencil settings. 715 GrPathRenderer::StencilSupport stencilSupport; 716 717 SkRegion::Op op = (SkRegion::Op)element->getOp(); 718 719 GrPathRenderer* pr = nullptr; 720 SkPath clipPath; 721 if (Element::kRect_Type == element->getType()) { 722 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; 723 fillInverted = false; 724 } else { 725 element->asPath(&clipPath); 726 fillInverted = clipPath.isInverseFillType(); 727 if (fillInverted) { 728 clipPath.toggleInverseFillType(); 729 } 730 731 GrShape shape(clipPath, GrStyle::SimpleFill()); 732 GrPathRenderer::CanDrawPathArgs canDrawArgs; 733 canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); 734 canDrawArgs.fViewMatrix = &SkMatrix::I(); 735 canDrawArgs.fShape = &shape; 736 canDrawArgs.fAAType = aaType; 737 canDrawArgs.fHasUserStencilSettings = false; 738 739 GrDrawingManager* dm = context->contextPriv().drawingManager(); 740 pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil, 741 &stencilSupport); 742 if (!pr) { 743 return false; 744 } 745 } 746 747 bool canRenderDirectToStencil = 748 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; 749 bool drawDirectToClip; // Given the renderer, the element, 750 // fill rule, and set operation should 751 // we render the element directly to 752 // stencil bit used for clipping. 753 GrUserStencilSettings const* const* stencilPasses = 754 GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted, 755 &drawDirectToClip); 756 757 // draw the element to the client stencil bits if necessary 758 if (!drawDirectToClip) { 759 static constexpr GrUserStencilSettings kDrawToStencil( 760 GrUserStencilSettings::StaticInit< 761 0x0000, 762 GrUserStencilTest::kAlways, 763 0xffff, 764 GrUserStencilOp::kIncMaybeClamp, 765 GrUserStencilOp::kIncMaybeClamp, 766 0xffff>() 767 ); 768 if (Element::kRect_Type == element->getType()) { 769 renderTargetContext->priv().stencilRect(stencilClip.fixedClip(), &kDrawToStencil, 770 aaType, SkMatrix::I(), element->getRect()); 771 } else { 772 if (!clipPath.isEmpty()) { 773 GrShape shape(clipPath, GrStyle::SimpleFill()); 774 if (canRenderDirectToStencil) { 775 GrPaint paint; 776 paint.setXPFactory(GrDisableColorXPFactory::Get()); 777 778 GrPathRenderer::DrawPathArgs args{context, 779 std::move(paint), 780 &kDrawToStencil, 781 renderTargetContext, 782 &stencilClip.fixedClip(), 783 &SkMatrix::I(), 784 &shape, 785 aaType, 786 false}; 787 pr->drawPath(args); 788 } else { 789 GrPathRenderer::StencilPathArgs args; 790 args.fContext = context; 791 args.fRenderTargetContext = renderTargetContext; 792 args.fClip = &stencilClip.fixedClip(); 793 args.fViewMatrix = &SkMatrix::I(); 794 args.fAAType = aaType; 795 args.fShape = &shape; 796 pr->stencilPath(args); 797 } 798 } 799 } 800 } 801 802 // now we modify the clip bit by rendering either the clip 803 // element directly or a bounding rect of the entire clip. 804 for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) { 805 if (drawDirectToClip) { 806 if (Element::kRect_Type == element->getType()) { 807 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, 808 SkMatrix::I(), element->getRect()); 809 } else { 810 GrShape shape(clipPath, GrStyle::SimpleFill()); 811 GrPaint paint; 812 paint.setXPFactory(GrDisableColorXPFactory::Get()); 813 GrPathRenderer::DrawPathArgs args{context, 814 std::move(paint), 815 *pass, 816 renderTargetContext, 817 &stencilClip, 818 &SkMatrix::I(), 819 &shape, 820 aaType, 821 false}; 822 pr->drawPath(args); 823 } 824 } else { 825 // The view matrix is setup to do clip space -> stencil space translation, so 826 // draw rect in clip space. 827 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, SkMatrix::I(), 828 SkRect::Make(fIBounds)); 829 } 830 } 831 } 832 return true; 833} 834