gfx/skia/trunk/src/core/SkCanvas.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial