1 /* 2 * Copyright 2013 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 "SkBitmapDevice.h" 9 #include "SkDraw.h" 10 #include "SkImageFilter.h" 11 #include "SkImageFilterCache.h" 12 #include "SkMallocPixelRef.h" 13 #include "SkMatrix.h" 14 #include "SkPaint.h" 15 #include "SkPath.h" 16 #include "SkPixelRef.h" 17 #include "SkPixmap.h" 18 #include "SkRasterClip.h" 19 #include "SkRasterHandleAllocator.h" 20 #include "SkShader.h" 21 #include "SkSpecialImage.h" 22 #include "SkSurface.h" 23 #include "SkTLazy.h" 24 #include "SkVertices.h" 25 26 class SkColorTable; 27 28 static bool valid_for_bitmap_device(const SkImageInfo& info, 29 SkAlphaType* newAlphaType) { 30 if (info.width() < 0 || info.height() < 0) { 31 return false; 32 } 33 34 // TODO: can we stop supporting kUnknown in SkBitmkapDevice? 35 if (kUnknown_SkColorType == info.colorType()) { 36 if (newAlphaType) { 37 *newAlphaType = kUnknown_SkAlphaType; 38 } 39 return true; 40 } 41 42 SkAlphaType canonicalAlphaType = info.alphaType(); 43 44 switch (info.colorType()) { 45 case kAlpha_8_SkColorType: 46 case kARGB_4444_SkColorType: 47 case kRGBA_8888_SkColorType: 48 case kBGRA_8888_SkColorType: 49 case kRGBA_1010102_SkColorType: 50 case kRGBA_F16_SkColorType: 51 break; 52 case kGray_8_SkColorType: 53 case kRGB_565_SkColorType: 54 case kRGB_888x_SkColorType: 55 case kRGB_101010x_SkColorType: 56 canonicalAlphaType = kOpaque_SkAlphaType; 57 break; 58 default: 59 return false; 60 } 61 62 if (newAlphaType) { 63 *newAlphaType = canonicalAlphaType; 64 } 65 return true; 66 } 67 68 SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) 69 : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)) 70 , fBitmap(bitmap) 71 , fRCStack(bitmap.width(), bitmap.height()) 72 { 73 SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); 74 } 75 76 SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) { 77 return Create(info, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)); 78 } 79 80 SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, 81 SkRasterHandleAllocator::Handle hndl) 82 : INHERITED(bitmap.info(), surfaceProps) 83 , fBitmap(bitmap) 84 , fRasterHandle(hndl) 85 , fRCStack(bitmap.width(), bitmap.height()) 86 { 87 SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); 88 } 89 90 SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, 91 const SkSurfaceProps& surfaceProps, 92 SkRasterHandleAllocator* allocator) { 93 SkAlphaType newAT = origInfo.alphaType(); 94 if (!valid_for_bitmap_device(origInfo, &newAT)) { 95 return nullptr; 96 } 97 98 SkRasterHandleAllocator::Handle hndl = nullptr; 99 const SkImageInfo info = origInfo.makeAlphaType(newAT); 100 SkBitmap bitmap; 101 102 if (kUnknown_SkColorType == info.colorType()) { 103 if (!bitmap.setInfo(info)) { 104 return nullptr; 105 } 106 } else if (allocator) { 107 hndl = allocator->allocBitmap(info, &bitmap); 108 if (!hndl) { 109 return nullptr; 110 } 111 } else if (info.isOpaque()) { 112 // If this bitmap is opaque, we don't have any sensible default color, 113 // so we just return uninitialized pixels. 114 if (!bitmap.tryAllocPixels(info)) { 115 return nullptr; 116 } 117 } else { 118 // This bitmap has transparency, so we'll zero the pixels (to transparent). 119 // We use the flag as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT). 120 if (!bitmap.tryAllocPixelsFlags(info, SkBitmap::kZeroPixels_AllocFlag)) { 121 return nullptr; 122 } 123 } 124 125 return new SkBitmapDevice(bitmap, surfaceProps, hndl); 126 } 127 128 void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { 129 SkASSERT(bm.width() == fBitmap.width()); 130 SkASSERT(bm.height() == fBitmap.height()); 131 fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config) 132 this->privateResize(fBitmap.info().width(), fBitmap.info().height()); 133 } 134 135 SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) { 136 const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry); 137 return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator); 138 } 139 140 bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) { 141 if (this->onPeekPixels(pmap)) { 142 fBitmap.notifyPixelsChanged(); 143 return true; 144 } 145 return false; 146 } 147 148 bool SkBitmapDevice::onPeekPixels(SkPixmap* pmap) { 149 const SkImageInfo info = fBitmap.info(); 150 if (fBitmap.getPixels() && (kUnknown_SkColorType != info.colorType())) { 151 pmap->reset(fBitmap.info(), fBitmap.getPixels(), fBitmap.rowBytes()); 152 return true; 153 } 154 return false; 155 } 156 157 bool SkBitmapDevice::onWritePixels(const SkPixmap& pm, int x, int y) { 158 // since we don't stop creating un-pixeled devices yet, check for no pixels here 159 if (nullptr == fBitmap.getPixels()) { 160 return false; 161 } 162 163 if (fBitmap.writePixels(pm, x, y)) { 164 fBitmap.notifyPixelsChanged(); 165 return true; 166 } 167 return false; 168 } 169 170 bool SkBitmapDevice::onReadPixels(const SkPixmap& pm, int x, int y) { 171 return fBitmap.readPixels(pm, x, y); 172 } 173 174 /////////////////////////////////////////////////////////////////////////////// 175 176 class SkBitmapDevice::BDDraw : public SkDraw { 177 public: 178 BDDraw(SkBitmapDevice* dev) { 179 // we need fDst to be set, and if we're actually drawing, to dirty the genID 180 if (!dev->accessPixels(&fDst)) { 181 // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels 182 fDst.reset(dev->imageInfo(), nullptr, 0); 183 } 184 fMatrix = &dev->ctm(); 185 fRC = &dev->fRCStack.rc(); 186 } 187 }; 188 189 void SkBitmapDevice::drawPaint(const SkPaint& paint) { 190 BDDraw(this).drawPaint(paint); 191 } 192 193 void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count, 194 const SkPoint pts[], const SkPaint& paint) { 195 BDDraw(this).drawPoints(mode, count, pts, paint, nullptr); 196 } 197 198 void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { 199 BDDraw(this).drawRect(r, paint); 200 } 201 202 void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) { 203 SkPath path; 204 path.addOval(oval); 205 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't 206 // required to override drawOval. 207 this->drawPath(path, paint, nullptr, true); 208 } 209 210 void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { 211 #ifdef SK_IGNORE_BLURRED_RRECT_OPT 212 SkPath path; 213 214 path.addRRect(rrect); 215 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't 216 // required to override drawRRect. 217 this->drawPath(path, paint, nullptr, true); 218 #else 219 BDDraw(this).drawRRect(rrect, paint); 220 #endif 221 } 222 223 void SkBitmapDevice::drawPath(const SkPath& path, 224 const SkPaint& paint, const SkMatrix* prePathMatrix, 225 bool pathIsMutable) { 226 BDDraw(this).drawPath(path, paint, prePathMatrix, pathIsMutable); 227 } 228 229 void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, 230 const SkPaint& paint) { 231 SkMatrix matrix = SkMatrix::MakeTrans(x, y); 232 LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality()); 233 BDDraw(this).drawBitmap(bitmap, matrix, nullptr, paint); 234 } 235 236 static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) { 237 if (!paint.getMaskFilter()) { 238 return true; 239 } 240 241 // Some mask filters parameters (sigma) depend on the CTM/scale. 242 return m.getType() <= SkMatrix::kTranslate_Mask; 243 } 244 245 void SkBitmapDevice::drawBitmapRect(const SkBitmap& bitmap, 246 const SkRect* src, const SkRect& dst, 247 const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { 248 SkMatrix matrix; 249 SkRect bitmapBounds, tmpSrc, tmpDst; 250 SkBitmap tmpBitmap; 251 252 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); 253 254 // Compute matrix from the two rectangles 255 if (src) { 256 tmpSrc = *src; 257 } else { 258 tmpSrc = bitmapBounds; 259 } 260 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 261 262 LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality()); 263 264 const SkRect* dstPtr = &dst; 265 const SkBitmap* bitmapPtr = &bitmap; 266 267 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if 268 // needed (if the src was clipped). No check needed if src==null. 269 if (src) { 270 if (!bitmapBounds.contains(*src)) { 271 if (!tmpSrc.intersect(bitmapBounds)) { 272 return; // nothing to draw 273 } 274 // recompute dst, based on the smaller tmpSrc 275 matrix.mapRect(&tmpDst, tmpSrc); 276 dstPtr = &tmpDst; 277 } 278 } 279 280 if (src && !src->contains(bitmapBounds) && 281 SkCanvas::kFast_SrcRectConstraint == constraint && 282 paint.getFilterQuality() != kNone_SkFilterQuality) { 283 // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know 284 // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap, 285 // but we must use a shader w/ dst bounds (which can access all of the bitmap needed). 286 goto USE_SHADER; 287 } 288 289 if (src) { 290 // since we may need to clamp to the borders of the src rect within 291 // the bitmap, we extract a subset. 292 const SkIRect srcIR = tmpSrc.roundOut(); 293 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { 294 return; 295 } 296 bitmapPtr = &tmpBitmap; 297 298 // Since we did an extract, we need to adjust the matrix accordingly 299 SkScalar dx = 0, dy = 0; 300 if (srcIR.fLeft > 0) { 301 dx = SkIntToScalar(srcIR.fLeft); 302 } 303 if (srcIR.fTop > 0) { 304 dy = SkIntToScalar(srcIR.fTop); 305 } 306 if (dx || dy) { 307 matrix.preTranslate(dx, dy); 308 } 309 310 #ifdef SK_DRAWBITMAPRECT_FAST_OFFSET 311 SkRect extractedBitmapBounds = SkRect::MakeXYWH(dx, dy, 312 SkIntToScalar(bitmapPtr->width()), 313 SkIntToScalar(bitmapPtr->height())); 314 #else 315 SkRect extractedBitmapBounds; 316 extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); 317 #endif 318 if (extractedBitmapBounds == tmpSrc) { 319 // no fractional part in src, we can just call drawBitmap 320 goto USE_DRAWBITMAP; 321 } 322 } else { 323 USE_DRAWBITMAP: 324 // We can go faster by just calling drawBitmap, which will concat the 325 // matrix with the CTM, and try to call drawSprite if it can. If not, 326 // it will make a shader and call drawRect, as we do below. 327 if (CanApplyDstMatrixAsCTM(matrix, paint)) { 328 BDDraw(this).drawBitmap(*bitmapPtr, matrix, dstPtr, paint); 329 return; 330 } 331 } 332 333 USE_SHADER: 334 335 // TODO(herb): Move this over to SkArenaAlloc when arena alloc has a facility to return sk_sps. 336 // Since the shader need only live for our stack-frame, pass in a custom allocator. This 337 // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap 338 // if its mutable, since that precaution is not needed (give the short lifetime of the shader). 339 340 // construct a shader, so we can call drawRect with the dst 341 auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, 342 &matrix, kNever_SkCopyPixelsMode); 343 if (!s) { 344 return; 345 } 346 347 SkPaint paintWithShader(paint); 348 paintWithShader.setStyle(SkPaint::kFill_Style); 349 paintWithShader.setShader(s); 350 351 // Call ourself, in case the subclass wanted to share this setup code 352 // but handle the drawRect code themselves. 353 this->drawRect(*dstPtr, paintWithShader); 354 } 355 356 void SkBitmapDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { 357 BDDraw(this).drawSprite(bitmap, x, y, paint); 358 } 359 360 void SkBitmapDevice::drawText(const void* text, size_t len, 361 SkScalar x, SkScalar y, const SkPaint& paint) { 362 BDDraw(this).drawText((const char*)text, len, x, y, paint, &fSurfaceProps); 363 } 364 365 void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[], 366 int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) { 367 BDDraw(this).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint, 368 &fSurfaceProps); 369 } 370 371 void SkBitmapDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode, 372 const SkPaint& paint) { 373 BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(), 374 vertices->texCoords(), vertices->colors(), bmode, 375 vertices->indices(), vertices->indexCount(), paint); 376 } 377 378 void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) { 379 SkASSERT(!paint.getImageFilter()); 380 BDDraw(this).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint); 381 } 382 383 /////////////////////////////////////////////////////////////////////////////// 384 385 namespace { 386 387 class SkAutoDeviceClipRestore { 388 public: 389 SkAutoDeviceClipRestore(SkBaseDevice* device, const SkIRect& clip) 390 : fDevice(device) 391 , fPrevCTM(device->ctm()) { 392 fDevice->save(); 393 fDevice->setCTM(SkMatrix::I()); 394 fDevice->clipRect(SkRect::Make(clip), SkClipOp::kIntersect, false); 395 fDevice->setCTM(fPrevCTM); 396 } 397 398 ~SkAutoDeviceClipRestore() { 399 fDevice->restore(fPrevCTM); 400 } 401 402 private: 403 SkBaseDevice* fDevice; 404 const SkMatrix fPrevCTM; 405 }; 406 407 } // anonymous ns 408 409 void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint, 410 SkImage* clipImage, const SkMatrix& clipMatrix) { 411 SkASSERT(!src->isTextureBacked()); 412 413 sk_sp<SkSpecialImage> filteredImage; 414 SkTCopyOnFirstWrite<SkPaint> paint(origPaint); 415 416 if (SkImageFilter* filter = paint->getImageFilter()) { 417 SkIPoint offset = SkIPoint::Make(0, 0); 418 const SkMatrix matrix = SkMatrix::Concat( 419 SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm()); 420 const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y); 421 sk_sp<SkImageFilterCache> cache(this->getImageFilterCache()); 422 SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace()); 423 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); 424 425 filteredImage = filter->filterImage(src, ctx, &offset); 426 if (!filteredImage) { 427 return; 428 } 429 430 src = filteredImage.get(); 431 paint.writable()->setImageFilter(nullptr); 432 x += offset.x(); 433 y += offset.y(); 434 } 435 436 if (!clipImage) { 437 SkBitmap resultBM; 438 if (src->getROPixels(&resultBM)) { 439 this->drawSprite(resultBM, x, y, *paint); 440 } 441 return; 442 } 443 444 // Clip image case. 445 sk_sp<SkImage> srcImage(src->asImage()); 446 if (!srcImage) { 447 return; 448 } 449 450 const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix); 451 SkRect clipBounds; 452 totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds())); 453 const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y); 454 455 SkIRect maskBounds = fRCStack.rc().getBounds(); 456 if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) { 457 return; 458 } 459 460 sk_sp<SkImage> mask; 461 SkMatrix maskMatrix, shaderMatrix; 462 SkTLazy<SkAutoDeviceClipRestore> autoClipRestore; 463 464 SkMatrix totalInverse; 465 if (clipImage->isAlphaOnly() && totalMatrix.invert(&totalInverse)) { 466 // If the mask is already in A8 format, we can draw it directly 467 // (while compensating in the shader matrix). 468 mask = sk_ref_sp(clipImage); 469 maskMatrix = totalMatrix; 470 shaderMatrix = SkMatrix::Concat(totalInverse, SkMatrix::MakeTrans(x, y)); 471 472 // If the mask is not fully contained within the src layer, we must clip. 473 if (!srcBounds.contains(clipBounds)) { 474 autoClipRestore.init(this, srcBounds); 475 } 476 477 maskBounds.offsetTo(0, 0); 478 } else { 479 // Otherwise, we convert the mask to A8 explicitly. 480 sk_sp<SkSurface> surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(), 481 maskBounds.height())); 482 SkCanvas* canvas = surf->getCanvas(); 483 canvas->translate(-maskBounds.x(), -maskBounds.y()); 484 canvas->concat(totalMatrix); 485 canvas->drawImage(clipImage, 0, 0); 486 487 mask = surf->makeImageSnapshot(); 488 maskMatrix = SkMatrix::I(); 489 shaderMatrix = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y()); 490 } 491 492 SkAutoDeviceCTMRestore adctmr(this, maskMatrix); 493 paint.writable()->setShader(srcImage->makeShader(&shaderMatrix)); 494 this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint); 495 } 496 497 sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) { 498 return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap); 499 } 500 501 sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) { 502 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()), 503 image->makeNonTextureImage(), fBitmap.colorSpace()); 504 } 505 506 sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial() { 507 return this->makeSpecial(fBitmap); 508 } 509 510 /////////////////////////////////////////////////////////////////////////////// 511 512 sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { 513 return SkSurface::MakeRaster(info, &props); 514 } 515 516 SkImageFilterCache* SkBitmapDevice::getImageFilterCache() { 517 SkImageFilterCache* cache = SkImageFilterCache::Get(); 518 cache->ref(); 519 return cache; 520 } 521 522 /////////////////////////////////////////////////////////////////////////////////////////////////// 523 524 bool SkBitmapDevice::onShouldDisableLCD(const SkPaint& paint) const { 525 if (kN32_SkColorType != fBitmap.colorType() || 526 paint.getPathEffect() || 527 paint.isFakeBoldText() || 528 paint.getStyle() != SkPaint::kFill_Style || 529 !paint.isSrcOver()) 530 { 531 return true; 532 } 533 return false; 534 } 535 536 /////////////////////////////////////////////////////////////////////////////////////////////////// 537 538 void SkBitmapDevice::onSave() { 539 fRCStack.save(); 540 } 541 542 void SkBitmapDevice::onRestore() { 543 fRCStack.restore(); 544 } 545 546 void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) { 547 fRCStack.clipRect(this->ctm(), rect, op, aa); 548 } 549 550 void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { 551 fRCStack.clipRRect(this->ctm(), rrect, op, aa); 552 } 553 554 void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) { 555 fRCStack.clipPath(this->ctm(), path, op, aa); 556 } 557 558 void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) { 559 SkIPoint origin = this->getOrigin(); 560 SkRegion tmp; 561 const SkRegion* ptr = &rgn; 562 if (origin.fX | origin.fY) { 563 // translate from "global/canvas" coordinates to relative to this device 564 rgn.translate(-origin.fX, -origin.fY, &tmp); 565 ptr = &tmp; 566 } 567 fRCStack.clipRegion(*ptr, op); 568 } 569 570 void SkBitmapDevice::onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) { 571 fRCStack.setDeviceClipRestriction(mutableClipRestriction); 572 if (!mutableClipRestriction->isEmpty()) { 573 SkRegion rgn(*mutableClipRestriction); 574 fRCStack.clipRegion(rgn, SkClipOp::kIntersect); 575 } 576 } 577 578 bool SkBitmapDevice::onClipIsAA() const { 579 const SkRasterClip& rc = fRCStack.rc(); 580 return !rc.isEmpty() && rc.isAA(); 581 } 582 583 void SkBitmapDevice::onAsRgnClip(SkRegion* rgn) const { 584 const SkRasterClip& rc = fRCStack.rc(); 585 if (rc.isAA()) { 586 rgn->setRect(rc.getBounds()); 587 } else { 588 *rgn = rc.bwRgn(); 589 } 590 } 591 592 void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) { 593 #ifdef SK_DEBUG 594 const SkIRect& stackBounds = fRCStack.rc().getBounds(); 595 SkASSERT(drawClipBounds == stackBounds); 596 #endif 597 } 598 599 SkBaseDevice::ClipType SkBitmapDevice::onGetClipType() const { 600 const SkRasterClip& rc = fRCStack.rc(); 601 if (rc.isEmpty()) { 602 return kEmpty_ClipType; 603 } else if (rc.isRect()) { 604 return kRect_ClipType; 605 } else { 606 return kComplex_ClipType; 607 } 608 } 609