1 /*
2  * Copyright 2010 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 "SkRasterClip.h"
9 #include "SkPath.h"
10 
11 enum MutateResult {
12     kDoNothing_MutateResult,
13     kReplaceClippedAgainstGlobalBounds_MutateResult,
14     kContinue_MutateResult,
15 };
16 
mutate_conservative_op(SkRegion::Op * op,bool inverseFilled)17 static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
18     if (inverseFilled) {
19         switch (*op) {
20             case SkRegion::kIntersect_Op:
21             case SkRegion::kDifference_Op:
22                 // These ops can only shrink the current clip. So leaving
23                 // the clip unchanged conservatively respects the contract.
24                 return kDoNothing_MutateResult;
25             case SkRegion::kUnion_Op:
26             case SkRegion::kReplace_Op:
27             case SkRegion::kReverseDifference_Op:
28             case SkRegion::kXOR_Op: {
29                 // These ops can grow the current clip up to the extents of
30                 // the input clip, which is inverse filled, so we just set
31                 // the current clip to the device bounds.
32                 *op = SkRegion::kReplace_Op;
33                 return kReplaceClippedAgainstGlobalBounds_MutateResult;
34             }
35         }
36     } else {
37         // Not inverse filled
38         switch (*op) {
39             case SkRegion::kIntersect_Op:
40             case SkRegion::kUnion_Op:
41             case SkRegion::kReplace_Op:
42                 return kContinue_MutateResult;
43             case SkRegion::kDifference_Op:
44                 // Difference can only shrink the current clip.
45                 // Leaving clip unchanged conservatively fullfills the contract.
46                 return kDoNothing_MutateResult;
47             case SkRegion::kReverseDifference_Op:
48                 // To reverse, we swap in the bounds with a replace op.
49                 // As with difference, leave it unchanged.
50                 *op = SkRegion::kReplace_Op;
51                 return kContinue_MutateResult;
52             case SkRegion::kXOR_Op:
53                 // Be conservative, based on (A XOR B) always included in (A union B),
54                 // which is always included in (bounds(A) union bounds(B))
55                 *op = SkRegion::kUnion_Op;
56                 return kContinue_MutateResult;
57         }
58     }
59     SkASSERT(false);    // unknown op
60     return kDoNothing_MutateResult;
61 }
62 
opRect(const SkRect & localRect,const SkMatrix & ctm,const SkIRect & devBounds,SkRegion::Op op,bool doAA)63 void SkConservativeClip::opRect(const SkRect& localRect, const SkMatrix& ctm,
64                                 const SkIRect& devBounds, SkRegion::Op op, bool doAA) {
65     SkIRect ir;
66     switch (mutate_conservative_op(&op, false)) {
67         case kDoNothing_MutateResult:
68             return;
69         case kReplaceClippedAgainstGlobalBounds_MutateResult:
70             ir = devBounds;
71             break;
72         case kContinue_MutateResult: {
73             SkRect devRect;
74             ctm.mapRect(&devRect, localRect);
75             ir = doAA ? devRect.roundOut() : devRect.round();
76         } break;
77     }
78     this->opIRect(ir, op);
79 }
80 
opRRect(const SkRRect & rrect,const SkMatrix & ctm,const SkIRect & devBounds,SkRegion::Op op,bool doAA)81 void SkConservativeClip::opRRect(const SkRRect& rrect, const SkMatrix& ctm,
82                                  const SkIRect& devBounds, SkRegion::Op op, bool doAA) {
83     this->opRect(rrect.getBounds(), ctm, devBounds, op, doAA);
84 }
85 
opPath(const SkPath & path,const SkMatrix & ctm,const SkIRect & devBounds,SkRegion::Op op,bool doAA)86 void SkConservativeClip::opPath(const SkPath& path, const SkMatrix& ctm, const SkIRect& devBounds,
87                                 SkRegion::Op op, bool doAA) {
88     SkIRect ir;
89     switch (mutate_conservative_op(&op, path.isInverseFillType())) {
90         case kDoNothing_MutateResult:
91             return;
92         case kReplaceClippedAgainstGlobalBounds_MutateResult:
93             ir = devBounds;
94             break;
95         case kContinue_MutateResult: {
96             SkRect bounds = path.getBounds();
97             ctm.mapRect(&bounds);
98             ir = bounds.roundOut();
99             break;
100         }
101     }
102     return this->opIRect(ir, op);
103 }
104 
opRegion(const SkRegion & rgn,SkRegion::Op op)105 void SkConservativeClip::opRegion(const SkRegion& rgn, SkRegion::Op op) {
106     this->opIRect(rgn.getBounds(), op);
107 }
108 
opIRect(const SkIRect & devRect,SkRegion::Op op)109 void SkConservativeClip::opIRect(const SkIRect& devRect, SkRegion::Op op) {
110     if (SkRegion::kIntersect_Op == op) {
111         if (!fBounds.intersect(devRect)) {
112             fBounds.setEmpty();
113         }
114         return;
115     }
116 
117     // This may still create a complex region (which we would then take the bounds
118     // Perhaps we should inline the op-logic directly to never create the rgn...
119     SkRegion result;
120     result.op(SkRegion(fBounds), SkRegion(devRect), op);
121     fBounds = result.getBounds();
122     this->applyClipRestriction(op, &fBounds);
123 }
124 
125 ///////////////////////////////////////////////////////////////////////////////////////////////////
126 
SkRasterClip(const SkRasterClip & src)127 SkRasterClip::SkRasterClip(const SkRasterClip& src) {
128     AUTO_RASTERCLIP_VALIDATE(src);
129 
130     fIsBW = src.fIsBW;
131     if (fIsBW) {
132         fBW = src.fBW;
133     } else {
134         fAA = src.fAA;
135     }
136 
137     fIsEmpty = src.isEmpty();
138     fIsRect = src.isRect();
139     fClipRestrictionRect = src.fClipRestrictionRect;
140     SkDEBUGCODE(this->validate();)
141 }
142 
SkRasterClip(const SkRegion & rgn)143 SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
144     fIsBW = true;
145     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
146     fIsRect = !fIsEmpty;
147     SkDEBUGCODE(this->validate();)
148 }
149 
SkRasterClip(const SkIRect & bounds)150 SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
151     fIsBW = true;
152     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
153     fIsRect = !fIsEmpty;
154     SkDEBUGCODE(this->validate();)
155 }
156 
SkRasterClip()157 SkRasterClip::SkRasterClip() {
158     fIsBW = true;
159     fIsEmpty = true;
160     fIsRect = false;
161     SkDEBUGCODE(this->validate();)
162 }
163 
~SkRasterClip()164 SkRasterClip::~SkRasterClip() {
165     SkDEBUGCODE(this->validate();)
166 }
167 
operator ==(const SkRasterClip & other) const168 bool SkRasterClip::operator==(const SkRasterClip& other) const {
169     if (fIsBW != other.fIsBW) {
170         return false;
171     }
172     bool isEqual = fIsBW ? fBW == other.fBW : fAA == other.fAA;
173 #ifdef SK_DEBUG
174     if (isEqual) {
175         SkASSERT(fIsEmpty == other.fIsEmpty);
176         SkASSERT(fIsRect == other.fIsRect);
177     }
178 #endif
179     return isEqual;
180 }
181 
isComplex() const182 bool SkRasterClip::isComplex() const {
183     return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
184 }
185 
getBounds() const186 const SkIRect& SkRasterClip::getBounds() const {
187     return fIsBW ? fBW.getBounds() : fAA.getBounds();
188 }
189 
setEmpty()190 bool SkRasterClip::setEmpty() {
191     AUTO_RASTERCLIP_VALIDATE(*this);
192 
193     fIsBW = true;
194     fBW.setEmpty();
195     fAA.setEmpty();
196     fIsEmpty = true;
197     fIsRect = false;
198     return false;
199 }
200 
setRect(const SkIRect & rect)201 bool SkRasterClip::setRect(const SkIRect& rect) {
202     AUTO_RASTERCLIP_VALIDATE(*this);
203 
204     fIsBW = true;
205     fAA.setEmpty();
206     fIsRect = fBW.setRect(rect);
207     fIsEmpty = !fIsRect;
208     return fIsRect;
209 }
210 
211 /////////////////////////////////////////////////////////////////////////////////////
212 
setConservativeRect(const SkRect & r,const SkIRect & clipR,bool isInverse)213 bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
214     SkRegion::Op op;
215     if (isInverse) {
216         op = SkRegion::kDifference_Op;
217     } else {
218         op = SkRegion::kIntersect_Op;
219     }
220     fBW.setRect(clipR);
221     fBW.op(r.roundOut(), op);
222     return this->updateCacheAndReturnNonEmpty();
223 }
224 
225 /////////////////////////////////////////////////////////////////////////////////////
226 
setPath(const SkPath & path,const SkRegion & clip,bool doAA)227 bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
228     AUTO_RASTERCLIP_VALIDATE(*this);
229 
230     if (this->isBW() && !doAA) {
231         (void)fBW.setPath(path, clip);
232     } else {
233         // TODO: since we are going to over-write fAA completely (aren't we?)
234         // we should just clear our BW data (if any) and set fIsAA=true
235         if (this->isBW()) {
236             this->convertToAA();
237         }
238         (void)fAA.setPath(path, &clip, doAA);
239     }
240     return this->updateCacheAndReturnNonEmpty();
241 }
242 
op(const SkRRect & rrect,const SkMatrix & matrix,const SkIRect & devBounds,SkRegion::Op op,bool doAA)243 bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
244                       SkRegion::Op op, bool doAA) {
245     SkIRect bounds(devBounds);
246     this->applyClipRestriction(op, &bounds);
247 
248     SkPath path;
249     path.addRRect(rrect);
250 
251     return this->op(path, matrix, bounds, op, doAA);
252 }
253 
op(const SkPath & path,const SkMatrix & matrix,const SkIRect & devBounds,SkRegion::Op op,bool doAA)254 bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
255                       SkRegion::Op op, bool doAA) {
256     AUTO_RASTERCLIP_VALIDATE(*this);
257     SkIRect bounds(devBounds);
258     this->applyClipRestriction(op, &bounds);
259 
260     // base is used to limit the size (and therefore memory allocation) of the
261     // region that results from scan converting devPath.
262     SkRegion base;
263 
264     SkPath devPath;
265     if (matrix.isIdentity()) {
266         devPath = path;
267     } else {
268         path.transform(matrix, &devPath);
269         devPath.setIsVolatile(true);
270     }
271     if (SkRegion::kIntersect_Op == op) {
272         // since we are intersect, we can do better (tighter) with currRgn's
273         // bounds, than just using the device. However, if currRgn is complex,
274         // our region blitter may hork, so we do that case in two steps.
275         if (this->isRect()) {
276             // FIXME: we should also be able to do this when this->isBW(),
277             // but relaxing the test above triggers GM asserts in
278             // SkRgnBuilder::blitH(). We need to investigate what's going on.
279             return this->setPath(devPath, this->bwRgn(), doAA);
280         } else {
281             base.setRect(this->getBounds());
282             SkRasterClip clip;
283             clip.setPath(devPath, base, doAA);
284             return this->op(clip, op);
285         }
286     } else {
287         base.setRect(bounds);
288 
289         if (SkRegion::kReplace_Op == op) {
290             return this->setPath(devPath, base, doAA);
291         } else {
292             SkRasterClip clip;
293             clip.setPath(devPath, base, doAA);
294             return this->op(clip, op);
295         }
296     }
297 }
298 
setPath(const SkPath & path,const SkIRect & clip,bool doAA)299 bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
300     SkRegion tmp;
301     tmp.setRect(clip);
302     return this->setPath(path, tmp, doAA);
303 }
304 
op(const SkIRect & rect,SkRegion::Op op)305 bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
306     AUTO_RASTERCLIP_VALIDATE(*this);
307 
308     fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
309     return this->updateCacheAndReturnNonEmpty();
310 }
311 
op(const SkRegion & rgn,SkRegion::Op op)312 bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
313     AUTO_RASTERCLIP_VALIDATE(*this);
314 
315     if (fIsBW) {
316         (void)fBW.op(rgn, op);
317     } else {
318         SkAAClip tmp;
319         tmp.setRegion(rgn);
320         (void)fAA.op(tmp, op);
321     }
322     return this->updateCacheAndReturnNonEmpty();
323 }
324 
op(const SkRasterClip & clip,SkRegion::Op op)325 bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
326     AUTO_RASTERCLIP_VALIDATE(*this);
327     clip.validate();
328 
329     if (this->isBW() && clip.isBW()) {
330         (void)fBW.op(clip.fBW, op);
331     } else {
332         SkAAClip tmp;
333         const SkAAClip* other;
334 
335         if (this->isBW()) {
336             this->convertToAA();
337         }
338         if (clip.isBW()) {
339             tmp.setRegion(clip.bwRgn());
340             other = &tmp;
341         } else {
342             other = &clip.aaRgn();
343         }
344         (void)fAA.op(*other, op);
345     }
346     return this->updateCacheAndReturnNonEmpty();
347 }
348 
349 /**
350  *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
351  *  axis. Thus we can treat an axis coordinate as an integer if it differs
352  *  from its nearest int by < half of that value (1.8 in this case).
353  */
nearly_integral(SkScalar x)354 static bool nearly_integral(SkScalar x) {
355     static const SkScalar domain = SK_Scalar1 / 4;
356     static const SkScalar halfDomain = domain / 2;
357 
358     x += halfDomain;
359     return x - SkScalarFloorToScalar(x) < domain;
360 }
361 
op(const SkRect & localRect,const SkMatrix & matrix,const SkIRect & devBounds,SkRegion::Op op,bool doAA)362 bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
363                       SkRegion::Op op, bool doAA) {
364     AUTO_RASTERCLIP_VALIDATE(*this);
365     SkRect devRect;
366 
367     const bool isScaleTrans = matrix.isScaleTranslate();
368     if (!isScaleTrans) {
369         SkPath path;
370         path.addRect(localRect);
371         path.setIsVolatile(true);
372         return this->op(path, matrix, devBounds, op, doAA);
373     }
374 
375     matrix.mapRect(&devRect, localRect);
376 
377     if (fIsBW && doAA) {
378         // check that the rect really needs aa, or is it close enought to
379         // integer boundaries that we can just treat it as a BW rect?
380         if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
381             nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
382             doAA = false;
383         }
384     }
385 
386     if (fIsBW && !doAA) {
387         SkIRect ir;
388         devRect.round(&ir);
389         this->applyClipRestriction(op, &ir);
390         (void)fBW.op(ir, op);
391     } else {
392         if (fIsBW) {
393             this->convertToAA();
394         }
395         this->applyClipRestriction(op, &devRect);
396         (void)fAA.op(devRect, op, doAA);
397     }
398     return this->updateCacheAndReturnNonEmpty();
399 }
400 
translate(int dx,int dy,SkRasterClip * dst) const401 void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
402     if (nullptr == dst) {
403         return;
404     }
405 
406     AUTO_RASTERCLIP_VALIDATE(*this);
407 
408     if (this->isEmpty()) {
409         dst->setEmpty();
410         return;
411     }
412     if (0 == (dx | dy)) {
413         *dst = *this;
414         return;
415     }
416 
417     dst->fIsBW = fIsBW;
418     if (fIsBW) {
419         fBW.translate(dx, dy, &dst->fBW);
420         dst->fAA.setEmpty();
421     } else {
422         fAA.translate(dx, dy, &dst->fAA);
423         dst->fBW.setEmpty();
424     }
425     dst->updateCacheAndReturnNonEmpty();
426 }
427 
quickContains(const SkIRect & ir) const428 bool SkRasterClip::quickContains(const SkIRect& ir) const {
429     return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
430 }
431 
432 ///////////////////////////////////////////////////////////////////////////////
433 
forceGetBW()434 const SkRegion& SkRasterClip::forceGetBW() {
435     AUTO_RASTERCLIP_VALIDATE(*this);
436 
437     if (!fIsBW) {
438         fBW.setRect(fAA.getBounds());
439     }
440     return fBW;
441 }
442 
convertToAA()443 void SkRasterClip::convertToAA() {
444     AUTO_RASTERCLIP_VALIDATE(*this);
445 
446     SkASSERT(fIsBW);
447     fAA.setRegion(fBW);
448     fIsBW = false;
449 
450     // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
451     // ourselves back to BW.
452     (void)this->updateCacheAndReturnNonEmpty(false);
453 }
454 
455 #ifdef SK_DEBUG
validate() const456 void SkRasterClip::validate() const {
457     // can't ever assert that fBW is empty, since we may have called forceGetBW
458     if (fIsBW) {
459         SkASSERT(fAA.isEmpty());
460     }
461 
462     fBW.validate();
463     fAA.validate();
464 
465     SkASSERT(this->computeIsEmpty() == fIsEmpty);
466     SkASSERT(this->computeIsRect() == fIsRect);
467 }
468 #endif
469 
470 ///////////////////////////////////////////////////////////////////////////////
471 
SkAAClipBlitterWrapper()472 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
473     SkDEBUGCODE(fClipRgn = nullptr;)
474     SkDEBUGCODE(fBlitter = nullptr;)
475 }
476 
SkAAClipBlitterWrapper(const SkRasterClip & clip,SkBlitter * blitter)477 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
478                                                SkBlitter* blitter) {
479     this->init(clip, blitter);
480 }
481 
SkAAClipBlitterWrapper(const SkAAClip * aaclip,SkBlitter * blitter)482 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
483                                                SkBlitter* blitter) {
484     SkASSERT(blitter);
485     SkASSERT(aaclip);
486     fBWRgn.setRect(aaclip->getBounds());
487     fAABlitter.init(blitter, aaclip);
488     // now our return values
489     fClipRgn = &fBWRgn;
490     fBlitter = &fAABlitter;
491 }
492 
init(const SkRasterClip & clip,SkBlitter * blitter)493 void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
494     SkASSERT(blitter);
495     if (clip.isBW()) {
496         fClipRgn = &clip.bwRgn();
497         fBlitter = blitter;
498     } else {
499         const SkAAClip& aaclip = clip.aaRgn();
500         fBWRgn.setRect(aaclip.getBounds());
501         fAABlitter.init(blitter, &aaclip);
502         // now our return values
503         fClipRgn = &fBWRgn;
504         fBlitter = &fAABlitter;
505     }
506 }
507