Line data Source code
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 0 : GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
34 0 : int maxWindowRectangles) {
35 0 : SkASSERT(!queryBounds.isEmpty());
36 0 : fHasIBounds = false;
37 :
38 0 : if (stack.isWideOpen()) {
39 0 : fInitialState = InitialState::kAllIn;
40 0 : return;
41 : }
42 :
43 : SkClipStack::BoundsType stackBoundsType;
44 : SkRect stackBounds;
45 : bool iior;
46 0 : stack.getBounds(&stackBounds, &stackBoundsType, &iior);
47 :
48 0 : if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)) {
49 0 : bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
50 0 : fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
51 0 : return;
52 : }
53 :
54 0 : 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 0 : SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
58 0 : SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
59 0 : 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 0 : stackBounds.round(&fIBounds);
63 0 : fHasIBounds = true;
64 0 : fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
65 0 : return;
66 : }
67 0 : if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
68 0 : fInitialState = InitialState::kAllIn;
69 0 : return;
70 : }
71 :
72 : SkRect tightBounds;
73 0 : SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
74 0 : fIBounds = GrClip::GetPixelIBounds(tightBounds);
75 0 : SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above.
76 0 : fHasIBounds = true;
77 :
78 : // Implement the clip with an AA rect element.
79 0 : fElements.addToHead(stackBounds, kReplace_SkClipOp, true/*doAA*/);
80 0 : fElementsGenID = stack.getTopmostGenID();
81 0 : fRequiresAA = true;
82 :
83 0 : fInitialState = InitialState::kAllOut;
84 0 : return;
85 : }
86 :
87 0 : SkRect tighterQuery = queryBounds;
88 0 : 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 0 : SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
92 : }
93 :
94 0 : fIBounds = GrClip::GetPixelIBounds(tighterQuery);
95 0 : SkASSERT(!fIBounds.isEmpty()); // Empty should have been blocked by IsOutsideClip above.
96 0 : 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 0 : this->walkStack(stack, tighterQuery, maxWindowRectangles);
101 :
102 0 : if (fWindowRects.count() < maxWindowRectangles) {
103 0 : this->addInteriorWindowRectangles(maxWindowRectangles);
104 : }
105 : }
106 :
107 0 : void 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 0 : } 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 0 : bool embiggens = false;
123 0 : 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 0 : GrClip::kBoundsTolerance);
129 :
130 0 : SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
131 0 : int numAAElements = 0;
132 0 : while (InitialTriState::kUnknown == initialTriState) {
133 0 : const Element* element = iter.prev();
134 0 : if (nullptr == element) {
135 0 : initialTriState = InitialTriState::kAllIn;
136 0 : break;
137 : }
138 0 : if (SkClipStack::kEmptyGenID == element->getGenID()) {
139 0 : initialTriState = InitialTriState::kAllOut;
140 0 : break;
141 : }
142 0 : if (SkClipStack::kWideOpenGenID == element->getGenID()) {
143 0 : initialTriState = InitialTriState::kAllIn;
144 0 : break;
145 : }
146 :
147 0 : bool skippable = false;
148 0 : bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
149 :
150 0 : 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 0 : if (element->isInverseFilled()) {
155 0 : if (element->contains(relaxedQueryBounds)) {
156 0 : skippable = true;
157 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
158 0 : initialTriState = InitialTriState::kAllOut;
159 0 : skippable = true;
160 : }
161 : } else {
162 0 : if (element->contains(relaxedQueryBounds)) {
163 0 : initialTriState = InitialTriState::kAllOut;
164 0 : skippable = true;
165 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
166 0 : skippable = true;
167 0 : } else if (fWindowRects.count() < maxWindowRectangles && !embiggens &&
168 0 : !element->isAA() && Element::kRect_Type == element->getType()) {
169 0 : this->addWindowRectangle(element->getRect(), false);
170 0 : skippable = true;
171 : }
172 : }
173 0 : if (!skippable) {
174 0 : emsmallens = true;
175 : }
176 0 : 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 0 : if (element->isInverseFilled()) {
182 0 : if (element->contains(relaxedQueryBounds)) {
183 0 : initialTriState = InitialTriState::kAllOut;
184 0 : skippable = true;
185 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
186 0 : skippable = true;
187 : }
188 : } else {
189 0 : if (element->contains(relaxedQueryBounds)) {
190 0 : skippable = true;
191 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
192 0 : initialTriState = InitialTriState::kAllOut;
193 0 : skippable = true;
194 0 : } else if (!embiggens && !element->isAA() &&
195 0 : 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 0 : element->getRect().round(&nonaaRect);
200 0 : if (!this->intersectIBounds(nonaaRect)) {
201 0 : return;
202 : }
203 0 : skippable = true;
204 : }
205 : }
206 0 : if (!skippable) {
207 0 : emsmallens = true;
208 : }
209 0 : 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 0 : if (element->isInverseFilled()) {
215 0 : if (element->contains(relaxedQueryBounds)) {
216 0 : skippable = true;
217 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
218 0 : initialTriState = InitialTriState::kAllIn;
219 0 : skippable = true;
220 : }
221 : } else {
222 0 : if (element->contains(relaxedQueryBounds)) {
223 0 : initialTriState = InitialTriState::kAllIn;
224 0 : skippable = true;
225 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
226 0 : skippable = true;
227 : }
228 : }
229 0 : if (!skippable) {
230 0 : embiggens = true;
231 : }
232 0 : 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 0 : if (element->isInverseFilled()) {
239 0 : if (element->contains(relaxedQueryBounds)) {
240 0 : skippable = true;
241 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
242 0 : isFlip = true;
243 : }
244 : } else {
245 0 : if (element->contains(relaxedQueryBounds)) {
246 0 : isFlip = true;
247 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
248 0 : skippable = true;
249 : }
250 : }
251 0 : if (!skippable) {
252 0 : emsmallens = embiggens = true;
253 : }
254 0 : 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 0 : if (element->isInverseFilled()) {
261 0 : if (element->contains(relaxedQueryBounds)) {
262 0 : initialTriState = InitialTriState::kAllOut;
263 0 : skippable = true;
264 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
265 0 : isFlip = true;
266 : }
267 : } else {
268 0 : if (element->contains(relaxedQueryBounds)) {
269 0 : isFlip = true;
270 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
271 0 : initialTriState = InitialTriState::kAllOut;
272 0 : skippable = true;
273 : }
274 : }
275 0 : if (!skippable) {
276 0 : emsmallens = embiggens = true;
277 : }
278 0 : 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 0 : if (element->isInverseFilled()) {
286 0 : if (element->contains(relaxedQueryBounds)) {
287 0 : initialTriState = InitialTriState::kAllOut;
288 0 : skippable = true;
289 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
290 0 : initialTriState = InitialTriState::kAllIn;
291 0 : skippable = true;
292 : }
293 : } else {
294 0 : if (element->contains(relaxedQueryBounds)) {
295 0 : initialTriState = InitialTriState::kAllIn;
296 0 : skippable = true;
297 0 : } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
298 0 : initialTriState = InitialTriState::kAllOut;
299 0 : skippable = true;
300 0 : } else if (!embiggens && !element->isAA() &&
301 0 : 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 0 : element->getRect().round(&nonaaRect);
306 0 : if (!this->intersectIBounds(nonaaRect)) {
307 0 : return;
308 : }
309 0 : initialTriState = InitialTriState::kAllIn;
310 0 : skippable = true;
311 : }
312 : }
313 0 : if (!skippable) {
314 0 : initialTriState = InitialTriState::kAllOut;
315 0 : embiggens = emsmallens = true;
316 : }
317 0 : break;
318 : default:
319 0 : SkDEBUGFAIL("Unexpected op.");
320 0 : break;
321 : }
322 0 : if (!skippable) {
323 0 : if (0 == fElements.count()) {
324 : // This will be the last element. Record the stricter genID.
325 0 : fElementsGenID = element->getGenID();
326 : }
327 :
328 : // if it is a flip, change it to a bounds-filling rect
329 0 : if (isFlip) {
330 0 : SkASSERT(kXOR_SkClipOp == element->getOp() ||
331 : kReverseDifference_SkClipOp == element->getOp());
332 0 : fElements.addToHead(SkRect::Make(fIBounds), kReverseDifference_SkClipOp, false);
333 : } else {
334 0 : Element* newElement = fElements.addToHead(*element);
335 0 : if (newElement->isAA()) {
336 0 : ++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 0 : bool isReplace = kReplace_SkClipOp == newElement->getOp();
342 0 : if (newElement->isInverseFilled() &&
343 0 : (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
344 0 : newElement->invertShapeFillType();
345 0 : newElement->setOp(kDifference_SkClipOp);
346 0 : if (isReplace) {
347 0 : SkASSERT(InitialTriState::kAllOut == initialTriState);
348 0 : initialTriState = InitialTriState::kAllIn;
349 : }
350 : }
351 : }
352 : }
353 : }
354 :
355 0 : if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
356 0 : (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
357 0 : fElements.reset();
358 0 : numAAElements = 0;
359 : } else {
360 0 : Element* element = fElements.headIter().get();
361 0 : while (element) {
362 0 : bool skippable = false;
363 0 : switch (element->getOp()) {
364 : case kDifference_SkClipOp:
365 : // subtracting from the empty set yields the empty set.
366 0 : skippable = InitialTriState::kAllOut == initialTriState;
367 0 : break;
368 : case kIntersect_SkClipOp:
369 : // intersecting with the empty set yields the empty set
370 0 : if (InitialTriState::kAllOut == initialTriState) {
371 0 : skippable = true;
372 : } else {
373 : // We can clear to zero and then simply draw the clip element.
374 0 : initialTriState = InitialTriState::kAllOut;
375 0 : element->setOp(kReplace_SkClipOp);
376 : }
377 0 : break;
378 : case kUnion_SkClipOp:
379 0 : if (InitialTriState::kAllIn == initialTriState) {
380 : // unioning the infinite plane with anything is a no-op.
381 0 : skippable = true;
382 : } else {
383 : // unioning the empty set with a shape is the shape.
384 0 : element->setOp(kReplace_SkClipOp);
385 : }
386 0 : break;
387 : case kXOR_SkClipOp:
388 0 : if (InitialTriState::kAllOut == initialTriState) {
389 : // xor could be changed to diff in the kAllIn case, not sure it's a win.
390 0 : element->setOp(kReplace_SkClipOp);
391 : }
392 0 : break;
393 : case kReverseDifference_SkClipOp:
394 0 : if (InitialTriState::kAllIn == initialTriState) {
395 : // subtracting the whole plane will yield the empty set.
396 0 : skippable = true;
397 0 : initialTriState = InitialTriState::kAllOut;
398 : } else {
399 : // this picks up flips inserted in the backwards pass.
400 0 : skippable = element->isInverseFilled() ?
401 0 : GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
402 : element->contains(relaxedQueryBounds);
403 0 : if (skippable) {
404 0 : initialTriState = InitialTriState::kAllIn;
405 : } else {
406 0 : element->setOp(kReplace_SkClipOp);
407 : }
408 : }
409 0 : break;
410 : case kReplace_SkClipOp:
411 0 : skippable = false; // we would have skipped it in the backwards walk if we
412 : // could've.
413 0 : break;
414 : default:
415 0 : SkDEBUGFAIL("Unexpected op.");
416 0 : break;
417 : }
418 0 : if (!skippable) {
419 0 : break;
420 : } else {
421 0 : if (element->isAA()) {
422 0 : --numAAElements;
423 : }
424 0 : fElements.popHead();
425 0 : element = fElements.headIter().get();
426 : }
427 : }
428 : }
429 0 : fRequiresAA = numAAElements > 0;
430 :
431 0 : SkASSERT(InitialTriState::kUnknown != initialTriState);
432 0 : fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
433 : }
434 :
435 0 : static bool element_is_pure_subtract(SkClipOp op) {
436 0 : SkASSERT(static_cast<int>(op) >= 0);
437 0 : 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 :
443 0 : void GrReducedClip::addInteriorWindowRectangles(int maxWindowRectangles) {
444 0 : 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 0 : ElementList::Iter iter(fElements, ElementList::Iter::kTail_IterStart);
448 0 : for (; iter.get() && element_is_pure_subtract(iter.get()->getOp()); iter.prev()) {
449 0 : const Element* element = iter.get();
450 0 : if (kDifference_SkClipOp != element->getOp()) {
451 0 : continue;
452 : }
453 :
454 0 : if (Element::kRect_Type == element->getType()) {
455 0 : SkASSERT(element->isAA());
456 0 : this->addWindowRectangle(element->getRect(), true);
457 0 : if (fWindowRects.count() >= maxWindowRectangles) {
458 0 : return;
459 : }
460 0 : continue;
461 : }
462 :
463 0 : if (Element::kRRect_Type == element->getType()) {
464 : // For round rects we add two overlapping windows in the shape of a plus.
465 0 : const SkRRect& clipRRect = element->getRRect();
466 0 : SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
467 0 : SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
468 0 : if (SkRRect::kComplex_Type == clipRRect.getType()) {
469 0 : const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
470 0 : const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
471 0 : insetTL.fX = SkTMax(insetTL.x(), insetBL.x());
472 0 : insetTL.fY = SkTMax(insetTL.y(), insetTR.y());
473 0 : insetBR.fX = SkTMax(insetBR.x(), insetTR.x());
474 0 : insetBR.fY = SkTMax(insetBR.y(), insetBL.y());
475 : }
476 0 : const SkRect& bounds = clipRRect.getBounds();
477 0 : if (insetTL.x() + insetBR.x() >= bounds.width() ||
478 0 : insetTL.y() + insetBR.y() >= bounds.height()) {
479 0 : continue; // The interior "plus" is empty.
480 : }
481 :
482 0 : SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
483 0 : bounds.right(), bounds.bottom() - insetBR.y());
484 0 : this->addWindowRectangle(horzRect, element->isAA());
485 0 : if (fWindowRects.count() >= maxWindowRectangles) {
486 0 : return;
487 : }
488 :
489 0 : SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
490 0 : bounds.right() - insetBR.x(), bounds.bottom());
491 0 : this->addWindowRectangle(vertRect, element->isAA());
492 0 : if (fWindowRects.count() >= maxWindowRectangles) {
493 0 : return;
494 : }
495 0 : continue;
496 : }
497 : }
498 : }
499 :
500 0 : inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
501 : SkIRect window;
502 0 : if (!elementIsAA) {
503 0 : elementInteriorRect.round(&window);
504 : } else {
505 0 : elementInteriorRect.roundIn(&window);
506 : }
507 0 : if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
508 0 : fWindowRects.addWindow(window);
509 : }
510 0 : }
511 :
512 0 : inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) {
513 0 : SkASSERT(fHasIBounds);
514 0 : if (!fIBounds.intersect(irect)) {
515 0 : fHasIBounds = false;
516 0 : fWindowRects.reset();
517 0 : fElements.reset();
518 0 : fRequiresAA = false;
519 0 : fInitialState = InitialState::kAllOut;
520 0 : return false;
521 : }
522 0 : return true;
523 : }
524 :
525 : ////////////////////////////////////////////////////////////////////////////////
526 : // Create a 8-bit clip mask in alpha
527 :
528 0 : static bool stencil_element(GrRenderTargetContext* rtc,
529 : const GrFixedClip& clip,
530 : const GrUserStencilSettings* ss,
531 : const SkMatrix& viewMatrix,
532 : const SkClipStack::Element* element) {
533 0 : GrAA aa = GrBoolToAA(element->isAA());
534 0 : switch (element->getType()) {
535 : case Element::kEmpty_Type:
536 0 : SkDEBUGFAIL("Should never get here with an empty element.");
537 0 : break;
538 : case Element::kRect_Type:
539 0 : return rtc->priv().drawAndStencilRect(clip, ss,
540 0 : (SkRegion::Op)element->getOp(),
541 0 : element->isInverseFilled(), aa, viewMatrix,
542 0 : element->getRect());
543 : break;
544 : default: {
545 0 : SkPath path;
546 0 : element->asPath(&path);
547 0 : if (path.isInverseFillType()) {
548 0 : path.toggleInverseFillType();
549 : }
550 :
551 0 : return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
552 0 : element->isInverseFilled(), aa, viewMatrix, path);
553 : break;
554 : }
555 : }
556 :
557 0 : return false;
558 : }
559 :
560 0 : static 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 0 : switch (element->getType()) {
568 : case Element::kEmpty_Type:
569 0 : SkDEBUGFAIL("Should never get here with an empty element.");
570 0 : break;
571 : case Element::kRect_Type:
572 0 : rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getRect());
573 0 : break;
574 : default: {
575 0 : SkPath path;
576 0 : element->asPath(&path);
577 0 : if (path.isInverseFillType()) {
578 0 : path.toggleInverseFillType();
579 : }
580 :
581 0 : rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
582 0 : break;
583 : }
584 : }
585 0 : }
586 :
587 0 : bool 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 0 : GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height()));
591 :
592 0 : if (!fWindowRects.empty()) {
593 0 : clip.setWindowRectangles(fWindowRects.makeOffset(-fIBounds.left(), -fIBounds.top()),
594 0 : 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 0 : GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0;
600 0 : 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 0 : translate.setTranslate(SkIntToScalar(-fIBounds.left()), SkIntToScalar(-fIBounds.top()));
605 :
606 : // walk through each clip element and perform its set op
607 0 : for (ElementList::Iter iter(fElements); iter.get(); iter.next()) {
608 0 : const Element* element = iter.get();
609 0 : SkRegion::Op op = (SkRegion::Op)element->getOp();
610 0 : GrAA aa = GrBoolToAA(element->isAA());
611 0 : bool invert = element->isInverseFilled();
612 0 : 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 0 : if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
625 0 : 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 0 : if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo,
639 0 : translate, SkRect::Make(fIBounds))) {
640 0 : return false;
641 0 : }
642 : } else {
643 : // all the remaining ops can just be directly draw into the accumulation buffer
644 0 : GrPaint paint;
645 0 : paint.setCoverageSetOpXPFactory(op, false);
646 :
647 0 : draw_element(rtc, clip, std::move(paint), aa, translate, element);
648 : }
649 : }
650 :
651 0 : return true;
652 : }
653 :
654 : ////////////////////////////////////////////////////////////////////////////////
655 : // Create a 1-bit clip mask in the stencil buffer.
656 :
657 0 : class StencilClip final : public GrClip {
658 : public:
659 0 : StencilClip(const SkIRect& scissorRect) : fFixedClip(scissorRect) {}
660 0 : const GrFixedClip& fixedClip() const { return fFixedClip; }
661 :
662 0 : void setWindowRectangles(const GrWindowRectangles& windows, GrWindowRectsState::Mode mode) {
663 0 : fFixedClip.setWindowRectangles(windows, mode);
664 0 : }
665 :
666 : private:
667 0 : bool quickContains(const SkRect&) const override {
668 0 : return false;
669 : }
670 0 : void getConservativeBounds(int width, int height, SkIRect* bounds, bool* iior) const override {
671 0 : fFixedClip.getConservativeBounds(width, height, bounds, iior);
672 0 : }
673 0 : bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const override {
674 0 : return false;
675 : }
676 0 : bool apply(GrContext* context, GrRenderTargetContext* renderTargetContext, bool useHWAA,
677 : bool hasUserStencilSettings, GrAppliedClip* out, SkRect* bounds) const override {
678 0 : if (!fFixedClip.apply(context, renderTargetContext, useHWAA, hasUserStencilSettings, out,
679 : bounds)) {
680 0 : return false;
681 : }
682 0 : out->addStencilClip();
683 0 : return true;
684 : }
685 :
686 : GrFixedClip fFixedClip;
687 :
688 : typedef GrClip INHERITED;
689 : };
690 :
691 0 : bool 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 0 : StencilClip stencilClip(fIBounds);
695 :
696 0 : if (!fWindowRects.empty()) {
697 0 : stencilClip.setWindowRectangles(fWindowRects, GrWindowRectsState::Mode::kExclusive);
698 : }
699 :
700 0 : bool initialState = InitialState::kAllIn == this->initialState();
701 0 : renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState);
702 :
703 : // walk through each clip element and perform its set op with the existing clip.
704 0 : for (ElementList::Iter iter(fElements); iter.get(); iter.next()) {
705 0 : const Element* element = iter.get();
706 0 : GrAAType aaType = GrAAType::kNone;
707 0 : if (element->isAA() && renderTargetContext->isStencilBufferMultisampled()) {
708 0 : aaType = GrAAType::kMSAA;
709 : }
710 :
711 0 : 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 0 : SkRegion::Op op = (SkRegion::Op)element->getOp();
718 :
719 0 : GrPathRenderer* pr = nullptr;
720 0 : SkPath clipPath;
721 0 : if (Element::kRect_Type == element->getType()) {
722 0 : stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
723 0 : fillInverted = false;
724 : } else {
725 0 : element->asPath(&clipPath);
726 0 : fillInverted = clipPath.isInverseFillType();
727 0 : if (fillInverted) {
728 0 : clipPath.toggleInverseFillType();
729 : }
730 :
731 0 : GrShape shape(clipPath, GrStyle::SimpleFill());
732 : GrPathRenderer::CanDrawPathArgs canDrawArgs;
733 0 : canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
734 0 : canDrawArgs.fViewMatrix = &SkMatrix::I();
735 0 : canDrawArgs.fShape = &shape;
736 0 : canDrawArgs.fAAType = aaType;
737 0 : canDrawArgs.fHasUserStencilSettings = false;
738 :
739 0 : GrDrawingManager* dm = context->contextPriv().drawingManager();
740 : pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil,
741 0 : &stencilSupport);
742 0 : if (!pr) {
743 0 : return false;
744 : }
745 : }
746 :
747 : bool canRenderDirectToStencil =
748 0 : 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 0 : GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
755 0 : &drawDirectToClip);
756 :
757 : // draw the element to the client stencil bits if necessary
758 0 : 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 0 : if (Element::kRect_Type == element->getType()) {
769 0 : renderTargetContext->priv().stencilRect(stencilClip.fixedClip(), &kDrawToStencil,
770 0 : aaType, SkMatrix::I(), element->getRect());
771 : } else {
772 0 : if (!clipPath.isEmpty()) {
773 0 : GrShape shape(clipPath, GrStyle::SimpleFill());
774 0 : if (canRenderDirectToStencil) {
775 0 : GrPaint paint;
776 0 : paint.setXPFactory(GrDisableColorXPFactory::Get());
777 :
778 : GrPathRenderer::DrawPathArgs args{context,
779 0 : std::move(paint),
780 : &kDrawToStencil,
781 : renderTargetContext,
782 0 : &stencilClip.fixedClip(),
783 0 : &SkMatrix::I(),
784 : &shape,
785 : aaType,
786 0 : false};
787 0 : pr->drawPath(args);
788 : } else {
789 : GrPathRenderer::StencilPathArgs args;
790 0 : args.fContext = context;
791 0 : args.fRenderTargetContext = renderTargetContext;
792 0 : args.fClip = &stencilClip.fixedClip();
793 0 : args.fViewMatrix = &SkMatrix::I();
794 0 : args.fAAType = aaType;
795 0 : args.fShape = &shape;
796 0 : 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 0 : for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
805 0 : if (drawDirectToClip) {
806 0 : if (Element::kRect_Type == element->getType()) {
807 0 : renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType,
808 0 : SkMatrix::I(), element->getRect());
809 : } else {
810 0 : GrShape shape(clipPath, GrStyle::SimpleFill());
811 0 : GrPaint paint;
812 0 : paint.setXPFactory(GrDisableColorXPFactory::Get());
813 : GrPathRenderer::DrawPathArgs args{context,
814 0 : std::move(paint),
815 0 : *pass,
816 : renderTargetContext,
817 : &stencilClip,
818 0 : &SkMatrix::I(),
819 : &shape,
820 : aaType,
821 0 : false};
822 0 : 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 0 : renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, SkMatrix::I(),
828 0 : SkRect::Make(fIBounds));
829 : }
830 : }
831 : }
832 0 : return true;
833 : }
|