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