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