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