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
11SkRasterClip::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
27SkRasterClip::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
35SkRasterClip::SkRasterClip(bool forceConservativeRects) {
36    fForceConservativeRects = forceConservativeRects;
37    fIsBW = true;
38    fIsEmpty = true;
39    fIsRect = false;
40    SkDEBUGCODE(this->validate();)
41}
42
43SkRasterClip::~SkRasterClip() {
44    SkDEBUGCODE(this->validate();)
45}
46
47bool SkRasterClip::isComplex() const {
48    return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
49}
50
51const SkIRect& SkRasterClip::getBounds() const {
52    return fIsBW ? fBW.getBounds() : fAA.getBounds();
53}
54
55bool 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
66bool 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
78bool 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
92enum MutateResult {
93    kDoNothing_MutateResult,
94    kReplaceClippedAgainstGlobalBounds_MutateResult,
95    kContinue_MutateResult,
96};
97
98static 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
144bool 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
164bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) {
165    // base is used to limit the size (and therefore memory allocation) of the
166    // region that results from scan converting devPath.
167    SkRegion base;
168
169    if (fForceConservativeRects) {
170        SkIRect ir;
171        switch (mutate_conservative_op(&op, path.isInverseFillType())) {
172            case kDoNothing_MutateResult:
173                return !this->isEmpty();
174            case kReplaceClippedAgainstGlobalBounds_MutateResult:
175                ir = SkIRect::MakeSize(size);
176                break;
177            case kContinue_MutateResult:
178                ir = path.getBounds().roundOut();
179                break;
180        }
181        return this->op(ir, op);
182    }
183
184    if (SkRegion::kIntersect_Op == op) {
185        // since we are intersect, we can do better (tighter) with currRgn's
186        // bounds, than just using the device. However, if currRgn is complex,
187        // our region blitter may hork, so we do that case in two steps.
188        if (this->isRect()) {
189            // FIXME: we should also be able to do this when this->isBW(),
190            // but relaxing the test above triggers GM asserts in
191            // SkRgnBuilder::blitH(). We need to investigate what's going on.
192            return this->setPath(path, this->bwRgn(), doAA);
193        } else {
194            base.setRect(this->getBounds());
195            SkRasterClip clip(fForceConservativeRects);
196            clip.setPath(path, base, doAA);
197            return this->op(clip, op);
198        }
199    } else {
200        base.setRect(0, 0, size.width(), size.height());
201
202        if (SkRegion::kReplace_Op == op) {
203            return this->setPath(path, base, doAA);
204        } else {
205            SkRasterClip clip(fForceConservativeRects);
206            clip.setPath(path, base, doAA);
207            return this->op(clip, op);
208        }
209    }
210}
211
212bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
213    SkRegion tmp;
214    tmp.setRect(clip);
215    return this->setPath(path, tmp, doAA);
216}
217
218bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
219    AUTO_RASTERCLIP_VALIDATE(*this);
220
221    fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
222    return this->updateCacheAndReturnNonEmpty();
223}
224
225bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
226    AUTO_RASTERCLIP_VALIDATE(*this);
227
228    if (fIsBW) {
229        (void)fBW.op(rgn, op);
230    } else {
231        SkAAClip tmp;
232        tmp.setRegion(rgn);
233        (void)fAA.op(tmp, op);
234    }
235    return this->updateCacheAndReturnNonEmpty();
236}
237
238bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
239    AUTO_RASTERCLIP_VALIDATE(*this);
240    clip.validate();
241
242    if (this->isBW() && clip.isBW()) {
243        (void)fBW.op(clip.fBW, op);
244    } else {
245        SkAAClip tmp;
246        const SkAAClip* other;
247
248        if (this->isBW()) {
249            this->convertToAA();
250        }
251        if (clip.isBW()) {
252            tmp.setRegion(clip.bwRgn());
253            other = &tmp;
254        } else {
255            other = &clip.aaRgn();
256        }
257        (void)fAA.op(*other, op);
258    }
259    return this->updateCacheAndReturnNonEmpty();
260}
261
262/**
263 *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
264 *  axis. Thus we can treat an axis coordinate as an integer if it differs
265 *  from its nearest int by < half of that value (1.8 in this case).
266 */
267static bool nearly_integral(SkScalar x) {
268    static const SkScalar domain = SK_Scalar1 / 4;
269    static const SkScalar halfDomain = domain / 2;
270
271    x += halfDomain;
272    return x - SkScalarFloorToScalar(x) < domain;
273}
274
275bool SkRasterClip::op(const SkRect& r, const SkISize& size, SkRegion::Op op, bool doAA) {
276    AUTO_RASTERCLIP_VALIDATE(*this);
277
278    if (fForceConservativeRects) {
279        SkIRect ir;
280        switch (mutate_conservative_op(&op, false)) {
281            case kDoNothing_MutateResult:
282                return !this->isEmpty();
283            case kReplaceClippedAgainstGlobalBounds_MutateResult:
284                ir = SkIRect::MakeSize(size);
285                break;
286            case kContinue_MutateResult:
287                ir = r.roundOut();
288                break;
289        }
290        return this->op(ir, op);
291    }
292
293    if (fIsBW && doAA) {
294        // check that the rect really needs aa, or is it close enought to
295        // integer boundaries that we can just treat it as a BW rect?
296        if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) &&
297            nearly_integral(r.fRight) && nearly_integral(r.fBottom)) {
298            doAA = false;
299        }
300    }
301
302    if (fIsBW && !doAA) {
303        SkIRect ir;
304        r.round(&ir);
305        (void)fBW.op(ir, op);
306    } else {
307        if (fIsBW) {
308            this->convertToAA();
309        }
310        (void)fAA.op(r, op, doAA);
311    }
312    return this->updateCacheAndReturnNonEmpty();
313}
314
315void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
316    if (NULL == dst) {
317        return;
318    }
319
320    AUTO_RASTERCLIP_VALIDATE(*this);
321
322    if (this->isEmpty()) {
323        dst->setEmpty();
324        return;
325    }
326    if (0 == (dx | dy)) {
327        *dst = *this;
328        return;
329    }
330
331    dst->fIsBW = fIsBW;
332    if (fIsBW) {
333        fBW.translate(dx, dy, &dst->fBW);
334        dst->fAA.setEmpty();
335    } else {
336        fAA.translate(dx, dy, &dst->fAA);
337        dst->fBW.setEmpty();
338    }
339    dst->updateCacheAndReturnNonEmpty();
340}
341
342bool SkRasterClip::quickContains(const SkIRect& ir) const {
343    return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
344}
345
346///////////////////////////////////////////////////////////////////////////////
347
348const SkRegion& SkRasterClip::forceGetBW() {
349    AUTO_RASTERCLIP_VALIDATE(*this);
350
351    if (!fIsBW) {
352        fBW.setRect(fAA.getBounds());
353    }
354    return fBW;
355}
356
357void SkRasterClip::convertToAA() {
358    AUTO_RASTERCLIP_VALIDATE(*this);
359
360    SkASSERT(!fForceConservativeRects);
361
362    SkASSERT(fIsBW);
363    fAA.setRegion(fBW);
364    fIsBW = false;
365
366    // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
367    // ourselves back to BW.
368    (void)this->updateCacheAndReturnNonEmpty(false);
369}
370
371#ifdef SK_DEBUG
372void SkRasterClip::validate() const {
373    // can't ever assert that fBW is empty, since we may have called forceGetBW
374    if (fIsBW) {
375        SkASSERT(fAA.isEmpty());
376    }
377
378    fBW.validate();
379    fAA.validate();
380
381    SkASSERT(this->computeIsEmpty() == fIsEmpty);
382    SkASSERT(this->computeIsRect() == fIsRect);
383}
384#endif
385
386///////////////////////////////////////////////////////////////////////////////
387
388SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
389    SkDEBUGCODE(fClipRgn = NULL;)
390    SkDEBUGCODE(fBlitter = NULL;)
391}
392
393SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
394                                               SkBlitter* blitter) {
395    this->init(clip, blitter);
396}
397
398SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
399                                               SkBlitter* blitter) {
400    SkASSERT(blitter);
401    SkASSERT(aaclip);
402    fBWRgn.setRect(aaclip->getBounds());
403    fAABlitter.init(blitter, aaclip);
404    // now our return values
405    fClipRgn = &fBWRgn;
406    fBlitter = &fAABlitter;
407}
408
409void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
410    SkASSERT(blitter);
411    if (clip.isBW()) {
412        fClipRgn = &clip.bwRgn();
413        fBlitter = blitter;
414    } else {
415        const SkAAClip& aaclip = clip.aaRgn();
416        fBWRgn.setRect(aaclip.getBounds());
417        fAABlitter.init(blitter, &aaclip);
418        // now our return values
419        fClipRgn = &fBWRgn;
420        fBlitter = &fAABlitter;
421    }
422}
423