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 #include "SkRegionPriv.h"
     11 
     12 enum MutateResult {
     13     kDoNothing_MutateResult,
     14     kReplaceClippedAgainstGlobalBounds_MutateResult,
     15     kContinue_MutateResult,
     16 };
     17 
     18 static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
     19     if (inverseFilled) {
     20         switch (*op) {
     21             case SkRegion::kIntersect_Op:
     22             case SkRegion::kDifference_Op:
     23                 // These ops can only shrink the current clip. So leaving
     24                 // the clip unchanged conservatively respects the contract.
     25                 return kDoNothing_MutateResult;
     26             case SkRegion::kUnion_Op:
     27             case SkRegion::kReplace_Op:
     28             case SkRegion::kReverseDifference_Op:
     29             case SkRegion::kXOR_Op: {
     30                 // These ops can grow the current clip up to the extents of
     31                 // the input clip, which is inverse filled, so we just set
     32                 // the current clip to the device bounds.
     33                 *op = SkRegion::kReplace_Op;
     34                 return kReplaceClippedAgainstGlobalBounds_MutateResult;
     35             }
     36         }
     37     } else {
     38         // Not inverse filled
     39         switch (*op) {
     40             case SkRegion::kIntersect_Op:
     41             case SkRegion::kUnion_Op:
     42             case SkRegion::kReplace_Op:
     43                 return kContinue_MutateResult;
     44             case SkRegion::kDifference_Op:
     45                 // Difference can only shrink the current clip.
     46                 // Leaving clip unchanged conservatively fullfills the contract.
     47                 return kDoNothing_MutateResult;
     48             case SkRegion::kReverseDifference_Op:
     49                 // To reverse, we swap in the bounds with a replace op.
     50                 // As with difference, leave it unchanged.
     51                 *op = SkRegion::kReplace_Op;
     52                 return kContinue_MutateResult;
     53             case SkRegion::kXOR_Op:
     54                 // Be conservative, based on (A XOR B) always included in (A union B),
     55                 // which is always included in (bounds(A) union bounds(B))
     56                 *op = SkRegion::kUnion_Op;
     57                 return kContinue_MutateResult;
     58         }
     59     }
     60     SkASSERT(false);    // unknown op
     61     return kDoNothing_MutateResult;
     62 }
     63 
     64 void SkConservativeClip::opRect(const SkRect& localRect, const SkMatrix& ctm,
     65                                 const SkIRect& devBounds, SkRegion::Op op, bool doAA) {
     66     SkIRect ir;
     67     switch (mutate_conservative_op(&op, false)) {
     68         case kDoNothing_MutateResult:
     69             return;
     70         case kReplaceClippedAgainstGlobalBounds_MutateResult:
     71             ir = devBounds;
     72             break;
     73         case kContinue_MutateResult: {
     74             SkRect devRect;
     75             ctm.mapRect(&devRect, localRect);
     76             ir = doAA ? devRect.roundOut() : devRect.round();
     77         } break;
     78     }
     79     this->opIRect(ir, op);
     80 }
     81 
     82 void SkConservativeClip::opRRect(const SkRRect& rrect, const SkMatrix& ctm,
     83                                  const SkIRect& devBounds, SkRegion::Op op, bool doAA) {
     84     this->opRect(rrect.getBounds(), ctm, devBounds, op, doAA);
     85 }
     86 
     87 void SkConservativeClip::opPath(const SkPath& path, const SkMatrix& ctm, const SkIRect& devBounds,
     88                                 SkRegion::Op op, bool doAA) {
     89     SkIRect ir;
     90     switch (mutate_conservative_op(&op, path.isInverseFillType())) {
     91         case kDoNothing_MutateResult:
     92             return;
     93         case kReplaceClippedAgainstGlobalBounds_MutateResult:
     94             ir = devBounds;
     95             break;
     96         case kContinue_MutateResult: {
     97             SkRect bounds = path.getBounds();
     98             ctm.mapRect(&bounds);
     99             ir = bounds.roundOut();
    100             break;
    101         }
    102     }
    103     return this->opIRect(ir, op);
    104 }
    105 
    106 void SkConservativeClip::opRegion(const SkRegion& rgn, SkRegion::Op op) {
    107     this->opIRect(rgn.getBounds(), op);
    108 }
    109 
    110 void SkConservativeClip::opIRect(const SkIRect& devRect, SkRegion::Op op) {
    111     if (SkRegion::kIntersect_Op == op) {
    112         if (!fBounds.intersect(devRect)) {
    113             fBounds.setEmpty();
    114         }
    115         return;
    116     }
    117 
    118     // This may still create a complex region (which we would then take the bounds
    119     // Perhaps we should inline the op-logic directly to never create the rgn...
    120     SkRegion result;
    121     result.op(SkRegion(fBounds), SkRegion(devRect), op);
    122     fBounds = result.getBounds();
    123     this->applyClipRestriction(op, &fBounds);
    124 }
    125 
    126 ///////////////////////////////////////////////////////////////////////////////////////////////////
    127 
    128 SkRasterClip::SkRasterClip(const SkRasterClip& src) {
    129     AUTO_RASTERCLIP_VALIDATE(src);
    130 
    131     fIsBW = src.fIsBW;
    132     if (fIsBW) {
    133         fBW = src.fBW;
    134     } else {
    135         fAA = src.fAA;
    136     }
    137 
    138     fIsEmpty = src.isEmpty();
    139     fIsRect = src.isRect();
    140     fClipRestrictionRect = src.fClipRestrictionRect;
    141     SkDEBUGCODE(this->validate();)
    142 }
    143 
    144 SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
    145     fIsBW = true;
    146     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
    147     fIsRect = !fIsEmpty;
    148     SkDEBUGCODE(this->validate();)
    149 }
    150 
    151 SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
    152     fIsBW = true;
    153     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
    154     fIsRect = !fIsEmpty;
    155     SkDEBUGCODE(this->validate();)
    156 }
    157 
    158 SkRasterClip::SkRasterClip() {
    159     fIsBW = true;
    160     fIsEmpty = true;
    161     fIsRect = false;
    162     SkDEBUGCODE(this->validate();)
    163 }
    164 
    165 SkRasterClip::~SkRasterClip() {
    166     SkDEBUGCODE(this->validate();)
    167 }
    168 
    169 bool SkRasterClip::operator==(const SkRasterClip& other) const {
    170     if (fIsBW != other.fIsBW) {
    171         return false;
    172     }
    173     bool isEqual = fIsBW ? fBW == other.fBW : fAA == other.fAA;
    174 #ifdef SK_DEBUG
    175     if (isEqual) {
    176         SkASSERT(fIsEmpty == other.fIsEmpty);
    177         SkASSERT(fIsRect == other.fIsRect);
    178     }
    179 #endif
    180     return isEqual;
    181 }
    182 
    183 bool SkRasterClip::isComplex() const {
    184     return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
    185 }
    186 
    187 const SkIRect& SkRasterClip::getBounds() const {
    188     return fIsBW ? fBW.getBounds() : fAA.getBounds();
    189 }
    190 
    191 bool SkRasterClip::setEmpty() {
    192     AUTO_RASTERCLIP_VALIDATE(*this);
    193 
    194     fIsBW = true;
    195     fBW.setEmpty();
    196     fAA.setEmpty();
    197     fIsEmpty = true;
    198     fIsRect = false;
    199     return false;
    200 }
    201 
    202 bool SkRasterClip::setRect(const SkIRect& rect) {
    203     AUTO_RASTERCLIP_VALIDATE(*this);
    204 
    205     fIsBW = true;
    206     fAA.setEmpty();
    207     fIsRect = fBW.setRect(rect);
    208     fIsEmpty = !fIsRect;
    209     return fIsRect;
    210 }
    211 
    212 /////////////////////////////////////////////////////////////////////////////////////
    213 
    214 bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
    215     SkRegion::Op op;
    216     if (isInverse) {
    217         op = SkRegion::kDifference_Op;
    218     } else {
    219         op = SkRegion::kIntersect_Op;
    220     }
    221     fBW.setRect(clipR);
    222     fBW.op(r.roundOut(), op);
    223     return this->updateCacheAndReturnNonEmpty();
    224 }
    225 
    226 /////////////////////////////////////////////////////////////////////////////////////
    227 
    228 bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
    229     AUTO_RASTERCLIP_VALIDATE(*this);
    230 
    231     if (this->isBW() && !doAA) {
    232         (void)fBW.setPath(path, clip);
    233     } else {
    234         // TODO: since we are going to over-write fAA completely (aren't we?)
    235         // we should just clear our BW data (if any) and set fIsAA=true
    236         if (this->isBW()) {
    237             this->convertToAA();
    238         }
    239         (void)fAA.setPath(path, &clip, doAA);
    240     }
    241     return this->updateCacheAndReturnNonEmpty();
    242 }
    243 
    244 bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
    245                       SkRegion::Op op, bool doAA) {
    246     SkIRect bounds(devBounds);
    247     this->applyClipRestriction(op, &bounds);
    248 
    249     SkPath path;
    250     path.addRRect(rrect);
    251 
    252     return this->op(path, matrix, bounds, op, doAA);
    253 }
    254 
    255 bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
    256                       SkRegion::Op op, bool doAA) {
    257     AUTO_RASTERCLIP_VALIDATE(*this);
    258     SkIRect bounds(devBounds);
    259     this->applyClipRestriction(op, &bounds);
    260 
    261     // base is used to limit the size (and therefore memory allocation) of the
    262     // region that results from scan converting devPath.
    263     SkRegion base;
    264 
    265     SkPath devPath;
    266     if (matrix.isIdentity()) {
    267         devPath = path;
    268     } else {
    269         path.transform(matrix, &devPath);
    270         devPath.setIsVolatile(true);
    271     }
    272     if (SkRegion::kIntersect_Op == op) {
    273         // since we are intersect, we can do better (tighter) with currRgn's
    274         // bounds, than just using the device. However, if currRgn is complex,
    275         // our region blitter may hork, so we do that case in two steps.
    276         if (this->isRect()) {
    277             // FIXME: we should also be able to do this when this->isBW(),
    278             // but relaxing the test above triggers GM asserts in
    279             // SkRgnBuilder::blitH(). We need to investigate what's going on.
    280             return this->setPath(devPath, this->bwRgn(), doAA);
    281         } else {
    282             base.setRect(this->getBounds());
    283             SkRasterClip clip;
    284             clip.setPath(devPath, base, doAA);
    285             return this->op(clip, op);
    286         }
    287     } else {
    288         base.setRect(bounds);
    289 
    290         if (SkRegion::kReplace_Op == op) {
    291             return this->setPath(devPath, base, doAA);
    292         } else {
    293             SkRasterClip clip;
    294             clip.setPath(devPath, base, doAA);
    295             return this->op(clip, op);
    296         }
    297     }
    298 }
    299 
    300 bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
    301     SkRegion tmp;
    302     tmp.setRect(clip);
    303     return this->setPath(path, tmp, doAA);
    304 }
    305 
    306 bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
    307     AUTO_RASTERCLIP_VALIDATE(*this);
    308 
    309     fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
    310     return this->updateCacheAndReturnNonEmpty();
    311 }
    312 
    313 bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
    314     AUTO_RASTERCLIP_VALIDATE(*this);
    315 
    316     if (fIsBW) {
    317         (void)fBW.op(rgn, op);
    318     } else {
    319         SkAAClip tmp;
    320         tmp.setRegion(rgn);
    321         (void)fAA.op(tmp, op);
    322     }
    323     return this->updateCacheAndReturnNonEmpty();
    324 }
    325 
    326 bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
    327     AUTO_RASTERCLIP_VALIDATE(*this);
    328     clip.validate();
    329 
    330     if (this->isBW() && clip.isBW()) {
    331         (void)fBW.op(clip.fBW, op);
    332     } else {
    333         SkAAClip tmp;
    334         const SkAAClip* other;
    335 
    336         if (this->isBW()) {
    337             this->convertToAA();
    338         }
    339         if (clip.isBW()) {
    340             tmp.setRegion(clip.bwRgn());
    341             other = &tmp;
    342         } else {
    343             other = &clip.aaRgn();
    344         }
    345         (void)fAA.op(*other, op);
    346     }
    347     return this->updateCacheAndReturnNonEmpty();
    348 }
    349 
    350 /**
    351  *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
    352  *  axis. Thus we can treat an axis coordinate as an integer if it differs
    353  *  from its nearest int by < half of that value (1.8 in this case).
    354  */
    355 static bool nearly_integral(SkScalar x) {
    356     static const SkScalar domain = SK_Scalar1 / 4;
    357     static const SkScalar halfDomain = domain / 2;
    358 
    359     x += halfDomain;
    360     return x - SkScalarFloorToScalar(x) < domain;
    361 }
    362 
    363 bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
    364                       SkRegion::Op op, bool doAA) {
    365     AUTO_RASTERCLIP_VALIDATE(*this);
    366     SkRect devRect;
    367 
    368     const bool isScaleTrans = matrix.isScaleTranslate();
    369     if (!isScaleTrans) {
    370         SkPath path;
    371         path.addRect(localRect);
    372         path.setIsVolatile(true);
    373         return this->op(path, matrix, devBounds, op, doAA);
    374     }
    375 
    376     matrix.mapRect(&devRect, localRect);
    377 
    378     if (fIsBW && doAA) {
    379         // check that the rect really needs aa, or is it close enought to
    380         // integer boundaries that we can just treat it as a BW rect?
    381         if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
    382             nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
    383             doAA = false;
    384         }
    385     }
    386 
    387     if (fIsBW && !doAA) {
    388         SkIRect ir;
    389         devRect.round(&ir);
    390         this->applyClipRestriction(op, &ir);
    391         (void)fBW.op(ir, op);
    392     } else {
    393         if (fIsBW) {
    394             this->convertToAA();
    395         }
    396         this->applyClipRestriction(op, &devRect);
    397         (void)fAA.op(devRect, op, doAA);
    398     }
    399     return this->updateCacheAndReturnNonEmpty();
    400 }
    401 
    402 void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
    403     if (nullptr == dst) {
    404         return;
    405     }
    406 
    407     AUTO_RASTERCLIP_VALIDATE(*this);
    408 
    409     if (this->isEmpty()) {
    410         dst->setEmpty();
    411         return;
    412     }
    413     if (0 == (dx | dy)) {
    414         *dst = *this;
    415         return;
    416     }
    417 
    418     dst->fIsBW = fIsBW;
    419     if (fIsBW) {
    420         fBW.translate(dx, dy, &dst->fBW);
    421         dst->fAA.setEmpty();
    422     } else {
    423         fAA.translate(dx, dy, &dst->fAA);
    424         dst->fBW.setEmpty();
    425     }
    426     dst->updateCacheAndReturnNonEmpty();
    427 }
    428 
    429 bool SkRasterClip::quickContains(const SkIRect& ir) const {
    430     return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
    431 }
    432 
    433 ///////////////////////////////////////////////////////////////////////////////
    434 
    435 const SkRegion& SkRasterClip::forceGetBW() {
    436     AUTO_RASTERCLIP_VALIDATE(*this);
    437 
    438     if (!fIsBW) {
    439         fBW.setRect(fAA.getBounds());
    440     }
    441     return fBW;
    442 }
    443 
    444 void SkRasterClip::convertToAA() {
    445     AUTO_RASTERCLIP_VALIDATE(*this);
    446 
    447     SkASSERT(fIsBW);
    448     fAA.setRegion(fBW);
    449     fIsBW = false;
    450 
    451     // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
    452     // ourselves back to BW.
    453     (void)this->updateCacheAndReturnNonEmpty(false);
    454 }
    455 
    456 #ifdef SK_DEBUG
    457 void SkRasterClip::validate() const {
    458     // can't ever assert that fBW is empty, since we may have called forceGetBW
    459     if (fIsBW) {
    460         SkASSERT(fAA.isEmpty());
    461     }
    462 
    463     SkRegionPriv::Validate(fBW);
    464     fAA.validate();
    465 
    466     SkASSERT(this->computeIsEmpty() == fIsEmpty);
    467     SkASSERT(this->computeIsRect() == fIsRect);
    468 }
    469 #endif
    470 
    471 ///////////////////////////////////////////////////////////////////////////////
    472 
    473 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
    474     SkDEBUGCODE(fClipRgn = nullptr;)
    475     SkDEBUGCODE(fBlitter = nullptr;)
    476 }
    477 
    478 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
    479                                                SkBlitter* blitter) {
    480     this->init(clip, blitter);
    481 }
    482 
    483 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
    484                                                SkBlitter* blitter) {
    485     SkASSERT(blitter);
    486     SkASSERT(aaclip);
    487     fBWRgn.setRect(aaclip->getBounds());
    488     fAABlitter.init(blitter, aaclip);
    489     // now our return values
    490     fClipRgn = &fBWRgn;
    491     fBlitter = &fAABlitter;
    492 }
    493 
    494 void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
    495     SkASSERT(blitter);
    496     if (clip.isBW()) {
    497         fClipRgn = &clip.bwRgn();
    498         fBlitter = blitter;
    499     } else {
    500         const SkAAClip& aaclip = clip.aaRgn();
    501         fBWRgn.setRect(aaclip.getBounds());
    502         fAABlitter.init(blitter, &aaclip);
    503         // now our return values
    504         fClipRgn = &fBWRgn;
    505         fBlitter = &fAABlitter;
    506     }
    507 }
    508