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