SkCanvas.cpp revision fd4ee4dea13f083c81cc80180fa09ee0127158a1
1 2/* 3 * Copyright 2008 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkCanvas.h" 11#include "SkBitmapDevice.h" 12#include "SkBounder.h" 13#include "SkDeviceImageFilterProxy.h" 14#include "SkDraw.h" 15#include "SkDrawFilter.h" 16#include "SkDrawLooper.h" 17#include "SkMetaData.h" 18#include "SkPathOps.h" 19#include "SkPicture.h" 20#include "SkRasterClip.h" 21#include "SkRRect.h" 22#include "SkSmallAllocator.h" 23#include "SkSurface_Base.h" 24#include "SkTemplates.h" 25#include "SkTextFormatParams.h" 26#include "SkTLazy.h" 27#include "SkUtils.h" 28 29#if SK_SUPPORT_GPU 30#include "GrRenderTarget.h" 31#endif 32 33// experimental for faster tiled drawing... 34//#define SK_ENABLE_CLIP_QUICKREJECT 35 36//#define SK_TRACE_SAVERESTORE 37 38#ifdef SK_TRACE_SAVERESTORE 39 static int gLayerCounter; 40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); } 41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); } 42 43 static int gRecCounter; 44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); } 45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); } 46 47 static int gCanvasCounter; 48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); } 49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); } 50#else 51 #define inc_layer() 52 #define dec_layer() 53 #define inc_rec() 54 #define dec_rec() 55 #define inc_canvas() 56 #define dec_canvas() 57#endif 58 59#ifdef SK_DEBUG 60#include "SkPixelRef.h" 61 62/* 63 * Some pixelref subclasses can support being "locked" from another thread 64 * during the lock-scope of skia calling them. In these instances, this balance 65 * check will fail, but may not be indicative of a problem, so we allow a build 66 * flag to disable this check. 67 * 68 * Potentially another fix would be to have a (debug-only) virtual or flag on 69 * pixelref, which could tell us at runtime if this check is valid. That would 70 * eliminate the need for this heavy-handed build check. 71 */ 72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK 73class AutoCheckLockCountBalance { 74public: 75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ } 76}; 77#else 78class AutoCheckLockCountBalance { 79public: 80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) { 81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0; 82 } 83 ~AutoCheckLockCountBalance() { 84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0; 85 SkASSERT(count == fLockCount); 86 } 87 88private: 89 const SkPixelRef* fPixelRef; 90 int fLockCount; 91}; 92#endif 93 94class AutoCheckNoSetContext { 95public: 96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) { 97 this->assertNoSetContext(fPaint); 98 } 99 ~AutoCheckNoSetContext() { 100 this->assertNoSetContext(fPaint); 101 } 102 103private: 104 const SkPaint& fPaint; 105 106 void assertNoSetContext(const SkPaint& paint) { 107 SkShader* s = paint.getShader(); 108 if (s) { 109 SkASSERT(!s->setContextHasBeenCalled()); 110 } 111 } 112}; 113 114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap) 115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint) 116 117#else 118 #define CHECK_LOCKCOUNT_BALANCE(bitmap) 119 #define CHECK_SHADER_NOSETCONTEXT(paint) 120#endif 121 122typedef SkTLazy<SkPaint> SkLazyPaint; 123 124void SkCanvas::predrawNotify() { 125 if (fSurfaceBase) { 126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode); 127 } 128} 129 130/////////////////////////////////////////////////////////////////////////////// 131 132/* This is the record we keep for each SkBaseDevice that the user installs. 133 The clip/matrix/proc are fields that reflect the top of the save/restore 134 stack. Whenever the canvas changes, it marks a dirty flag, and then before 135 these are used (assuming we're not on a layer) we rebuild these cache 136 values: they reflect the top of the save stack, but translated and clipped 137 by the device's XY offset and bitmap-bounds. 138*/ 139struct DeviceCM { 140 DeviceCM* fNext; 141 SkBaseDevice* fDevice; 142 SkRasterClip fClip; 143 const SkMatrix* fMatrix; 144 SkPaint* fPaint; // may be null (in the future) 145 146 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas) 147 : fNext(NULL) { 148 if (NULL != device) { 149 device->ref(); 150 device->onAttachToCanvas(canvas); 151 } 152 fDevice = device; 153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL; 154 } 155 156 ~DeviceCM() { 157 if (NULL != fDevice) { 158 fDevice->onDetachFromCanvas(); 159 fDevice->unref(); 160 } 161 SkDELETE(fPaint); 162 } 163 164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip, 165 const SkClipStack& clipStack, SkRasterClip* updateClip) { 166 int x = fDevice->getOrigin().x(); 167 int y = fDevice->getOrigin().y(); 168 int width = fDevice->width(); 169 int height = fDevice->height(); 170 171 if ((x | y) == 0) { 172 fMatrix = &totalMatrix; 173 fClip = totalClip; 174 } else { 175 fMatrixStorage = totalMatrix; 176 fMatrixStorage.postTranslate(SkIntToScalar(-x), 177 SkIntToScalar(-y)); 178 fMatrix = &fMatrixStorage; 179 180 totalClip.translate(-x, -y, &fClip); 181 } 182 183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op); 184 185 // intersect clip, but don't translate it (yet) 186 187 if (updateClip) { 188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height), 189 SkRegion::kDifference_Op); 190 } 191 192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack); 193 194#ifdef SK_DEBUG 195 if (!fClip.isEmpty()) { 196 SkIRect deviceR; 197 deviceR.set(0, 0, width, height); 198 SkASSERT(deviceR.contains(fClip.getBounds())); 199 } 200#endif 201 } 202 203private: 204 SkMatrix fMatrixStorage; 205}; 206 207/* This is the record we keep for each save/restore level in the stack. 208 Since a level optionally copies the matrix and/or stack, we have pointers 209 for these fields. If the value is copied for this level, the copy is 210 stored in the ...Storage field, and the pointer points to that. If the 211 value is not copied for this level, we ignore ...Storage, and just point 212 at the corresponding value in the previous level in the stack. 213*/ 214class SkCanvas::MCRec { 215public: 216 int fFlags; 217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec 218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec 219 SkDrawFilter* fFilter; // the current filter (or null) 220 221 DeviceCM* fLayer; 222 /* If there are any layers in the stack, this points to the top-most 223 one that is at or below this level in the stack (so we know what 224 bitmap/device to draw into from this level. This value is NOT 225 reference counted, since the real owner is either our fLayer field, 226 or a previous one in a lower level.) 227 */ 228 DeviceCM* fTopLayer; 229 230 MCRec(const MCRec* prev, int flags) : fFlags(flags) { 231 if (NULL != prev) { 232 if (flags & SkCanvas::kMatrix_SaveFlag) { 233 fMatrixStorage = *prev->fMatrix; 234 fMatrix = &fMatrixStorage; 235 } else { 236 fMatrix = prev->fMatrix; 237 } 238 239 if (flags & SkCanvas::kClip_SaveFlag) { 240 fRasterClipStorage = *prev->fRasterClip; 241 fRasterClip = &fRasterClipStorage; 242 } else { 243 fRasterClip = prev->fRasterClip; 244 } 245 246 fFilter = prev->fFilter; 247 SkSafeRef(fFilter); 248 249 fTopLayer = prev->fTopLayer; 250 } else { // no prev 251 fMatrixStorage.reset(); 252 253 fMatrix = &fMatrixStorage; 254 fRasterClip = &fRasterClipStorage; 255 fFilter = NULL; 256 fTopLayer = NULL; 257 } 258 fLayer = NULL; 259 260 // don't bother initializing fNext 261 inc_rec(); 262 } 263 ~MCRec() { 264 SkSafeUnref(fFilter); 265 SkDELETE(fLayer); 266 dec_rec(); 267 } 268 269private: 270 SkMatrix fMatrixStorage; 271 SkRasterClip fRasterClipStorage; 272}; 273 274class SkDrawIter : public SkDraw { 275public: 276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) { 277 canvas = canvas->canvasForDrawIter(); 278 fCanvas = canvas; 279 canvas->updateDeviceCMCache(); 280 281 fClipStack = &canvas->fClipStack; 282 fBounder = canvas->getBounder(); 283 fCurrLayer = canvas->fMCRec->fTopLayer; 284 fSkipEmptyClips = skipEmptyClips; 285 } 286 287 bool next() { 288 // skip over recs with empty clips 289 if (fSkipEmptyClips) { 290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) { 291 fCurrLayer = fCurrLayer->fNext; 292 } 293 } 294 295 const DeviceCM* rec = fCurrLayer; 296 if (rec && rec->fDevice) { 297 298 fMatrix = rec->fMatrix; 299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW(); 300 fRC = &rec->fClip; 301 fDevice = rec->fDevice; 302 fBitmap = &fDevice->accessBitmap(true); 303 fPaint = rec->fPaint; 304 SkDEBUGCODE(this->validate();) 305 306 fCurrLayer = rec->fNext; 307 if (fBounder) { 308 fBounder->setClip(fClip); 309 } 310 // fCurrLayer may be NULL now 311 312 return true; 313 } 314 return false; 315 } 316 317 SkBaseDevice* getDevice() const { return fDevice; } 318 int getX() const { return fDevice->getOrigin().x(); } 319 int getY() const { return fDevice->getOrigin().y(); } 320 const SkMatrix& getMatrix() const { return *fMatrix; } 321 const SkRegion& getClip() const { return *fClip; } 322 const SkPaint* getPaint() const { return fPaint; } 323 324private: 325 SkCanvas* fCanvas; 326 const DeviceCM* fCurrLayer; 327 const SkPaint* fPaint; // May be null. 328 SkBool8 fSkipEmptyClips; 329 330 typedef SkDraw INHERITED; 331}; 332 333///////////////////////////////////////////////////////////////////////////// 334 335class AutoDrawLooper { 336public: 337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, 338 bool skipLayerForImageFilter = false, 339 const SkRect* bounds = NULL) : fOrigPaint(paint) { 340 fCanvas = canvas; 341 fFilter = canvas->getDrawFilter(); 342 fPaint = NULL; 343 fSaveCount = canvas->getSaveCount(); 344 fDoClearImageFilter = false; 345 fDone = false; 346 347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) { 348 SkPaint tmp; 349 tmp.setImageFilter(fOrigPaint.getImageFilter()); 350 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag, 351 true, SkCanvas::kFullLayer_SaveLayerStrategy); 352 // we'll clear the imageFilter for the actual draws in next(), so 353 // it will only be applied during the restore(). 354 fDoClearImageFilter = true; 355 } 356 357 if (SkDrawLooper* looper = paint.getLooper()) { 358 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>( 359 looper->contextSize()); 360 fLooperContext = looper->createContext(canvas, buffer); 361 fIsSimple = false; 362 } else { 363 fLooperContext = NULL; 364 // can we be marked as simple? 365 fIsSimple = !fFilter && !fDoClearImageFilter; 366 } 367 } 368 369 ~AutoDrawLooper() { 370 if (fDoClearImageFilter) { 371 fCanvas->internalRestore(); 372 } 373 SkASSERT(fCanvas->getSaveCount() == fSaveCount); 374 } 375 376 const SkPaint& paint() const { 377 SkASSERT(fPaint); 378 return *fPaint; 379 } 380 381 bool next(SkDrawFilter::Type drawType) { 382 if (fDone) { 383 return false; 384 } else if (fIsSimple) { 385 fDone = true; 386 fPaint = &fOrigPaint; 387 return !fPaint->nothingToDraw(); 388 } else { 389 return this->doNext(drawType); 390 } 391 } 392 393private: 394 SkLazyPaint fLazyPaint; 395 SkCanvas* fCanvas; 396 const SkPaint& fOrigPaint; 397 SkDrawFilter* fFilter; 398 const SkPaint* fPaint; 399 int fSaveCount; 400 bool fDoClearImageFilter; 401 bool fDone; 402 bool fIsSimple; 403 SkDrawLooper::Context* fLooperContext; 404 SkSmallAllocator<1, 32> fLooperContextAllocator; 405 406 bool doNext(SkDrawFilter::Type drawType); 407}; 408 409bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) { 410 fPaint = NULL; 411 SkASSERT(!fIsSimple); 412 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter); 413 414 SkPaint* paint = fLazyPaint.set(fOrigPaint); 415 416 if (fDoClearImageFilter) { 417 paint->setImageFilter(NULL); 418 } 419 420 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) { 421 fDone = true; 422 return false; 423 } 424 if (fFilter) { 425 if (!fFilter->filter(paint, drawType)) { 426 fDone = true; 427 return false; 428 } 429 if (NULL == fLooperContext) { 430 // no looper means we only draw once 431 fDone = true; 432 } 433 } 434 fPaint = paint; 435 436 // if we only came in here for the imagefilter, mark us as done 437 if (!fLooperContext && !fFilter) { 438 fDone = true; 439 } 440 441 // call this after any possible paint modifiers 442 if (fPaint->nothingToDraw()) { 443 fPaint = NULL; 444 return false; 445 } 446 return true; 447} 448 449/* Stack helper for managing a SkBounder. In the destructor, if we were 450 given a bounder, we call its commit() method, signifying that we are 451 done accumulating bounds for that draw. 452*/ 453class SkAutoBounderCommit { 454public: 455 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {} 456 ~SkAutoBounderCommit() { 457 if (NULL != fBounder) { 458 fBounder->commit(); 459 } 460 } 461private: 462 SkBounder* fBounder; 463}; 464#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit) 465 466#include "SkColorPriv.h" 467 468////////// macros to place around the internal draw calls ////////////////// 469 470#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \ 471 this->predrawNotify(); \ 472 AutoDrawLooper looper(this, paint, true); \ 473 while (looper.next(type)) { \ 474 SkAutoBounderCommit ac(fBounder); \ 475 SkDrawIter iter(this); 476 477#define LOOPER_BEGIN(paint, type, bounds) \ 478 this->predrawNotify(); \ 479 AutoDrawLooper looper(this, paint, false, bounds); \ 480 while (looper.next(type)) { \ 481 SkAutoBounderCommit ac(fBounder); \ 482 SkDrawIter iter(this); 483 484#define LOOPER_END } 485 486//////////////////////////////////////////////////////////////////////////// 487 488SkBaseDevice* SkCanvas::init(SkBaseDevice* device) { 489 fBounder = NULL; 490 fCachedLocalClipBounds.setEmpty(); 491 fCachedLocalClipBoundsDirty = true; 492 fAllowSoftClip = true; 493 fAllowSimplifyClip = false; 494 fDeviceCMDirty = false; 495 fSaveLayerCount = 0; 496 fCullCount = 0; 497 fMetaData = NULL; 498 499 fMCRec = (MCRec*)fMCStack.push_back(); 500 new (fMCRec) MCRec(NULL, 0); 501 502 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL)); 503 fMCRec->fTopLayer = fMCRec->fLayer; 504 505 fSurfaceBase = NULL; 506 507 return this->setRootDevice(device); 508} 509 510SkCanvas::SkCanvas() 511 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) 512{ 513 inc_canvas(); 514 515 this->init(NULL); 516} 517 518SkCanvas::SkCanvas(int width, int height) 519 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) 520{ 521 inc_canvas(); 522 523 SkBitmap bitmap; 524 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height)); 525 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref(); 526} 527 528SkCanvas::SkCanvas(SkBaseDevice* device) 529 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) 530{ 531 inc_canvas(); 532 533 this->init(device); 534} 535 536SkCanvas::SkCanvas(const SkBitmap& bitmap) 537 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) 538{ 539 inc_canvas(); 540 541 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref(); 542} 543 544SkCanvas::~SkCanvas() { 545 // free up the contents of our deque 546 this->restoreToCount(1); // restore everything but the last 547 SkASSERT(0 == fSaveLayerCount); 548 549 this->internalRestore(); // restore the last, since we're going away 550 551 SkSafeUnref(fBounder); 552 SkDELETE(fMetaData); 553 554 dec_canvas(); 555} 556 557SkBounder* SkCanvas::setBounder(SkBounder* bounder) { 558 SkRefCnt_SafeAssign(fBounder, bounder); 559 return bounder; 560} 561 562SkDrawFilter* SkCanvas::getDrawFilter() const { 563 return fMCRec->fFilter; 564} 565 566SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) { 567 SkRefCnt_SafeAssign(fMCRec->fFilter, filter); 568 return filter; 569} 570 571SkMetaData& SkCanvas::getMetaData() { 572 // metadata users are rare, so we lazily allocate it. If that changes we 573 // can decide to just make it a field in the device (rather than a ptr) 574 if (NULL == fMetaData) { 575 fMetaData = new SkMetaData; 576 } 577 return *fMetaData; 578} 579 580/////////////////////////////////////////////////////////////////////////////// 581 582void SkCanvas::flush() { 583 SkBaseDevice* device = this->getDevice(); 584 if (device) { 585 device->flush(); 586 } 587} 588 589SkISize SkCanvas::getTopLayerSize() const { 590 SkBaseDevice* d = this->getTopDevice(); 591 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); 592} 593 594SkIPoint SkCanvas::getTopLayerOrigin() const { 595 SkBaseDevice* d = this->getTopDevice(); 596 return d ? d->getOrigin() : SkIPoint::Make(0, 0); 597} 598 599SkISize SkCanvas::getBaseLayerSize() const { 600 SkBaseDevice* d = this->getDevice(); 601 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); 602} 603 604SkBaseDevice* SkCanvas::getDevice() const { 605 // return root device 606 MCRec* rec = (MCRec*) fMCStack.front(); 607 SkASSERT(rec && rec->fLayer); 608 return rec->fLayer->fDevice; 609} 610 611SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const { 612 if (updateMatrixClip) { 613 const_cast<SkCanvas*>(this)->updateDeviceCMCache(); 614 } 615 return fMCRec->fTopLayer->fDevice; 616} 617 618SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) { 619 // return root device 620 SkDeque::F2BIter iter(fMCStack); 621 MCRec* rec = (MCRec*)iter.next(); 622 SkASSERT(rec && rec->fLayer); 623 SkBaseDevice* rootDevice = rec->fLayer->fDevice; 624 625 if (rootDevice == device) { 626 return device; 627 } 628 629 if (device) { 630 device->onAttachToCanvas(this); 631 } 632 if (rootDevice) { 633 rootDevice->onDetachFromCanvas(); 634 } 635 636 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device); 637 rootDevice = device; 638 639 fDeviceCMDirty = true; 640 641 /* Now we update our initial region to have the bounds of the new device, 642 and then intersect all of the clips in our stack with these bounds, 643 to ensure that we can't draw outside of the device's bounds (and trash 644 memory). 645 646 NOTE: this is only a partial-fix, since if the new device is larger than 647 the previous one, we don't know how to "enlarge" the clips in our stack, 648 so drawing may be artificially restricted. Without keeping a history of 649 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly 650 reconstruct the correct clips, so this approximation will have to do. 651 The caller really needs to restore() back to the base if they want to 652 accurately take advantage of the new device bounds. 653 */ 654 655 SkIRect bounds; 656 if (device) { 657 bounds.set(0, 0, device->width(), device->height()); 658 } else { 659 bounds.setEmpty(); 660 } 661 // now jam our 1st clip to be bounds, and intersect the rest with that 662 rec->fRasterClip->setRect(bounds); 663 while ((rec = (MCRec*)iter.next()) != NULL) { 664 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op); 665 } 666 667 return device; 668} 669 670bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) { 671 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) { 672 return false; 673 } 674 675 bool weAllocated = false; 676 if (NULL == bitmap->pixelRef()) { 677 if (!bitmap->allocPixels()) { 678 return false; 679 } 680 weAllocated = true; 681 } 682 683 SkBitmap bm(*bitmap); 684 bm.lockPixels(); 685 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) { 686 return true; 687 } 688 689 if (weAllocated) { 690 bitmap->setPixelRef(NULL); 691 } 692 return false; 693} 694 695bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { 696 SkIRect r = srcRect; 697 const SkISize size = this->getBaseLayerSize(); 698 if (!r.intersect(0, 0, size.width(), size.height())) { 699 bitmap->reset(); 700 return false; 701 } 702 703 if (!bitmap->allocN32Pixels(r.width(), r.height())) { 704 // bitmap will already be reset. 705 return false; 706 } 707 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) { 708 bitmap->reset(); 709 return false; 710 } 711 return true; 712} 713 714bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) { 715 switch (origInfo.colorType()) { 716 case kUnknown_SkColorType: 717 case kIndex_8_SkColorType: 718 return false; 719 default: 720 break; 721 } 722 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) { 723 return false; 724 } 725 if (0 == origInfo.width() || 0 == origInfo.height()) { 726 return false; 727 } 728 729 SkBaseDevice* device = this->getDevice(); 730 if (!device) { 731 return false; 732 } 733 734 const SkISize size = this->getBaseLayerSize(); 735 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height()); 736 if (!srcR.intersect(0, 0, size.width(), size.height())) { 737 return false; 738 } 739 740 SkImageInfo info = origInfo; 741 // the intersect may have shrunk info's logical size 742 info.fWidth = srcR.width(); 743 info.fHeight = srcR.height(); 744 745 // if x or y are negative, then we have to adjust pixels 746 if (x > 0) { 747 x = 0; 748 } 749 if (y > 0) { 750 y = 0; 751 } 752 // here x,y are either 0 or negative 753 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel()); 754 755 // The device can assert that the requested area is always contained in its bounds 756 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y()); 757} 758 759bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) { 760 if (bitmap.getTexture()) { 761 return false; 762 } 763 SkBitmap bm(bitmap); 764 bm.lockPixels(); 765 if (bm.getPixels()) { 766 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y); 767 } 768 return false; 769} 770 771bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes, 772 int x, int y) { 773 switch (origInfo.colorType()) { 774 case kUnknown_SkColorType: 775 case kIndex_8_SkColorType: 776 return false; 777 default: 778 break; 779 } 780 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) { 781 return false; 782 } 783 784 const SkISize size = this->getBaseLayerSize(); 785 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height()); 786 if (!target.intersect(0, 0, size.width(), size.height())) { 787 return false; 788 } 789 790 SkBaseDevice* device = this->getDevice(); 791 if (!device) { 792 return false; 793 } 794 795 SkImageInfo info = origInfo; 796 // the intersect may have shrunk info's logical size 797 info.fWidth = target.width(); 798 info.fHeight = target.height(); 799 800 // if x or y are negative, then we have to adjust pixels 801 if (x > 0) { 802 x = 0; 803 } 804 if (y > 0) { 805 y = 0; 806 } 807 // here x,y are either 0 or negative 808 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel()); 809 810 // The device can assert that the requested area is always contained in its bounds 811 return device->writePixels(info, pixels, rowBytes, target.x(), target.y()); 812} 813 814SkCanvas* SkCanvas::canvasForDrawIter() { 815 return this; 816} 817 818////////////////////////////////////////////////////////////////////////////// 819 820void SkCanvas::updateDeviceCMCache() { 821 if (fDeviceCMDirty) { 822 const SkMatrix& totalMatrix = this->getTotalMatrix(); 823 const SkRasterClip& totalClip = *fMCRec->fRasterClip; 824 DeviceCM* layer = fMCRec->fTopLayer; 825 826 if (NULL == layer->fNext) { // only one layer 827 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL); 828 } else { 829 SkRasterClip clip(totalClip); 830 do { 831 layer->updateMC(totalMatrix, clip, fClipStack, &clip); 832 } while ((layer = layer->fNext) != NULL); 833 } 834 fDeviceCMDirty = false; 835 } 836} 837 838/////////////////////////////////////////////////////////////////////////////// 839 840int SkCanvas::internalSave(SaveFlags flags) { 841 int saveCount = this->getSaveCount(); // record this before the actual save 842 843 MCRec* newTop = (MCRec*)fMCStack.push_back(); 844 new (newTop) MCRec(fMCRec, flags); // balanced in restore() 845 846 fMCRec = newTop; 847 848 if (SkCanvas::kClip_SaveFlag & flags) { 849 fClipStack.save(); 850 } 851 852 return saveCount; 853} 854 855void SkCanvas::willSave(SaveFlags) { 856 // Do nothing. Subclasses may do something. 857} 858 859int SkCanvas::save(SaveFlags flags) { 860 this->willSave(flags); 861 // call shared impl 862 return this->internalSave(flags); 863} 864 865static bool bounds_affects_clip(SkCanvas::SaveFlags flags) { 866#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG 867 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0; 868#else 869 return true; 870#endif 871} 872 873bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, 874 SkIRect* intersection, const SkImageFilter* imageFilter) { 875 SkIRect clipBounds; 876 SkRegion::Op op = SkRegion::kIntersect_Op; 877 if (!this->getClipDeviceBounds(&clipBounds)) { 878 return false; 879 } 880 881 if (imageFilter) { 882 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds); 883 // Filters may grow the bounds beyond the device bounds. 884 op = SkRegion::kReplace_Op; 885 } 886 SkIRect ir; 887 if (NULL != bounds) { 888 SkRect r; 889 890 this->getTotalMatrix().mapRect(&r, *bounds); 891 r.roundOut(&ir); 892 // early exit if the layer's bounds are clipped out 893 if (!ir.intersect(clipBounds)) { 894 if (bounds_affects_clip(flags)) { 895 fMCRec->fRasterClip->setEmpty(); 896 } 897 return false; 898 } 899 } else { // no user bounds, so just use the clip 900 ir = clipBounds; 901 } 902 903 if (bounds_affects_clip(flags)) { 904 fClipStack.clipDevRect(ir, op); 905 // early exit if the clip is now empty 906 if (!fMCRec->fRasterClip->op(ir, op)) { 907 return false; 908 } 909 } 910 911 if (intersection) { 912 *intersection = ir; 913 } 914 return true; 915} 916 917SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) { 918 919 // Do nothing. Subclasses may do something. 920 return kFullLayer_SaveLayerStrategy; 921} 922 923int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, 924 SaveFlags flags) { 925 // Overriding classes may return false to signal that we don't need to create a layer. 926 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags); 927 return this->internalSaveLayer(bounds, paint, flags, false, strategy); 928} 929 930static SkBaseDevice* create_compatible_device(SkCanvas* canvas, 931 const SkImageInfo& info) { 932 SkBaseDevice* device = canvas->getDevice(); 933 return device ? device->createCompatibleDevice(info) : NULL; 934} 935 936int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags, 937 bool justForImageFilter, SaveLayerStrategy strategy) { 938#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG 939 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag); 940#endif 941 942 // do this before we create the layer. We don't call the public save() since 943 // that would invoke a possibly overridden virtual 944 int count = this->internalSave(flags); 945 946 fDeviceCMDirty = true; 947 948 SkIRect ir; 949 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) { 950 return count; 951 } 952 953 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about 954 // the clipRectBounds() call above? 955 if (kNoLayer_SaveLayerStrategy == strategy) { 956 return count; 957 } 958 959 // Kill the imagefilter if our device doesn't allow it 960 SkLazyPaint lazyP; 961 if (paint && paint->getImageFilter()) { 962 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) { 963 if (justForImageFilter) { 964 // early exit if the layer was just for the imageFilter 965 return count; 966 } 967 SkPaint* p = lazyP.set(*paint); 968 p->setImageFilter(NULL); 969 paint = p; 970 } 971 } 972 973 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag); 974 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(), 975 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); 976 977 SkBaseDevice* device; 978 if (paint && paint->getImageFilter()) { 979 device = create_compatible_device(this, info); 980 } else { 981 device = this->createLayerDevice(info); 982 } 983 if (NULL == device) { 984 SkDebugf("Unable to create device for layer."); 985 return count; 986 } 987 988 device->setOrigin(ir.fLeft, ir.fTop); 989 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this)); 990 device->unref(); 991 992 layer->fNext = fMCRec->fTopLayer; 993 fMCRec->fLayer = layer; 994 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer 995 996 fSaveLayerCount += 1; 997 return count; 998} 999 1000int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha, 1001 SaveFlags flags) { 1002 if (0xFF == alpha) { 1003 return this->saveLayer(bounds, NULL, flags); 1004 } else { 1005 SkPaint tmpPaint; 1006 tmpPaint.setAlpha(alpha); 1007 return this->saveLayer(bounds, &tmpPaint, flags); 1008 } 1009} 1010 1011void SkCanvas::willRestore() { 1012 // Do nothing. Subclasses may do something. 1013} 1014 1015void SkCanvas::restore() { 1016 // check for underflow 1017 if (fMCStack.count() > 1) { 1018 this->willRestore(); 1019 this->internalRestore(); 1020 } 1021} 1022 1023void SkCanvas::internalRestore() { 1024 SkASSERT(fMCStack.count() != 0); 1025 1026 fDeviceCMDirty = true; 1027 fCachedLocalClipBoundsDirty = true; 1028 1029 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) { 1030 fClipStack.restore(); 1031 } 1032 1033 // reserve our layer (if any) 1034 DeviceCM* layer = fMCRec->fLayer; // may be null 1035 // now detach it from fMCRec so we can pop(). Gets freed after its drawn 1036 fMCRec->fLayer = NULL; 1037 1038 // now do the normal restore() 1039 fMCRec->~MCRec(); // balanced in save() 1040 fMCStack.pop_back(); 1041 fMCRec = (MCRec*)fMCStack.back(); 1042 1043 /* Time to draw the layer's offscreen. We can't call the public drawSprite, 1044 since if we're being recorded, we don't want to record this (the 1045 recorder will have already recorded the restore). 1046 */ 1047 if (NULL != layer) { 1048 if (layer->fNext) { 1049 const SkIPoint& origin = layer->fDevice->getOrigin(); 1050 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), 1051 layer->fPaint); 1052 // reset this, since internalDrawDevice will have set it to true 1053 fDeviceCMDirty = true; 1054 1055 SkASSERT(fSaveLayerCount > 0); 1056 fSaveLayerCount -= 1; 1057 } 1058 SkDELETE(layer); 1059 } 1060} 1061 1062int SkCanvas::getSaveCount() const { 1063 return fMCStack.count(); 1064} 1065 1066void SkCanvas::restoreToCount(int count) { 1067 // sanity check 1068 if (count < 1) { 1069 count = 1; 1070 } 1071 1072 int n = this->getSaveCount() - count; 1073 for (int i = 0; i < n; ++i) { 1074 this->restore(); 1075 } 1076} 1077 1078bool SkCanvas::isDrawingToLayer() const { 1079 return fSaveLayerCount > 0; 1080} 1081 1082SkSurface* SkCanvas::newSurface(const SkImageInfo& info) { 1083 return this->onNewSurface(info); 1084} 1085 1086SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) { 1087 SkBaseDevice* dev = this->getDevice(); 1088 return dev ? dev->newSurface(info) : NULL; 1089} 1090 1091SkImageInfo SkCanvas::imageInfo() const { 1092 SkBaseDevice* dev = this->getDevice(); 1093 if (dev) { 1094 return dev->imageInfo(); 1095 } else { 1096 return SkImageInfo::MakeUnknown(0, 0); 1097 } 1098} 1099 1100const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) { 1101 return this->onPeekPixels(info, rowBytes); 1102} 1103 1104const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) { 1105 SkBaseDevice* dev = this->getDevice(); 1106 return dev ? dev->peekPixels(info, rowBytes) : NULL; 1107} 1108 1109void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) { 1110 void* pixels = this->onAccessTopLayerPixels(info, rowBytes); 1111 if (pixels && origin) { 1112 *origin = this->getTopDevice(false)->getOrigin(); 1113 } 1114 return pixels; 1115} 1116 1117void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) { 1118 SkBaseDevice* dev = this->getTopDevice(); 1119 return dev ? dev->accessPixels(info, rowBytes) : NULL; 1120} 1121 1122SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) { 1123 fAddr = canvas->peekPixels(&fInfo, &fRowBytes); 1124 if (NULL == fAddr) { 1125 fInfo = canvas->imageInfo(); 1126 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) { 1127 return; // failure, fAddr is NULL 1128 } 1129 if (!canvas->readPixels(&fBitmap, 0, 0)) { 1130 return; // failure, fAddr is NULL 1131 } 1132 fAddr = fBitmap.getPixels(); 1133 fRowBytes = fBitmap.rowBytes(); 1134 } 1135 SkASSERT(fAddr); // success 1136} 1137 1138bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const { 1139 if (fAddr) { 1140 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes, 1141 NULL, NULL); 1142 } else { 1143 bitmap->reset(); 1144 return false; 1145 } 1146} 1147 1148void SkCanvas::onPushCull(const SkRect& cullRect) { 1149 // do nothing. Subclasses may do something 1150} 1151 1152void SkCanvas::onPopCull() { 1153 // do nothing. Subclasses may do something 1154} 1155 1156///////////////////////////////////////////////////////////////////////////// 1157#ifdef SK_DEBUG 1158// Ensure that cull rects are monotonically nested in device space. 1159void SkCanvas::validateCull(const SkIRect& devCull) { 1160 if (fCullStack.isEmpty() 1161 || devCull.isEmpty() 1162 || fCullStack.top().contains(devCull)) { 1163 return; 1164 } 1165 1166 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n", 1167 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(), 1168 fCullStack.top().x(), fCullStack.top().y(), 1169 fCullStack.top().right(), fCullStack.top().bottom())); 1170 1171#ifdef ASSERT_NESTED_CULLING 1172 SkDEBUGFAIL("Invalid cull."); 1173#endif 1174} 1175#endif 1176 1177void SkCanvas::pushCull(const SkRect& cullRect) { 1178 ++fCullCount; 1179 this->onPushCull(cullRect); 1180 1181#ifdef SK_DEBUG 1182 // Map the cull rect into device space. 1183 SkRect mappedCull; 1184 this->getTotalMatrix().mapRect(&mappedCull, cullRect); 1185 1186 // Take clipping into account. 1187 SkIRect devClip, devCull; 1188 mappedCull.roundOut(&devCull); 1189 this->getClipDeviceBounds(&devClip); 1190 if (!devCull.intersect(devClip)) { 1191 devCull.setEmpty(); 1192 } 1193 1194 this->validateCull(devCull); 1195 fCullStack.push(devCull); // balanced in popCull 1196#endif 1197} 1198 1199void SkCanvas::popCull() { 1200 SkASSERT(fCullStack.count() == fCullCount); 1201 1202 if (fCullCount > 0) { 1203 --fCullCount; 1204 this->onPopCull(); 1205 1206 SkDEBUGCODE(fCullStack.pop()); 1207 } 1208} 1209 1210///////////////////////////////////////////////////////////////////////////// 1211 1212void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, 1213 const SkMatrix& matrix, const SkPaint* paint) { 1214 if (bitmap.drawsNothing()) { 1215 return; 1216 } 1217 1218 SkLazyPaint lazy; 1219 if (NULL == paint) { 1220 paint = lazy.init(); 1221 } 1222 1223 SkDEBUGCODE(bitmap.validate();) 1224 CHECK_LOCKCOUNT_BALANCE(bitmap); 1225 1226 SkRect storage; 1227 const SkRect* bounds = NULL; 1228 if (paint && paint->canComputeFastBounds()) { 1229 bitmap.getBounds(&storage); 1230 matrix.mapRect(&storage); 1231 bounds = &paint->computeFastBounds(storage, &storage); 1232 } 1233 1234 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) 1235 1236 while (iter.next()) { 1237 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint()); 1238 } 1239 1240 LOOPER_END 1241} 1242 1243void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, 1244 const SkPaint* paint) { 1245 SkPaint tmp; 1246 if (NULL == paint) { 1247 tmp.setDither(true); 1248 paint = &tmp; 1249 } 1250 1251 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) 1252 while (iter.next()) { 1253 SkBaseDevice* dstDev = iter.fDevice; 1254 paint = &looper.paint(); 1255 SkImageFilter* filter = paint->getImageFilter(); 1256 SkIPoint pos = { x - iter.getX(), y - iter.getY() }; 1257 if (filter && !dstDev->canHandleImageFilter(filter)) { 1258 SkDeviceImageFilterProxy proxy(dstDev); 1259 SkBitmap dst; 1260 SkIPoint offset = SkIPoint::Make(0, 0); 1261 const SkBitmap& src = srcDev->accessBitmap(false); 1262 SkMatrix matrix = *iter.fMatrix; 1263 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y())); 1264 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height()); 1265 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache(); 1266 SkAutoUnref aur(NULL); 1267 if (!cache) { 1268 cache = SkImageFilter::Cache::Create(); 1269 aur.reset(cache); 1270 } 1271 SkImageFilter::Context ctx(matrix, clipBounds, cache); 1272 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) { 1273 SkPaint tmpUnfiltered(*paint); 1274 tmpUnfiltered.setImageFilter(NULL); 1275 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), 1276 tmpUnfiltered); 1277 } 1278 } else { 1279 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint); 1280 } 1281 } 1282 LOOPER_END 1283} 1284 1285void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, 1286 const SkPaint* paint) { 1287 if (bitmap.drawsNothing()) { 1288 return; 1289 } 1290 SkDEBUGCODE(bitmap.validate();) 1291 CHECK_LOCKCOUNT_BALANCE(bitmap); 1292 1293 SkPaint tmp; 1294 if (NULL == paint) { 1295 paint = &tmp; 1296 } 1297 1298 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) 1299 1300 while (iter.next()) { 1301 paint = &looper.paint(); 1302 SkImageFilter* filter = paint->getImageFilter(); 1303 SkIPoint pos = { x - iter.getX(), y - iter.getY() }; 1304 if (filter && !iter.fDevice->canHandleImageFilter(filter)) { 1305 SkDeviceImageFilterProxy proxy(iter.fDevice); 1306 SkBitmap dst; 1307 SkIPoint offset = SkIPoint::Make(0, 0); 1308 SkMatrix matrix = *iter.fMatrix; 1309 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y())); 1310 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); 1311 SkImageFilter::Cache* cache = SkImageFilter::GetExternalCache(); 1312 SkAutoUnref aur(NULL); 1313 if (!cache) { 1314 cache = SkImageFilter::Cache::Create(); 1315 aur.reset(cache); 1316 } 1317 SkImageFilter::Context ctx(matrix, clipBounds, cache); 1318 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) { 1319 SkPaint tmpUnfiltered(*paint); 1320 tmpUnfiltered.setImageFilter(NULL); 1321 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), 1322 tmpUnfiltered); 1323 } 1324 } else { 1325 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint); 1326 } 1327 } 1328 LOOPER_END 1329} 1330 1331///////////////////////////////////////////////////////////////////////////// 1332void SkCanvas::translate(SkScalar dx, SkScalar dy) { 1333 SkMatrix m; 1334 m.setTranslate(dx, dy); 1335 this->concat(m); 1336} 1337 1338void SkCanvas::scale(SkScalar sx, SkScalar sy) { 1339 SkMatrix m; 1340 m.setScale(sx, sy); 1341 this->concat(m); 1342} 1343 1344void SkCanvas::rotate(SkScalar degrees) { 1345 SkMatrix m; 1346 m.setRotate(degrees); 1347 this->concat(m); 1348} 1349 1350void SkCanvas::skew(SkScalar sx, SkScalar sy) { 1351 SkMatrix m; 1352 m.setSkew(sx, sy); 1353 this->concat(m); 1354} 1355 1356void SkCanvas::didConcat(const SkMatrix&) { 1357 // Do nothing. Subclasses may do something. 1358} 1359 1360void SkCanvas::concat(const SkMatrix& matrix) { 1361 if (matrix.isIdentity()) { 1362 return; 1363 } 1364 1365 fDeviceCMDirty = true; 1366 fCachedLocalClipBoundsDirty = true; 1367 fMCRec->fMatrix->preConcat(matrix); 1368 1369 this->didConcat(matrix); 1370} 1371 1372void SkCanvas::didSetMatrix(const SkMatrix&) { 1373 // Do nothing. Subclasses may do something. 1374} 1375 1376void SkCanvas::setMatrix(const SkMatrix& matrix) { 1377 fDeviceCMDirty = true; 1378 fCachedLocalClipBoundsDirty = true; 1379 *fMCRec->fMatrix = matrix; 1380 this->didSetMatrix(matrix); 1381} 1382 1383void SkCanvas::resetMatrix() { 1384 SkMatrix matrix; 1385 1386 matrix.reset(); 1387 this->setMatrix(matrix); 1388} 1389 1390////////////////////////////////////////////////////////////////////////////// 1391 1392void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { 1393 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; 1394 this->onClipRect(rect, op, edgeStyle); 1395} 1396 1397void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { 1398#ifdef SK_ENABLE_CLIP_QUICKREJECT 1399 if (SkRegion::kIntersect_Op == op) { 1400 if (fMCRec->fRasterClip->isEmpty()) { 1401 return false; 1402 } 1403 1404 if (this->quickReject(rect)) { 1405 fDeviceCMDirty = true; 1406 fCachedLocalClipBoundsDirty = true; 1407 1408 fClipStack.clipEmpty(); 1409 return fMCRec->fRasterClip->setEmpty(); 1410 } 1411 } 1412#endif 1413 1414 AutoValidateClip avc(this); 1415 1416 fDeviceCMDirty = true; 1417 fCachedLocalClipBoundsDirty = true; 1418 if (!fAllowSoftClip) { 1419 edgeStyle = kHard_ClipEdgeStyle; 1420 } 1421 1422 if (fMCRec->fMatrix->rectStaysRect()) { 1423 // for these simpler matrices, we can stay a rect even after applying 1424 // the matrix. This means we don't have to a) make a path, and b) tell 1425 // the region code to scan-convert the path, only to discover that it 1426 // is really just a rect. 1427 SkRect r; 1428 1429 fMCRec->fMatrix->mapRect(&r, rect); 1430 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle); 1431 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle); 1432 } else { 1433 // since we're rotated or some such thing, we convert the rect to a path 1434 // and clip against that, since it can handle any matrix. However, to 1435 // avoid recursion in the case where we are subclassed (e.g. Pictures) 1436 // we explicitly call "our" version of clipPath. 1437 SkPath path; 1438 1439 path.addRect(rect); 1440 this->SkCanvas::onClipPath(path, op, edgeStyle); 1441 } 1442} 1443 1444static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip, 1445 const SkPath& devPath, SkRegion::Op op, bool doAA) { 1446 // base is used to limit the size (and therefore memory allocation) of the 1447 // region that results from scan converting devPath. 1448 SkRegion base; 1449 1450 if (SkRegion::kIntersect_Op == op) { 1451 // since we are intersect, we can do better (tighter) with currRgn's 1452 // bounds, than just using the device. However, if currRgn is complex, 1453 // our region blitter may hork, so we do that case in two steps. 1454 if (currClip->isRect()) { 1455 // FIXME: we should also be able to do this when currClip->isBW(), 1456 // but relaxing the test above triggers GM asserts in 1457 // SkRgnBuilder::blitH(). We need to investigate what's going on. 1458 currClip->setPath(devPath, currClip->bwRgn(), doAA); 1459 } else { 1460 base.setRect(currClip->getBounds()); 1461 SkRasterClip clip; 1462 clip.setPath(devPath, base, doAA); 1463 currClip->op(clip, op); 1464 } 1465 } else { 1466 const SkBaseDevice* device = canvas->getDevice(); 1467 if (!device) { 1468 currClip->setEmpty(); 1469 return; 1470 } 1471 1472 base.setRect(0, 0, device->width(), device->height()); 1473 1474 if (SkRegion::kReplace_Op == op) { 1475 currClip->setPath(devPath, base, doAA); 1476 } else { 1477 SkRasterClip clip; 1478 clip.setPath(devPath, base, doAA); 1479 currClip->op(clip, op); 1480 } 1481 } 1482} 1483 1484void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { 1485 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; 1486 if (rrect.isRect()) { 1487 this->onClipRect(rrect.getBounds(), op, edgeStyle); 1488 } else { 1489 this->onClipRRect(rrect, op, edgeStyle); 1490 } 1491} 1492 1493void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { 1494 SkRRect transformedRRect; 1495 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) { 1496 AutoValidateClip avc(this); 1497 1498 fDeviceCMDirty = true; 1499 fCachedLocalClipBoundsDirty = true; 1500 if (!fAllowSoftClip) { 1501 edgeStyle = kHard_ClipEdgeStyle; 1502 } 1503 1504 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle); 1505 1506 SkPath devPath; 1507 devPath.addRRect(transformedRRect); 1508 1509 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle); 1510 return; 1511 } 1512 1513 SkPath path; 1514 path.addRRect(rrect); 1515 // call the non-virtual version 1516 this->SkCanvas::onClipPath(path, op, edgeStyle); 1517} 1518 1519void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { 1520 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; 1521 SkRect r; 1522 if (!path.isInverseFillType() && path.isRect(&r)) { 1523 this->onClipRect(r, op, edgeStyle); 1524 } else { 1525 this->onClipPath(path, op, edgeStyle); 1526 } 1527} 1528 1529void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { 1530#ifdef SK_ENABLE_CLIP_QUICKREJECT 1531 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) { 1532 if (fMCRec->fRasterClip->isEmpty()) { 1533 return false; 1534 } 1535 1536 if (this->quickReject(path.getBounds())) { 1537 fDeviceCMDirty = true; 1538 fCachedLocalClipBoundsDirty = true; 1539 1540 fClipStack.clipEmpty(); 1541 return fMCRec->fRasterClip->setEmpty(); 1542 } 1543 } 1544#endif 1545 1546 AutoValidateClip avc(this); 1547 1548 fDeviceCMDirty = true; 1549 fCachedLocalClipBoundsDirty = true; 1550 if (!fAllowSoftClip) { 1551 edgeStyle = kHard_ClipEdgeStyle; 1552 } 1553 1554 SkPath devPath; 1555 path.transform(*fMCRec->fMatrix, &devPath); 1556 1557 // Check if the transfomation, or the original path itself 1558 // made us empty. Note this can also happen if we contained NaN 1559 // values. computing the bounds detects this, and will set our 1560 // bounds to empty if that is the case. (see SkRect::set(pts, count)) 1561 if (devPath.getBounds().isEmpty()) { 1562 // resetting the path will remove any NaN or other wanky values 1563 // that might upset our scan converter. 1564 devPath.reset(); 1565 } 1566 1567 // if we called path.swap() we could avoid a deep copy of this path 1568 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle); 1569 1570 if (fAllowSimplifyClip) { 1571 devPath.reset(); 1572 devPath.setFillType(SkPath::kInverseEvenOdd_FillType); 1573 const SkClipStack* clipStack = getClipStack(); 1574 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart); 1575 const SkClipStack::Element* element; 1576 while ((element = iter.next())) { 1577 SkClipStack::Element::Type type = element->getType(); 1578 if (type == SkClipStack::Element::kEmpty_Type) { 1579 continue; 1580 } 1581 SkPath operand; 1582 element->asPath(&operand); 1583 SkRegion::Op elementOp = element->getOp(); 1584 if (elementOp == SkRegion::kReplace_Op) { 1585 devPath = operand; 1586 } else { 1587 Op(devPath, operand, (SkPathOp) elementOp, &devPath); 1588 } 1589 // if the prev and curr clips disagree about aa -vs- not, favor the aa request. 1590 // perhaps we need an API change to avoid this sort of mixed-signals about 1591 // clipping. 1592 if (element->isAA()) { 1593 edgeStyle = kSoft_ClipEdgeStyle; 1594 } 1595 } 1596 op = SkRegion::kReplace_Op; 1597 } 1598 1599 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle); 1600} 1601 1602void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op, 1603 bool inverseFilled) { 1604 // This is for updating the clip conservatively using only bounds 1605 // information. 1606 // Contract: 1607 // The current clip must contain the true clip. The true 1608 // clip is the clip that would have normally been computed 1609 // by calls to clipPath and clipRRect 1610 // Objective: 1611 // Keep the current clip as small as possible without 1612 // breaking the contract, using only clip bounding rectangles 1613 // (for performance). 1614 1615 // N.B.: This *never* calls back through a virtual on canvas, so subclasses 1616 // don't have to worry about getting caught in a loop. Thus anywhere 1617 // we call a virtual method, we explicitly prefix it with 1618 // SkCanvas:: to be sure to call the base-class. 1619 1620 if (inverseFilled) { 1621 switch (op) { 1622 case SkRegion::kIntersect_Op: 1623 case SkRegion::kDifference_Op: 1624 // These ops can only shrink the current clip. So leaving 1625 // the clip unchanged conservatively respects the contract. 1626 break; 1627 case SkRegion::kUnion_Op: 1628 case SkRegion::kReplace_Op: 1629 case SkRegion::kReverseDifference_Op: 1630 case SkRegion::kXOR_Op: { 1631 // These ops can grow the current clip up to the extents of 1632 // the input clip, which is inverse filled, so we just set 1633 // the current clip to the device bounds. 1634 SkRect deviceBounds; 1635 SkIRect deviceIBounds; 1636 this->getDevice()->getGlobalBounds(&deviceIBounds); 1637 deviceBounds = SkRect::Make(deviceIBounds); 1638 1639 // set the clip in device space 1640 SkMatrix savedMatrix = this->getTotalMatrix(); 1641 this->SkCanvas::setMatrix(SkMatrix::I()); 1642 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op, 1643 kHard_ClipEdgeStyle); 1644 this->setMatrix(savedMatrix); 1645 break; 1646 } 1647 default: 1648 SkASSERT(0); // unhandled op? 1649 } 1650 } else { 1651 // Not inverse filled 1652 switch (op) { 1653 case SkRegion::kIntersect_Op: 1654 case SkRegion::kUnion_Op: 1655 case SkRegion::kReplace_Op: 1656 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle); 1657 break; 1658 case SkRegion::kDifference_Op: 1659 // Difference can only shrink the current clip. 1660 // Leaving clip unchanged conservatively fullfills the contract. 1661 break; 1662 case SkRegion::kReverseDifference_Op: 1663 // To reverse, we swap in the bounds with a replace op. 1664 // As with difference, leave it unchanged. 1665 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle); 1666 break; 1667 case SkRegion::kXOR_Op: 1668 // Be conservative, based on (A XOR B) always included in (A union B), 1669 // which is always included in (bounds(A) union bounds(B)) 1670 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle); 1671 break; 1672 default: 1673 SkASSERT(0); // unhandled op? 1674 } 1675 } 1676} 1677 1678void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { 1679 this->onClipRegion(rgn, op); 1680} 1681 1682void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) { 1683 AutoValidateClip avc(this); 1684 1685 fDeviceCMDirty = true; 1686 fCachedLocalClipBoundsDirty = true; 1687 1688 // todo: signal fClipStack that we have a region, and therefore (I guess) 1689 // we have to ignore it, and use the region directly? 1690 fClipStack.clipDevRect(rgn.getBounds(), op); 1691 1692 fMCRec->fRasterClip->op(rgn, op); 1693} 1694 1695#ifdef SK_DEBUG 1696void SkCanvas::validateClip() const { 1697 // construct clipRgn from the clipstack 1698 const SkBaseDevice* device = this->getDevice(); 1699 if (!device) { 1700 SkASSERT(this->isClipEmpty()); 1701 return; 1702 } 1703 1704 SkIRect ir; 1705 ir.set(0, 0, device->width(), device->height()); 1706 SkRasterClip tmpClip(ir); 1707 1708 SkClipStack::B2TIter iter(fClipStack); 1709 const SkClipStack::Element* element; 1710 while ((element = iter.next()) != NULL) { 1711 switch (element->getType()) { 1712 case SkClipStack::Element::kRect_Type: 1713 element->getRect().round(&ir); 1714 tmpClip.op(ir, element->getOp()); 1715 break; 1716 case SkClipStack::Element::kEmpty_Type: 1717 tmpClip.setEmpty(); 1718 break; 1719 default: { 1720 SkPath path; 1721 element->asPath(&path); 1722 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA()); 1723 break; 1724 } 1725 } 1726 } 1727} 1728#endif 1729 1730void SkCanvas::replayClips(ClipVisitor* visitor) const { 1731 SkClipStack::B2TIter iter(fClipStack); 1732 const SkClipStack::Element* element; 1733 1734 static const SkRect kEmpty = { 0, 0, 0, 0 }; 1735 while ((element = iter.next()) != NULL) { 1736 switch (element->getType()) { 1737 case SkClipStack::Element::kPath_Type: 1738 visitor->clipPath(element->getPath(), element->getOp(), element->isAA()); 1739 break; 1740 case SkClipStack::Element::kRRect_Type: 1741 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA()); 1742 break; 1743 case SkClipStack::Element::kRect_Type: 1744 visitor->clipRect(element->getRect(), element->getOp(), element->isAA()); 1745 break; 1746 case SkClipStack::Element::kEmpty_Type: 1747 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false); 1748 break; 1749 } 1750 } 1751} 1752 1753/////////////////////////////////////////////////////////////////////////////// 1754 1755bool SkCanvas::isClipEmpty() const { 1756 return fMCRec->fRasterClip->isEmpty(); 1757} 1758 1759bool SkCanvas::isClipRect() const { 1760 return fMCRec->fRasterClip->isRect(); 1761} 1762 1763bool SkCanvas::quickReject(const SkRect& rect) const { 1764 1765 if (!rect.isFinite()) 1766 return true; 1767 1768 if (fMCRec->fRasterClip->isEmpty()) { 1769 return true; 1770 } 1771 1772 if (fMCRec->fMatrix->hasPerspective()) { 1773 SkRect dst; 1774 fMCRec->fMatrix->mapRect(&dst, rect); 1775 SkIRect idst; 1776 dst.roundOut(&idst); 1777 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds()); 1778 } else { 1779 const SkRect& clipR = this->getLocalClipBounds(); 1780 1781 // for speed, do the most likely reject compares first 1782 // TODO: should we use | instead, or compare all 4 at once? 1783 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) { 1784 return true; 1785 } 1786 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) { 1787 return true; 1788 } 1789 return false; 1790 } 1791} 1792 1793bool SkCanvas::quickReject(const SkPath& path) const { 1794 return path.isEmpty() || this->quickReject(path.getBounds()); 1795} 1796 1797bool SkCanvas::getClipBounds(SkRect* bounds) const { 1798 SkIRect ibounds; 1799 if (!this->getClipDeviceBounds(&ibounds)) { 1800 return false; 1801 } 1802 1803 SkMatrix inverse; 1804 // if we can't invert the CTM, we can't return local clip bounds 1805 if (!fMCRec->fMatrix->invert(&inverse)) { 1806 if (bounds) { 1807 bounds->setEmpty(); 1808 } 1809 return false; 1810 } 1811 1812 if (NULL != bounds) { 1813 SkRect r; 1814 // adjust it outwards in case we are antialiasing 1815 const int inset = 1; 1816 1817 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset, 1818 ibounds.fRight + inset, ibounds.fBottom + inset); 1819 inverse.mapRect(bounds, r); 1820 } 1821 return true; 1822} 1823 1824bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const { 1825 const SkRasterClip& clip = *fMCRec->fRasterClip; 1826 if (clip.isEmpty()) { 1827 if (bounds) { 1828 bounds->setEmpty(); 1829 } 1830 return false; 1831 } 1832 1833 if (NULL != bounds) { 1834 *bounds = clip.getBounds(); 1835 } 1836 return true; 1837} 1838 1839const SkMatrix& SkCanvas::getTotalMatrix() const { 1840 return *fMCRec->fMatrix; 1841} 1842 1843#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE 1844SkCanvas::ClipType SkCanvas::getClipType() const { 1845 if (fMCRec->fRasterClip->isEmpty()) { 1846 return kEmpty_ClipType; 1847 } 1848 if (fMCRec->fRasterClip->isRect()) { 1849 return kRect_ClipType; 1850 } 1851 return kComplex_ClipType; 1852} 1853#endif 1854 1855#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP 1856const SkRegion& SkCanvas::getTotalClip() const { 1857 return fMCRec->fRasterClip->forceGetBW(); 1858} 1859#endif 1860 1861const SkRegion& SkCanvas::internal_private_getTotalClip() const { 1862 return fMCRec->fRasterClip->forceGetBW(); 1863} 1864 1865void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const { 1866 path->reset(); 1867 1868 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW(); 1869 if (rgn.isEmpty()) { 1870 return; 1871 } 1872 (void)rgn.getBoundaryPath(path); 1873} 1874 1875GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() { 1876 SkBaseDevice* dev = this->getTopDevice(); 1877 return dev ? dev->accessRenderTarget() : NULL; 1878} 1879 1880SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) { 1881 SkBaseDevice* device = this->getTopDevice(); 1882 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL; 1883} 1884 1885GrContext* SkCanvas::getGrContext() { 1886#if SK_SUPPORT_GPU 1887 SkBaseDevice* device = this->getTopDevice(); 1888 if (NULL != device) { 1889 GrRenderTarget* renderTarget = device->accessRenderTarget(); 1890 if (NULL != renderTarget) { 1891 return renderTarget->getContext(); 1892 } 1893 } 1894#endif 1895 1896 return NULL; 1897 1898} 1899 1900void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner, 1901 const SkPaint& paint) { 1902 if (outer.isEmpty()) { 1903 return; 1904 } 1905 if (inner.isEmpty()) { 1906 this->drawRRect(outer, paint); 1907 return; 1908 } 1909 1910 // We don't have this method (yet), but technically this is what we should 1911 // be able to assert... 1912 // SkASSERT(outer.contains(inner)); 1913 // 1914 // For now at least check for containment of bounds 1915 SkASSERT(outer.getBounds().contains(inner.getBounds())); 1916 1917 this->onDrawDRRect(outer, inner, paint); 1918} 1919 1920////////////////////////////////////////////////////////////////////////////// 1921// These are the virtual drawing methods 1922////////////////////////////////////////////////////////////////////////////// 1923 1924void SkCanvas::clear(SkColor color) { 1925 SkDrawIter iter(this); 1926 this->predrawNotify(); 1927 while (iter.next()) { 1928 iter.fDevice->clear(color); 1929 } 1930} 1931 1932void SkCanvas::onDiscard() { 1933 if (NULL != fSurfaceBase) { 1934 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode); 1935 } 1936} 1937 1938void SkCanvas::drawPaint(const SkPaint& paint) { 1939 this->internalDrawPaint(paint); 1940} 1941 1942void SkCanvas::internalDrawPaint(const SkPaint& paint) { 1943 CHECK_SHADER_NOSETCONTEXT(paint); 1944 1945 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL) 1946 1947 while (iter.next()) { 1948 iter.fDevice->drawPaint(iter, looper.paint()); 1949 } 1950 1951 LOOPER_END 1952} 1953 1954void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], 1955 const SkPaint& paint) { 1956 if ((long)count <= 0) { 1957 return; 1958 } 1959 1960 CHECK_SHADER_NOSETCONTEXT(paint); 1961 1962 SkRect r, storage; 1963 const SkRect* bounds = NULL; 1964 if (paint.canComputeFastBounds()) { 1965 // special-case 2 points (common for drawing a single line) 1966 if (2 == count) { 1967 r.set(pts[0], pts[1]); 1968 } else { 1969 r.set(pts, SkToInt(count)); 1970 } 1971 bounds = &paint.computeFastStrokeBounds(r, &storage); 1972 if (this->quickReject(*bounds)) { 1973 return; 1974 } 1975 } 1976 1977 SkASSERT(pts != NULL); 1978 1979 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds) 1980 1981 while (iter.next()) { 1982 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint()); 1983 } 1984 1985 LOOPER_END 1986} 1987 1988void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) { 1989 CHECK_SHADER_NOSETCONTEXT(paint); 1990 1991 SkRect storage; 1992 const SkRect* bounds = NULL; 1993 if (paint.canComputeFastBounds()) { 1994 bounds = &paint.computeFastBounds(r, &storage); 1995 if (this->quickReject(*bounds)) { 1996 return; 1997 } 1998 } 1999 2000 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds) 2001 2002 while (iter.next()) { 2003 iter.fDevice->drawRect(iter, r, looper.paint()); 2004 } 2005 2006 LOOPER_END 2007} 2008 2009void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) { 2010 CHECK_SHADER_NOSETCONTEXT(paint); 2011 2012 SkRect storage; 2013 const SkRect* bounds = NULL; 2014 if (paint.canComputeFastBounds()) { 2015 bounds = &paint.computeFastBounds(oval, &storage); 2016 if (this->quickReject(*bounds)) { 2017 return; 2018 } 2019 } 2020 2021 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds) 2022 2023 while (iter.next()) { 2024 iter.fDevice->drawOval(iter, oval, looper.paint()); 2025 } 2026 2027 LOOPER_END 2028} 2029 2030void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { 2031 CHECK_SHADER_NOSETCONTEXT(paint); 2032 2033 SkRect storage; 2034 const SkRect* bounds = NULL; 2035 if (paint.canComputeFastBounds()) { 2036 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage); 2037 if (this->quickReject(*bounds)) { 2038 return; 2039 } 2040 } 2041 2042 if (rrect.isRect()) { 2043 // call the non-virtual version 2044 this->SkCanvas::drawRect(rrect.getBounds(), paint); 2045 return; 2046 } else if (rrect.isOval()) { 2047 // call the non-virtual version 2048 this->SkCanvas::drawOval(rrect.getBounds(), paint); 2049 return; 2050 } 2051 2052 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds) 2053 2054 while (iter.next()) { 2055 iter.fDevice->drawRRect(iter, rrect, looper.paint()); 2056 } 2057 2058 LOOPER_END 2059} 2060 2061void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, 2062 const SkPaint& paint) { 2063 CHECK_SHADER_NOSETCONTEXT(paint); 2064 2065 SkRect storage; 2066 const SkRect* bounds = NULL; 2067 if (paint.canComputeFastBounds()) { 2068 bounds = &paint.computeFastBounds(outer.getBounds(), &storage); 2069 if (this->quickReject(*bounds)) { 2070 return; 2071 } 2072 } 2073 2074 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds) 2075 2076 while (iter.next()) { 2077 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint()); 2078 } 2079 2080 LOOPER_END 2081} 2082 2083void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 2084 CHECK_SHADER_NOSETCONTEXT(paint); 2085 2086 if (!path.isFinite()) { 2087 return; 2088 } 2089 2090 SkRect storage; 2091 const SkRect* bounds = NULL; 2092 if (!path.isInverseFillType() && paint.canComputeFastBounds()) { 2093 const SkRect& pathBounds = path.getBounds(); 2094 bounds = &paint.computeFastBounds(pathBounds, &storage); 2095 if (this->quickReject(*bounds)) { 2096 return; 2097 } 2098 } 2099 2100 const SkRect& r = path.getBounds(); 2101 if (r.width() <= 0 && r.height() <= 0) { 2102 if (path.isInverseFillType()) { 2103 this->internalDrawPaint(paint); 2104 } 2105 return; 2106 } 2107 2108 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds) 2109 2110 while (iter.next()) { 2111 iter.fDevice->drawPath(iter, path, looper.paint()); 2112 } 2113 2114 LOOPER_END 2115} 2116 2117void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, 2118 const SkPaint* paint) { 2119 SkDEBUGCODE(bitmap.validate();) 2120 2121 if (NULL == paint || paint->canComputeFastBounds()) { 2122 SkRect bounds = { 2123 x, y, 2124 x + SkIntToScalar(bitmap.width()), 2125 y + SkIntToScalar(bitmap.height()) 2126 }; 2127 if (paint) { 2128 (void)paint->computeFastBounds(bounds, &bounds); 2129 } 2130 if (this->quickReject(bounds)) { 2131 return; 2132 } 2133 } 2134 2135 SkMatrix matrix; 2136 matrix.setTranslate(x, y); 2137 this->internalDrawBitmap(bitmap, matrix, paint); 2138} 2139 2140// this one is non-virtual, so it can be called safely by other canvas apis 2141void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, 2142 const SkRect& dst, const SkPaint* paint, 2143 DrawBitmapRectFlags flags) { 2144 if (bitmap.drawsNothing() || dst.isEmpty()) { 2145 return; 2146 } 2147 2148 CHECK_LOCKCOUNT_BALANCE(bitmap); 2149 2150 SkRect storage; 2151 const SkRect* bounds = &dst; 2152 if (NULL == paint || paint->canComputeFastBounds()) { 2153 if (paint) { 2154 bounds = &paint->computeFastBounds(dst, &storage); 2155 } 2156 if (this->quickReject(*bounds)) { 2157 return; 2158 } 2159 } 2160 2161 SkLazyPaint lazy; 2162 if (NULL == paint) { 2163 paint = lazy.init(); 2164 } 2165 2166 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) 2167 2168 while (iter.next()) { 2169 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags); 2170 } 2171 2172 LOOPER_END 2173} 2174 2175void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, 2176 const SkRect& dst, const SkPaint* paint, 2177 DrawBitmapRectFlags flags) { 2178 SkDEBUGCODE(bitmap.validate();) 2179 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags); 2180} 2181 2182void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, 2183 const SkPaint* paint) { 2184 SkDEBUGCODE(bitmap.validate();) 2185 this->internalDrawBitmap(bitmap, matrix, paint); 2186} 2187 2188void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap, 2189 const SkIRect& center, const SkRect& dst, 2190 const SkPaint* paint) { 2191 if (bitmap.drawsNothing()) { 2192 return; 2193 } 2194 if (NULL == paint || paint->canComputeFastBounds()) { 2195 SkRect storage; 2196 const SkRect* bounds = &dst; 2197 if (paint) { 2198 bounds = &paint->computeFastBounds(dst, &storage); 2199 } 2200 if (this->quickReject(*bounds)) { 2201 return; 2202 } 2203 } 2204 2205 const int32_t w = bitmap.width(); 2206 const int32_t h = bitmap.height(); 2207 2208 SkIRect c = center; 2209 // pin center to the bounds of the bitmap 2210 c.fLeft = SkMax32(0, center.fLeft); 2211 c.fTop = SkMax32(0, center.fTop); 2212 c.fRight = SkPin32(center.fRight, c.fLeft, w); 2213 c.fBottom = SkPin32(center.fBottom, c.fTop, h); 2214 2215 const SkScalar srcX[4] = { 2216 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w) 2217 }; 2218 const SkScalar srcY[4] = { 2219 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h) 2220 }; 2221 SkScalar dstX[4] = { 2222 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft), 2223 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight 2224 }; 2225 SkScalar dstY[4] = { 2226 dst.fTop, dst.fTop + SkIntToScalar(c.fTop), 2227 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom 2228 }; 2229 2230 if (dstX[1] > dstX[2]) { 2231 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width()); 2232 dstX[2] = dstX[1]; 2233 } 2234 2235 if (dstY[1] > dstY[2]) { 2236 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height()); 2237 dstY[2] = dstY[1]; 2238 } 2239 2240 for (int y = 0; y < 3; y++) { 2241 SkRect s, d; 2242 2243 s.fTop = srcY[y]; 2244 s.fBottom = srcY[y+1]; 2245 d.fTop = dstY[y]; 2246 d.fBottom = dstY[y+1]; 2247 for (int x = 0; x < 3; x++) { 2248 s.fLeft = srcX[x]; 2249 s.fRight = srcX[x+1]; 2250 d.fLeft = dstX[x]; 2251 d.fRight = dstX[x+1]; 2252 this->internalDrawBitmapRect(bitmap, &s, d, paint, 2253 kNone_DrawBitmapRectFlag); 2254 } 2255 } 2256} 2257 2258void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, 2259 const SkRect& dst, const SkPaint* paint) { 2260 SkDEBUGCODE(bitmap.validate();) 2261 2262 // Need a device entry-point, so gpu can use a mesh 2263 this->internalDrawBitmapNine(bitmap, center, dst, paint); 2264} 2265 2266class SkDeviceFilteredPaint { 2267public: 2268 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) { 2269 SkBaseDevice::TextFlags flags; 2270 if (device->filterTextFlags(paint, &flags)) { 2271 SkPaint* newPaint = fLazy.set(paint); 2272 newPaint->setFlags(flags.fFlags); 2273 newPaint->setHinting(flags.fHinting); 2274 fPaint = newPaint; 2275 } else { 2276 fPaint = &paint; 2277 } 2278 } 2279 2280 const SkPaint& paint() const { return *fPaint; } 2281 2282private: 2283 const SkPaint* fPaint; 2284 SkLazyPaint fLazy; 2285}; 2286 2287void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint, 2288 const SkRect& r, SkScalar textSize) { 2289 if (paint.getStyle() == SkPaint::kFill_Style) { 2290 draw.fDevice->drawRect(draw, r, paint); 2291 } else { 2292 SkPaint p(paint); 2293 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth())); 2294 draw.fDevice->drawRect(draw, r, p); 2295 } 2296} 2297 2298void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint, 2299 const char text[], size_t byteLength, 2300 SkScalar x, SkScalar y) { 2301 SkASSERT(byteLength == 0 || text != NULL); 2302 2303 // nothing to draw 2304 if (text == NULL || byteLength == 0 || 2305 draw.fClip->isEmpty() || 2306 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { 2307 return; 2308 } 2309 2310 SkScalar width = 0; 2311 SkPoint start; 2312 2313 start.set(0, 0); // to avoid warning 2314 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag | 2315 SkPaint::kStrikeThruText_Flag)) { 2316 width = paint.measureText(text, byteLength); 2317 2318 SkScalar offsetX = 0; 2319 if (paint.getTextAlign() == SkPaint::kCenter_Align) { 2320 offsetX = SkScalarHalf(width); 2321 } else if (paint.getTextAlign() == SkPaint::kRight_Align) { 2322 offsetX = width; 2323 } 2324 start.set(x - offsetX, y); 2325 } 2326 2327 if (0 == width) { 2328 return; 2329 } 2330 2331 uint32_t flags = paint.getFlags(); 2332 2333 if (flags & (SkPaint::kUnderlineText_Flag | 2334 SkPaint::kStrikeThruText_Flag)) { 2335 SkScalar textSize = paint.getTextSize(); 2336 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); 2337 SkRect r; 2338 2339 r.fLeft = start.fX; 2340 r.fRight = start.fX + width; 2341 2342 if (flags & SkPaint::kUnderlineText_Flag) { 2343 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset, 2344 start.fY); 2345 r.fTop = offset; 2346 r.fBottom = offset + height; 2347 DrawRect(draw, paint, r, textSize); 2348 } 2349 if (flags & SkPaint::kStrikeThruText_Flag) { 2350 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, 2351 start.fY); 2352 r.fTop = offset; 2353 r.fBottom = offset + height; 2354 DrawRect(draw, paint, r, textSize); 2355 } 2356 } 2357} 2358 2359void SkCanvas::drawText(const void* text, size_t byteLength, 2360 SkScalar x, SkScalar y, const SkPaint& paint) { 2361 CHECK_SHADER_NOSETCONTEXT(paint); 2362 2363 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) 2364 2365 while (iter.next()) { 2366 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint()); 2367 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint()); 2368 DrawTextDecorations(iter, dfp.paint(), 2369 static_cast<const char*>(text), byteLength, x, y); 2370 } 2371 2372 LOOPER_END 2373} 2374 2375void SkCanvas::drawPosText(const void* text, size_t byteLength, 2376 const SkPoint pos[], const SkPaint& paint) { 2377 CHECK_SHADER_NOSETCONTEXT(paint); 2378 2379 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) 2380 2381 while (iter.next()) { 2382 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint()); 2383 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2, 2384 dfp.paint()); 2385 } 2386 2387 LOOPER_END 2388} 2389 2390void SkCanvas::drawPosTextH(const void* text, size_t byteLength, 2391 const SkScalar xpos[], SkScalar constY, 2392 const SkPaint& paint) { 2393 CHECK_SHADER_NOSETCONTEXT(paint); 2394 2395 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) 2396 2397 while (iter.next()) { 2398 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint()); 2399 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1, 2400 dfp.paint()); 2401 } 2402 2403 LOOPER_END 2404} 2405 2406void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, 2407 const SkPath& path, const SkMatrix* matrix, 2408 const SkPaint& paint) { 2409 CHECK_SHADER_NOSETCONTEXT(paint); 2410 2411 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) 2412 2413 while (iter.next()) { 2414 iter.fDevice->drawTextOnPath(iter, text, byteLength, path, 2415 matrix, looper.paint()); 2416 } 2417 2418 LOOPER_END 2419} 2420 2421void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, 2422 const SkPoint verts[], const SkPoint texs[], 2423 const SkColor colors[], SkXfermode* xmode, 2424 const uint16_t indices[], int indexCount, 2425 const SkPaint& paint) { 2426 CHECK_SHADER_NOSETCONTEXT(paint); 2427 2428 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL) 2429 2430 while (iter.next()) { 2431 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs, 2432 colors, xmode, indices, indexCount, 2433 looper.paint()); 2434 } 2435 2436 LOOPER_END 2437} 2438 2439////////////////////////////////////////////////////////////////////////////// 2440// These methods are NOT virtual, and therefore must call back into virtual 2441// methods, rather than actually drawing themselves. 2442////////////////////////////////////////////////////////////////////////////// 2443 2444void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, 2445 SkXfermode::Mode mode) { 2446 SkPaint paint; 2447 2448 paint.setARGB(a, r, g, b); 2449 if (SkXfermode::kSrcOver_Mode != mode) { 2450 paint.setXfermodeMode(mode); 2451 } 2452 this->drawPaint(paint); 2453} 2454 2455void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) { 2456 SkPaint paint; 2457 2458 paint.setColor(c); 2459 if (SkXfermode::kSrcOver_Mode != mode) { 2460 paint.setXfermodeMode(mode); 2461 } 2462 this->drawPaint(paint); 2463} 2464 2465void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { 2466 SkPoint pt; 2467 2468 pt.set(x, y); 2469 this->drawPoints(kPoints_PointMode, 1, &pt, paint); 2470} 2471 2472void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) { 2473 SkPoint pt; 2474 SkPaint paint; 2475 2476 pt.set(x, y); 2477 paint.setColor(color); 2478 this->drawPoints(kPoints_PointMode, 1, &pt, paint); 2479} 2480 2481void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, 2482 const SkPaint& paint) { 2483 SkPoint pts[2]; 2484 2485 pts[0].set(x0, y0); 2486 pts[1].set(x1, y1); 2487 this->drawPoints(kLines_PointMode, 2, pts, paint); 2488} 2489 2490void SkCanvas::drawRectCoords(SkScalar left, SkScalar top, 2491 SkScalar right, SkScalar bottom, 2492 const SkPaint& paint) { 2493 SkRect r; 2494 2495 r.set(left, top, right, bottom); 2496 this->drawRect(r, paint); 2497} 2498 2499void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, 2500 const SkPaint& paint) { 2501 if (radius < 0) { 2502 radius = 0; 2503 } 2504 2505 SkRect r; 2506 r.set(cx - radius, cy - radius, cx + radius, cy + radius); 2507 this->drawOval(r, paint); 2508} 2509 2510void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry, 2511 const SkPaint& paint) { 2512 if (rx > 0 && ry > 0) { 2513 if (paint.canComputeFastBounds()) { 2514 SkRect storage; 2515 if (this->quickReject(paint.computeFastBounds(r, &storage))) { 2516 return; 2517 } 2518 } 2519 SkRRect rrect; 2520 rrect.setRectXY(r, rx, ry); 2521 this->drawRRect(rrect, paint); 2522 } else { 2523 this->drawRect(r, paint); 2524 } 2525} 2526 2527void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle, 2528 SkScalar sweepAngle, bool useCenter, 2529 const SkPaint& paint) { 2530 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) { 2531 this->drawOval(oval, paint); 2532 } else { 2533 SkPath path; 2534 if (useCenter) { 2535 path.moveTo(oval.centerX(), oval.centerY()); 2536 } 2537 path.arcTo(oval, startAngle, sweepAngle, !useCenter); 2538 if (useCenter) { 2539 path.close(); 2540 } 2541 this->drawPath(path, paint); 2542 } 2543} 2544 2545void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength, 2546 const SkPath& path, SkScalar hOffset, 2547 SkScalar vOffset, const SkPaint& paint) { 2548 SkMatrix matrix; 2549 2550 matrix.setTranslate(hOffset, vOffset); 2551 this->drawTextOnPath(text, byteLength, path, &matrix, paint); 2552} 2553 2554/////////////////////////////////////////////////////////////////////////////// 2555void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) { 2556 SkBaseDevice* device = this->getDevice(); 2557 if (NULL != device) { 2558 device->EXPERIMENTAL_optimize(picture); 2559 } 2560} 2561 2562void SkCanvas::EXPERIMENTAL_purge(SkPicture* picture) { 2563 SkBaseDevice* device = this->getTopDevice(); 2564 if (NULL != device) { 2565 device->EXPERIMENTAL_purge(picture); 2566 } 2567} 2568 2569void SkCanvas::drawPicture(SkPicture& picture) { 2570 SkBaseDevice* device = this->getTopDevice(); 2571 if (NULL != device) { 2572 // Canvas has to first give the device the opportunity to render 2573 // the picture itself. 2574 if (device->EXPERIMENTAL_drawPicture(this, &picture)) { 2575 return; // the device has rendered the entire picture 2576 } 2577 } 2578 2579 picture.draw(this); 2580} 2581 2582/////////////////////////////////////////////////////////////////////////////// 2583/////////////////////////////////////////////////////////////////////////////// 2584 2585SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) { 2586 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small); 2587 2588 SkASSERT(canvas); 2589 2590 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips); 2591 fDone = !fImpl->next(); 2592} 2593 2594SkCanvas::LayerIter::~LayerIter() { 2595 fImpl->~SkDrawIter(); 2596} 2597 2598void SkCanvas::LayerIter::next() { 2599 fDone = !fImpl->next(); 2600} 2601 2602SkBaseDevice* SkCanvas::LayerIter::device() const { 2603 return fImpl->getDevice(); 2604} 2605 2606const SkMatrix& SkCanvas::LayerIter::matrix() const { 2607 return fImpl->getMatrix(); 2608} 2609 2610const SkPaint& SkCanvas::LayerIter::paint() const { 2611 const SkPaint* paint = fImpl->getPaint(); 2612 if (NULL == paint) { 2613 paint = &fDefaultPaint; 2614 } 2615 return *paint; 2616} 2617 2618const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); } 2619int SkCanvas::LayerIter::x() const { return fImpl->getX(); } 2620int SkCanvas::LayerIter::y() const { return fImpl->getY(); } 2621 2622/////////////////////////////////////////////////////////////////////////////// 2623 2624SkCanvas::ClipVisitor::~ClipVisitor() { } 2625 2626/////////////////////////////////////////////////////////////////////////////// 2627 2628static bool supported_for_raster_canvas(const SkImageInfo& info) { 2629 switch (info.alphaType()) { 2630 case kPremul_SkAlphaType: 2631 case kOpaque_SkAlphaType: 2632 break; 2633 default: 2634 return false; 2635 } 2636 2637 switch (info.colorType()) { 2638 case kAlpha_8_SkColorType: 2639 case kRGB_565_SkColorType: 2640 case kN32_SkColorType: 2641 break; 2642 default: 2643 return false; 2644 } 2645 2646 return true; 2647} 2648 2649SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) { 2650 if (!supported_for_raster_canvas(info)) { 2651 return NULL; 2652 } 2653 2654 SkBitmap bitmap; 2655 if (!bitmap.allocPixels(info)) { 2656 return NULL; 2657 } 2658 2659 // should this functionality be moved into allocPixels()? 2660 if (!bitmap.info().isOpaque()) { 2661 bitmap.eraseColor(0); 2662 } 2663 return SkNEW_ARGS(SkCanvas, (bitmap)); 2664} 2665 2666SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) { 2667 if (!supported_for_raster_canvas(info)) { 2668 return NULL; 2669 } 2670 2671 SkBitmap bitmap; 2672 if (!bitmap.installPixels(info, pixels, rowBytes)) { 2673 return NULL; 2674 } 2675 return SkNEW_ARGS(SkCanvas, (bitmap)); 2676} 2677