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