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
11enum MutateResult {
12    kDoNothing_MutateResult,
13    kReplaceClippedAgainstGlobalBounds_MutateResult,
14    kContinue_MutateResult,
15};
16
17static 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
63void 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
81void 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
86void 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
105void SkConservativeClip::opRegion(const SkRegion& rgn, SkRegion::Op op) {
106    this->opIRect(rgn.getBounds(), op);
107}
108
109void 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
127SkRasterClip::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
143SkRasterClip::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
150SkRasterClip::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
157SkRasterClip::SkRasterClip() {
158    fIsBW = true;
159    fIsEmpty = true;
160    fIsRect = false;
161    SkDEBUGCODE(this->validate();)
162}
163
164SkRasterClip::~SkRasterClip() {
165    SkDEBUGCODE(this->validate();)
166}
167
168bool 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
182bool SkRasterClip::isComplex() const {
183    return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
184}
185
186const SkIRect& SkRasterClip::getBounds() const {
187    return fIsBW ? fBW.getBounds() : fAA.getBounds();
188}
189
190bool 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
201bool 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
213bool 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
227bool 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
243bool 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
254bool 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
299bool 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
305bool 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
312bool 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
325bool 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 */
354static 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
362bool 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
401void 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
428bool SkRasterClip::quickContains(const SkIRect& ir) const {
429    return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
430}
431
432///////////////////////////////////////////////////////////////////////////////
433
434const SkRegion& SkRasterClip::forceGetBW() {
435    AUTO_RASTERCLIP_VALIDATE(*this);
436
437    if (!fIsBW) {
438        fBW.setRect(fAA.getBounds());
439    }
440    return fBW;
441}
442
443void 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
456void 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
472SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
473    SkDEBUGCODE(fClipRgn = nullptr;)
474    SkDEBUGCODE(fBlitter = nullptr;)
475}
476
477SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
478                                               SkBlitter* blitter) {
479    this->init(clip, blitter);
480}
481
482SkAAClipBlitterWrapper::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
493void 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