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