Home | History | Annotate | Download | only in core
      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 
     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 
     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 
     35 SkRasterClip::SkRasterClip(bool forceConservativeRects) {
     36     fForceConservativeRects = forceConservativeRects;
     37     fIsBW = true;
     38     fIsEmpty = true;
     39     fIsRect = false;
     40     SkDEBUGCODE(this->validate();)
     41 }
     42 
     43 SkRasterClip::~SkRasterClip() {
     44     SkDEBUGCODE(this->validate();)
     45 }
     46 
     47 bool SkRasterClip::isComplex() const {
     48     return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
     49 }
     50 
     51 const SkIRect& SkRasterClip::getBounds() const {
     52     return fIsBW ? fBW.getBounds() : fAA.getBounds();
     53 }
     54 
     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 
     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 
     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 
     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 
    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 
    164 bool 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 
    212 bool 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 
    218 bool 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 
    225 bool 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 
    238 bool 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  */
    267 static 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 
    275 bool 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 
    315 void 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 
    342 bool SkRasterClip::quickContains(const SkIRect& ir) const {
    343     return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
    344 }
    345 
    346 ///////////////////////////////////////////////////////////////////////////////
    347 
    348 const SkRegion& SkRasterClip::forceGetBW() {
    349     AUTO_RASTERCLIP_VALIDATE(*this);
    350 
    351     if (!fIsBW) {
    352         fBW.setRect(fAA.getBounds());
    353     }
    354     return fBW;
    355 }
    356 
    357 void 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
    372 void 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 
    388 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
    389     SkDEBUGCODE(fClipRgn = NULL;)
    390     SkDEBUGCODE(fBlitter = NULL;)
    391 }
    392 
    393 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
    394                                                SkBlitter* blitter) {
    395     this->init(clip, blitter);
    396 }
    397 
    398 SkAAClipBlitterWrapper::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 
    409 void 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