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