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