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     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 
     95 enum MutateResult {
     96     kDoNothing_MutateResult,
     97     kReplaceClippedAgainstGlobalBounds_MutateResult,
     98     kContinue_MutateResult,
     99 };
    100 
    101 static 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 
    147 bool 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 
    167 bool 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 
    215 bool 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 
    221 bool 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 
    228 bool 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 
    241 bool 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  */
    270 static 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 
    278 bool 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 
    318 void 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 
    345 bool SkRasterClip::quickContains(const SkIRect& ir) const {
    346     return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
    347 }
    348 
    349 ///////////////////////////////////////////////////////////////////////////////
    350 
    351 const SkRegion& SkRasterClip::forceGetBW() {
    352     AUTO_RASTERCLIP_VALIDATE(*this);
    353 
    354     if (!fIsBW) {
    355         fBW.setRect(fAA.getBounds());
    356     }
    357     return fBW;
    358 }
    359 
    360 void 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
    375 void 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 
    391 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
    392     SkDEBUGCODE(fClipRgn = NULL;)
    393     SkDEBUGCODE(fBlitter = NULL;)
    394 }
    395 
    396 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
    397                                                SkBlitter* blitter) {
    398     this->init(clip, blitter);
    399 }
    400 
    401 SkAAClipBlitterWrapper::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 
    412 void 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