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