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