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 26class SkColorTable; 27 28static 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 68SkBitmapDevice::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 76SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) { 77 return Create(info, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)); 78} 79 80SkBitmapDevice::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 90SkBitmapDevice* 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 128void 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 135SkBaseDevice* 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 140bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) { 141 if (this->onPeekPixels(pmap)) { 142 fBitmap.notifyPixelsChanged(); 143 return true; 144 } 145 return false; 146} 147 148bool 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 157bool 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 170bool SkBitmapDevice::onReadPixels(const SkPixmap& pm, int x, int y) { 171 return fBitmap.readPixels(pm, x, y); 172} 173 174/////////////////////////////////////////////////////////////////////////////// 175 176class SkBitmapDevice::BDDraw : public SkDraw { 177public: 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 189void SkBitmapDevice::drawPaint(const SkPaint& paint) { 190 BDDraw(this).drawPaint(paint); 191} 192 193void 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 198void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { 199 BDDraw(this).drawRect(r, paint); 200} 201 202void 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 210void 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 223void 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 229void 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 236static 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 245void 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 356void SkBitmapDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { 357 BDDraw(this).drawSprite(bitmap, x, y, paint); 358} 359 360void 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 365void 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 371void 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 378void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& origPaint) { 379 SkASSERT(!origPaint.getImageFilter()); 380 381 // todo: can we unify with similar adjustment in SkGpuDevice? 382 SkTCopyOnFirstWrite<SkPaint> paint(origPaint); 383 if (paint->getMaskFilter()) { 384 paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm())); 385 } 386 387 BDDraw(this).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, *paint); 388} 389 390/////////////////////////////////////////////////////////////////////////////// 391 392namespace { 393 394class SkAutoDeviceClipRestore { 395public: 396 SkAutoDeviceClipRestore(SkBaseDevice* device, const SkIRect& clip) 397 : fDevice(device) 398 , fPrevCTM(device->ctm()) { 399 fDevice->save(); 400 fDevice->setCTM(SkMatrix::I()); 401 fDevice->clipRect(SkRect::Make(clip), SkClipOp::kIntersect, false); 402 fDevice->setCTM(fPrevCTM); 403 } 404 405 ~SkAutoDeviceClipRestore() { 406 fDevice->restore(fPrevCTM); 407 } 408 409private: 410 SkBaseDevice* fDevice; 411 const SkMatrix fPrevCTM; 412}; 413 414} // anonymous ns 415 416void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint, 417 SkImage* clipImage, const SkMatrix& clipMatrix) { 418 SkASSERT(!src->isTextureBacked()); 419 420 sk_sp<SkSpecialImage> filteredImage; 421 SkTCopyOnFirstWrite<SkPaint> paint(origPaint); 422 423 if (SkImageFilter* filter = paint->getImageFilter()) { 424 SkIPoint offset = SkIPoint::Make(0, 0); 425 const SkMatrix matrix = SkMatrix::Concat( 426 SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm()); 427 const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y); 428 sk_sp<SkImageFilterCache> cache(this->getImageFilterCache()); 429 SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace()); 430 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); 431 432 filteredImage = filter->filterImage(src, ctx, &offset); 433 if (!filteredImage) { 434 return; 435 } 436 437 src = filteredImage.get(); 438 paint.writable()->setImageFilter(nullptr); 439 x += offset.x(); 440 y += offset.y(); 441 } 442 443 if (paint->getMaskFilter()) { 444 paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithLocalMatrix(this->ctm())); 445 } 446 447 if (!clipImage) { 448 SkBitmap resultBM; 449 if (src->getROPixels(&resultBM)) { 450 this->drawSprite(resultBM, x, y, *paint); 451 } 452 return; 453 } 454 455 // Clip image case. 456 sk_sp<SkImage> srcImage(src->asImage()); 457 if (!srcImage) { 458 return; 459 } 460 461 const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix); 462 SkRect clipBounds; 463 totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds())); 464 const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y); 465 466 SkIRect maskBounds = fRCStack.rc().getBounds(); 467 if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) { 468 return; 469 } 470 471 sk_sp<SkImage> mask; 472 SkMatrix maskMatrix, shaderMatrix; 473 SkTLazy<SkAutoDeviceClipRestore> autoClipRestore; 474 475 SkMatrix totalInverse; 476 if (clipImage->isAlphaOnly() && totalMatrix.invert(&totalInverse)) { 477 // If the mask is already in A8 format, we can draw it directly 478 // (while compensating in the shader matrix). 479 mask = sk_ref_sp(clipImage); 480 maskMatrix = totalMatrix; 481 shaderMatrix = SkMatrix::Concat(totalInverse, SkMatrix::MakeTrans(x, y)); 482 483 // If the mask is not fully contained within the src layer, we must clip. 484 if (!srcBounds.contains(clipBounds)) { 485 autoClipRestore.init(this, srcBounds); 486 } 487 488 maskBounds.offsetTo(0, 0); 489 } else { 490 // Otherwise, we convert the mask to A8 explicitly. 491 sk_sp<SkSurface> surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(), 492 maskBounds.height())); 493 SkCanvas* canvas = surf->getCanvas(); 494 canvas->translate(-maskBounds.x(), -maskBounds.y()); 495 canvas->concat(totalMatrix); 496 canvas->drawImage(clipImage, 0, 0); 497 498 mask = surf->makeImageSnapshot(); 499 maskMatrix = SkMatrix::I(); 500 shaderMatrix = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y()); 501 } 502 503 SkAutoDeviceCTMRestore adctmr(this, maskMatrix); 504 paint.writable()->setShader(srcImage->makeShader(&shaderMatrix)); 505 this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint); 506} 507 508sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) { 509 return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap); 510} 511 512sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) { 513 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()), 514 image->makeNonTextureImage(), fBitmap.colorSpace()); 515} 516 517sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial() { 518 return this->makeSpecial(fBitmap); 519} 520 521/////////////////////////////////////////////////////////////////////////////// 522 523sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { 524 return SkSurface::MakeRaster(info, &props); 525} 526 527SkImageFilterCache* SkBitmapDevice::getImageFilterCache() { 528 SkImageFilterCache* cache = SkImageFilterCache::Get(); 529 cache->ref(); 530 return cache; 531} 532 533/////////////////////////////////////////////////////////////////////////////////////////////////// 534 535bool SkBitmapDevice::onShouldDisableLCD(const SkPaint& paint) const { 536 if (kN32_SkColorType != fBitmap.colorType() || 537 paint.getPathEffect() || 538 paint.isFakeBoldText() || 539 paint.getStyle() != SkPaint::kFill_Style || 540 !paint.isSrcOver()) 541 { 542 return true; 543 } 544 return false; 545} 546 547/////////////////////////////////////////////////////////////////////////////////////////////////// 548 549void SkBitmapDevice::onSave() { 550 fRCStack.save(); 551} 552 553void SkBitmapDevice::onRestore() { 554 fRCStack.restore(); 555} 556 557void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) { 558 fRCStack.clipRect(this->ctm(), rect, op, aa); 559} 560 561void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { 562 fRCStack.clipRRect(this->ctm(), rrect, op, aa); 563} 564 565void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) { 566 fRCStack.clipPath(this->ctm(), path, op, aa); 567} 568 569void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) { 570 SkIPoint origin = this->getOrigin(); 571 SkRegion tmp; 572 const SkRegion* ptr = &rgn; 573 if (origin.fX | origin.fY) { 574 // translate from "global/canvas" coordinates to relative to this device 575 rgn.translate(-origin.fX, -origin.fY, &tmp); 576 ptr = &tmp; 577 } 578 fRCStack.clipRegion(*ptr, op); 579} 580 581void SkBitmapDevice::onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) { 582 fRCStack.setDeviceClipRestriction(mutableClipRestriction); 583 if (!mutableClipRestriction->isEmpty()) { 584 SkRegion rgn(*mutableClipRestriction); 585 fRCStack.clipRegion(rgn, SkClipOp::kIntersect); 586 } 587} 588 589bool SkBitmapDevice::onClipIsAA() const { 590 const SkRasterClip& rc = fRCStack.rc(); 591 return !rc.isEmpty() && rc.isAA(); 592} 593 594void SkBitmapDevice::onAsRgnClip(SkRegion* rgn) const { 595 const SkRasterClip& rc = fRCStack.rc(); 596 if (rc.isAA()) { 597 rgn->setRect(rc.getBounds()); 598 } else { 599 *rgn = rc.bwRgn(); 600 } 601} 602 603void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) { 604#ifdef SK_DEBUG 605 const SkIRect& stackBounds = fRCStack.rc().getBounds(); 606 SkASSERT(drawClipBounds == stackBounds); 607#endif 608} 609 610SkBaseDevice::ClipType SkBitmapDevice::onGetClipType() const { 611 const SkRasterClip& rc = fRCStack.rc(); 612 if (rc.isEmpty()) { 613 return kEmpty_ClipType; 614 } else if (rc.isRect()) { 615 return kRect_ClipType; 616 } else { 617 return kComplex_ClipType; 618 } 619} 620