SkRasterClip.cpp revision d954498c01ccf0417feacf89e45d0c62a06a813b
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 11SkRasterClip::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 27SkRasterClip::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 35SkRasterClip::SkRasterClip(bool forceConservativeRects) { 36 fForceConservativeRects = forceConservativeRects; 37 fIsBW = true; 38 fIsEmpty = true; 39 fIsRect = false; 40 SkDEBUGCODE(this->validate();) 41} 42 43SkRasterClip::~SkRasterClip() { 44 SkDEBUGCODE(this->validate();) 45} 46 47bool SkRasterClip::isComplex() const { 48 return fIsBW ? fBW.isComplex() : !fAA.isEmpty(); 49} 50 51const SkIRect& SkRasterClip::getBounds() const { 52 return fIsBW ? fBW.getBounds() : fAA.getBounds(); 53} 54 55bool 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 66bool 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 78bool 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 95enum MutateResult { 96 kDoNothing_MutateResult, 97 kReplaceClippedAgainstGlobalBounds_MutateResult, 98 kContinue_MutateResult, 99}; 100 101static 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 147bool 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 167bool 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 215bool 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 221bool 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 228bool 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 241bool 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 */ 270static 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 278bool 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 318void 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 345bool SkRasterClip::quickContains(const SkIRect& ir) const { 346 return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir); 347} 348 349/////////////////////////////////////////////////////////////////////////////// 350 351const SkRegion& SkRasterClip::forceGetBW() { 352 AUTO_RASTERCLIP_VALIDATE(*this); 353 354 if (!fIsBW) { 355 fBW.setRect(fAA.getBounds()); 356 } 357 return fBW; 358} 359 360void 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 375void 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 391SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() { 392 SkDEBUGCODE(fClipRgn = NULL;) 393 SkDEBUGCODE(fBlitter = NULL;) 394} 395 396SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip, 397 SkBlitter* blitter) { 398 this->init(clip, blitter); 399} 400 401SkAAClipBlitterWrapper::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 412void 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