michael@0: michael@0: /* michael@0: * Copyright 2008 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkCanvas.h" michael@0: #include "SkBitmapDevice.h" michael@0: #include "SkBounder.h" michael@0: #include "SkDeviceImageFilterProxy.h" michael@0: #include "SkDraw.h" michael@0: #include "SkDrawFilter.h" michael@0: #include "SkDrawLooper.h" michael@0: #include "SkMetaData.h" michael@0: #include "SkPathOps.h" michael@0: #include "SkPicture.h" michael@0: #include "SkRasterClip.h" michael@0: #include "SkRRect.h" michael@0: #include "SkSmallAllocator.h" michael@0: #include "SkSurface_Base.h" michael@0: #include "SkTemplates.h" michael@0: #include "SkTextFormatParams.h" michael@0: #include "SkTLazy.h" michael@0: #include "SkUtils.h" michael@0: michael@0: #if SK_SUPPORT_GPU michael@0: #include "GrRenderTarget.h" michael@0: #endif michael@0: michael@0: // experimental for faster tiled drawing... michael@0: //#define SK_ENABLE_CLIP_QUICKREJECT michael@0: michael@0: //#define SK_TRACE_SAVERESTORE michael@0: michael@0: #ifdef SK_TRACE_SAVERESTORE michael@0: static int gLayerCounter; michael@0: static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); } michael@0: static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); } michael@0: michael@0: static int gRecCounter; michael@0: static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); } michael@0: static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); } michael@0: michael@0: static int gCanvasCounter; michael@0: static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); } michael@0: static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); } michael@0: #else michael@0: #define inc_layer() michael@0: #define dec_layer() michael@0: #define inc_rec() michael@0: #define dec_rec() michael@0: #define inc_canvas() michael@0: #define dec_canvas() michael@0: #endif michael@0: michael@0: #ifdef SK_DEBUG michael@0: #include "SkPixelRef.h" michael@0: michael@0: /* michael@0: * Some pixelref subclasses can support being "locked" from another thread michael@0: * during the lock-scope of skia calling them. In these instances, this balance michael@0: * check will fail, but may not be indicative of a problem, so we allow a build michael@0: * flag to disable this check. michael@0: * michael@0: * Potentially another fix would be to have a (debug-only) virtual or flag on michael@0: * pixelref, which could tell us at runtime if this check is valid. That would michael@0: * eliminate the need for this heavy-handed build check. michael@0: */ michael@0: #ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK michael@0: class AutoCheckLockCountBalance { michael@0: public: michael@0: AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ } michael@0: }; michael@0: #else michael@0: class AutoCheckLockCountBalance { michael@0: public: michael@0: AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) { michael@0: fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0; michael@0: } michael@0: ~AutoCheckLockCountBalance() { michael@0: const int count = fPixelRef ? fPixelRef->getLockCount() : 0; michael@0: SkASSERT(count == fLockCount); michael@0: } michael@0: michael@0: private: michael@0: const SkPixelRef* fPixelRef; michael@0: int fLockCount; michael@0: }; michael@0: #endif michael@0: michael@0: class AutoCheckNoSetContext { michael@0: public: michael@0: AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) { michael@0: this->assertNoSetContext(fPaint); michael@0: } michael@0: ~AutoCheckNoSetContext() { michael@0: this->assertNoSetContext(fPaint); michael@0: } michael@0: michael@0: private: michael@0: const SkPaint& fPaint; michael@0: michael@0: void assertNoSetContext(const SkPaint& paint) { michael@0: SkShader* s = paint.getShader(); michael@0: if (s) { michael@0: SkASSERT(!s->setContextHasBeenCalled()); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: #define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap) michael@0: #define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint) michael@0: michael@0: #else michael@0: #define CHECK_LOCKCOUNT_BALANCE(bitmap) michael@0: #define CHECK_SHADER_NOSETCONTEXT(paint) michael@0: #endif michael@0: michael@0: typedef SkTLazy SkLazyPaint; michael@0: michael@0: void SkCanvas::predrawNotify() { michael@0: if (fSurfaceBase) { michael@0: fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode); michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: /* This is the record we keep for each SkBaseDevice that the user installs. michael@0: The clip/matrix/proc are fields that reflect the top of the save/restore michael@0: stack. Whenever the canvas changes, it marks a dirty flag, and then before michael@0: these are used (assuming we're not on a layer) we rebuild these cache michael@0: values: they reflect the top of the save stack, but translated and clipped michael@0: by the device's XY offset and bitmap-bounds. michael@0: */ michael@0: struct DeviceCM { michael@0: DeviceCM* fNext; michael@0: SkBaseDevice* fDevice; michael@0: SkRasterClip fClip; michael@0: const SkMatrix* fMatrix; michael@0: SkPaint* fPaint; // may be null (in the future) michael@0: michael@0: DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas) michael@0: : fNext(NULL) { michael@0: if (NULL != device) { michael@0: device->ref(); michael@0: device->onAttachToCanvas(canvas); michael@0: } michael@0: fDevice = device; michael@0: fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL; michael@0: } michael@0: michael@0: ~DeviceCM() { michael@0: if (NULL != fDevice) { michael@0: fDevice->onDetachFromCanvas(); michael@0: fDevice->unref(); michael@0: } michael@0: SkDELETE(fPaint); michael@0: } michael@0: michael@0: void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip, michael@0: const SkClipStack& clipStack, SkRasterClip* updateClip) { michael@0: int x = fDevice->getOrigin().x(); michael@0: int y = fDevice->getOrigin().y(); michael@0: int width = fDevice->width(); michael@0: int height = fDevice->height(); michael@0: michael@0: if ((x | y) == 0) { michael@0: fMatrix = &totalMatrix; michael@0: fClip = totalClip; michael@0: } else { michael@0: fMatrixStorage = totalMatrix; michael@0: fMatrixStorage.postTranslate(SkIntToScalar(-x), michael@0: SkIntToScalar(-y)); michael@0: fMatrix = &fMatrixStorage; michael@0: michael@0: totalClip.translate(-x, -y, &fClip); michael@0: } michael@0: michael@0: fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op); michael@0: michael@0: // intersect clip, but don't translate it (yet) michael@0: michael@0: if (updateClip) { michael@0: updateClip->op(SkIRect::MakeXYWH(x, y, width, height), michael@0: SkRegion::kDifference_Op); michael@0: } michael@0: michael@0: fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack); michael@0: michael@0: #ifdef SK_DEBUG michael@0: if (!fClip.isEmpty()) { michael@0: SkIRect deviceR; michael@0: deviceR.set(0, 0, width, height); michael@0: SkASSERT(deviceR.contains(fClip.getBounds())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: private: michael@0: SkMatrix fMatrixStorage; michael@0: }; michael@0: michael@0: /* This is the record we keep for each save/restore level in the stack. michael@0: Since a level optionally copies the matrix and/or stack, we have pointers michael@0: for these fields. If the value is copied for this level, the copy is michael@0: stored in the ...Storage field, and the pointer points to that. If the michael@0: value is not copied for this level, we ignore ...Storage, and just point michael@0: at the corresponding value in the previous level in the stack. michael@0: */ michael@0: class SkCanvas::MCRec { michael@0: public: michael@0: int fFlags; michael@0: SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec michael@0: SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec michael@0: SkDrawFilter* fFilter; // the current filter (or null) michael@0: michael@0: DeviceCM* fLayer; michael@0: /* If there are any layers in the stack, this points to the top-most michael@0: one that is at or below this level in the stack (so we know what michael@0: bitmap/device to draw into from this level. This value is NOT michael@0: reference counted, since the real owner is either our fLayer field, michael@0: or a previous one in a lower level.) michael@0: */ michael@0: DeviceCM* fTopLayer; michael@0: michael@0: MCRec(const MCRec* prev, int flags) : fFlags(flags) { michael@0: if (NULL != prev) { michael@0: if (flags & SkCanvas::kMatrix_SaveFlag) { michael@0: fMatrixStorage = *prev->fMatrix; michael@0: fMatrix = &fMatrixStorage; michael@0: } else { michael@0: fMatrix = prev->fMatrix; michael@0: } michael@0: michael@0: if (flags & SkCanvas::kClip_SaveFlag) { michael@0: fRasterClipStorage = *prev->fRasterClip; michael@0: fRasterClip = &fRasterClipStorage; michael@0: } else { michael@0: fRasterClip = prev->fRasterClip; michael@0: } michael@0: michael@0: fFilter = prev->fFilter; michael@0: SkSafeRef(fFilter); michael@0: michael@0: fTopLayer = prev->fTopLayer; michael@0: } else { // no prev michael@0: fMatrixStorage.reset(); michael@0: michael@0: fMatrix = &fMatrixStorage; michael@0: fRasterClip = &fRasterClipStorage; michael@0: fFilter = NULL; michael@0: fTopLayer = NULL; michael@0: } michael@0: fLayer = NULL; michael@0: michael@0: // don't bother initializing fNext michael@0: inc_rec(); michael@0: } michael@0: ~MCRec() { michael@0: SkSafeUnref(fFilter); michael@0: SkDELETE(fLayer); michael@0: dec_rec(); michael@0: } michael@0: michael@0: private: michael@0: SkMatrix fMatrixStorage; michael@0: SkRasterClip fRasterClipStorage; michael@0: }; michael@0: michael@0: class SkDrawIter : public SkDraw { michael@0: public: michael@0: SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) { michael@0: canvas = canvas->canvasForDrawIter(); michael@0: fCanvas = canvas; michael@0: canvas->updateDeviceCMCache(); michael@0: michael@0: fClipStack = &canvas->fClipStack; michael@0: fBounder = canvas->getBounder(); michael@0: fCurrLayer = canvas->fMCRec->fTopLayer; michael@0: fSkipEmptyClips = skipEmptyClips; michael@0: } michael@0: michael@0: bool next() { michael@0: // skip over recs with empty clips michael@0: if (fSkipEmptyClips) { michael@0: while (fCurrLayer && fCurrLayer->fClip.isEmpty()) { michael@0: fCurrLayer = fCurrLayer->fNext; michael@0: } michael@0: } michael@0: michael@0: const DeviceCM* rec = fCurrLayer; michael@0: if (rec && rec->fDevice) { michael@0: michael@0: fMatrix = rec->fMatrix; michael@0: fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW(); michael@0: fRC = &rec->fClip; michael@0: fDevice = rec->fDevice; michael@0: fBitmap = &fDevice->accessBitmap(true); michael@0: fPaint = rec->fPaint; michael@0: SkDEBUGCODE(this->validate();) michael@0: michael@0: fCurrLayer = rec->fNext; michael@0: if (fBounder) { michael@0: fBounder->setClip(fClip); michael@0: } michael@0: // fCurrLayer may be NULL now michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: SkBaseDevice* getDevice() const { return fDevice; } michael@0: int getX() const { return fDevice->getOrigin().x(); } michael@0: int getY() const { return fDevice->getOrigin().y(); } michael@0: const SkMatrix& getMatrix() const { return *fMatrix; } michael@0: const SkRegion& getClip() const { return *fClip; } michael@0: const SkPaint* getPaint() const { return fPaint; } michael@0: michael@0: private: michael@0: SkCanvas* fCanvas; michael@0: const DeviceCM* fCurrLayer; michael@0: const SkPaint* fPaint; // May be null. michael@0: SkBool8 fSkipEmptyClips; michael@0: michael@0: typedef SkDraw INHERITED; michael@0: }; michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class AutoDrawLooper { michael@0: public: michael@0: AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, michael@0: bool skipLayerForImageFilter = false, michael@0: const SkRect* bounds = NULL) : fOrigPaint(paint) { michael@0: fCanvas = canvas; michael@0: fFilter = canvas->getDrawFilter(); michael@0: fPaint = NULL; michael@0: fSaveCount = canvas->getSaveCount(); michael@0: fDoClearImageFilter = false; michael@0: fDone = false; michael@0: michael@0: if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) { michael@0: SkPaint tmp; michael@0: tmp.setImageFilter(fOrigPaint.getImageFilter()); michael@0: (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag, michael@0: true, SkCanvas::kFullLayer_SaveLayerStrategy); michael@0: // we'll clear the imageFilter for the actual draws in next(), so michael@0: // it will only be applied during the restore(). michael@0: fDoClearImageFilter = true; michael@0: } michael@0: michael@0: if (SkDrawLooper* looper = paint.getLooper()) { michael@0: void* buffer = fLooperContextAllocator.reserveT( michael@0: looper->contextSize()); michael@0: fLooperContext = looper->createContext(canvas, buffer); michael@0: fIsSimple = false; michael@0: } else { michael@0: fLooperContext = NULL; michael@0: // can we be marked as simple? michael@0: fIsSimple = !fFilter && !fDoClearImageFilter; michael@0: } michael@0: } michael@0: michael@0: ~AutoDrawLooper() { michael@0: if (fDoClearImageFilter) { michael@0: fCanvas->internalRestore(); michael@0: } michael@0: SkASSERT(fCanvas->getSaveCount() == fSaveCount); michael@0: } michael@0: michael@0: const SkPaint& paint() const { michael@0: SkASSERT(fPaint); michael@0: return *fPaint; michael@0: } michael@0: michael@0: bool next(SkDrawFilter::Type drawType) { michael@0: if (fDone) { michael@0: return false; michael@0: } else if (fIsSimple) { michael@0: fDone = true; michael@0: fPaint = &fOrigPaint; michael@0: return !fPaint->nothingToDraw(); michael@0: } else { michael@0: return this->doNext(drawType); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: SkLazyPaint fLazyPaint; michael@0: SkCanvas* fCanvas; michael@0: const SkPaint& fOrigPaint; michael@0: SkDrawFilter* fFilter; michael@0: const SkPaint* fPaint; michael@0: int fSaveCount; michael@0: bool fDoClearImageFilter; michael@0: bool fDone; michael@0: bool fIsSimple; michael@0: SkDrawLooper::Context* fLooperContext; michael@0: SkSmallAllocator<1, 32> fLooperContextAllocator; michael@0: michael@0: bool doNext(SkDrawFilter::Type drawType); michael@0: }; michael@0: michael@0: bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) { michael@0: fPaint = NULL; michael@0: SkASSERT(!fIsSimple); michael@0: SkASSERT(fLooperContext || fFilter || fDoClearImageFilter); michael@0: michael@0: SkPaint* paint = fLazyPaint.set(fOrigPaint); michael@0: michael@0: if (fDoClearImageFilter) { michael@0: paint->setImageFilter(NULL); michael@0: } michael@0: michael@0: if (fLooperContext && !fLooperContext->next(fCanvas, paint)) { michael@0: fDone = true; michael@0: return false; michael@0: } michael@0: if (fFilter) { michael@0: if (!fFilter->filter(paint, drawType)) { michael@0: fDone = true; michael@0: return false; michael@0: } michael@0: if (NULL == fLooperContext) { michael@0: // no looper means we only draw once michael@0: fDone = true; michael@0: } michael@0: } michael@0: fPaint = paint; michael@0: michael@0: // if we only came in here for the imagefilter, mark us as done michael@0: if (!fLooperContext && !fFilter) { michael@0: fDone = true; michael@0: } michael@0: michael@0: // call this after any possible paint modifiers michael@0: if (fPaint->nothingToDraw()) { michael@0: fPaint = NULL; michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* Stack helper for managing a SkBounder. In the destructor, if we were michael@0: given a bounder, we call its commit() method, signifying that we are michael@0: done accumulating bounds for that draw. michael@0: */ michael@0: class SkAutoBounderCommit { michael@0: public: michael@0: SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {} michael@0: ~SkAutoBounderCommit() { michael@0: if (NULL != fBounder) { michael@0: fBounder->commit(); michael@0: } michael@0: } michael@0: private: michael@0: SkBounder* fBounder; michael@0: }; michael@0: #define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit) michael@0: michael@0: #include "SkColorPriv.h" michael@0: michael@0: ////////// macros to place around the internal draw calls ////////////////// michael@0: michael@0: #define LOOPER_BEGIN_DRAWDEVICE(paint, type) \ michael@0: this->predrawNotify(); \ michael@0: AutoDrawLooper looper(this, paint, true); \ michael@0: while (looper.next(type)) { \ michael@0: SkAutoBounderCommit ac(fBounder); \ michael@0: SkDrawIter iter(this); michael@0: michael@0: #define LOOPER_BEGIN(paint, type, bounds) \ michael@0: this->predrawNotify(); \ michael@0: AutoDrawLooper looper(this, paint, false, bounds); \ michael@0: while (looper.next(type)) { \ michael@0: SkAutoBounderCommit ac(fBounder); \ michael@0: SkDrawIter iter(this); michael@0: michael@0: #define LOOPER_END } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkBaseDevice* SkCanvas::init(SkBaseDevice* device) { michael@0: fBounder = NULL; michael@0: fCachedLocalClipBounds.setEmpty(); michael@0: fCachedLocalClipBoundsDirty = true; michael@0: fAllowSoftClip = true; michael@0: fAllowSimplifyClip = false; michael@0: fDeviceCMDirty = false; michael@0: fSaveLayerCount = 0; michael@0: fCullCount = 0; michael@0: fMetaData = NULL; michael@0: michael@0: fMCRec = (MCRec*)fMCStack.push_back(); michael@0: new (fMCRec) MCRec(NULL, 0); michael@0: michael@0: fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL)); michael@0: fMCRec->fTopLayer = fMCRec->fLayer; michael@0: michael@0: fSurfaceBase = NULL; michael@0: michael@0: return this->setRootDevice(device); michael@0: } michael@0: michael@0: SkCanvas::SkCanvas() michael@0: : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) michael@0: { michael@0: inc_canvas(); michael@0: michael@0: this->init(NULL); michael@0: } michael@0: michael@0: SkCanvas::SkCanvas(int width, int height) michael@0: : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) michael@0: { michael@0: inc_canvas(); michael@0: michael@0: SkBitmap bitmap; michael@0: bitmap.setConfig(SkImageInfo::MakeUnknown(width, height)); michael@0: this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref(); michael@0: } michael@0: michael@0: SkCanvas::SkCanvas(SkBaseDevice* device) michael@0: : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) michael@0: { michael@0: inc_canvas(); michael@0: michael@0: this->init(device); michael@0: } michael@0: michael@0: SkCanvas::SkCanvas(const SkBitmap& bitmap) michael@0: : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) michael@0: { michael@0: inc_canvas(); michael@0: michael@0: this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref(); michael@0: } michael@0: michael@0: SkCanvas::~SkCanvas() { michael@0: // free up the contents of our deque michael@0: this->restoreToCount(1); // restore everything but the last michael@0: SkASSERT(0 == fSaveLayerCount); michael@0: michael@0: this->internalRestore(); // restore the last, since we're going away michael@0: michael@0: SkSafeUnref(fBounder); michael@0: SkDELETE(fMetaData); michael@0: michael@0: dec_canvas(); michael@0: } michael@0: michael@0: SkBounder* SkCanvas::setBounder(SkBounder* bounder) { michael@0: SkRefCnt_SafeAssign(fBounder, bounder); michael@0: return bounder; michael@0: } michael@0: michael@0: SkDrawFilter* SkCanvas::getDrawFilter() const { michael@0: return fMCRec->fFilter; michael@0: } michael@0: michael@0: SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) { michael@0: SkRefCnt_SafeAssign(fMCRec->fFilter, filter); michael@0: return filter; michael@0: } michael@0: michael@0: SkMetaData& SkCanvas::getMetaData() { michael@0: // metadata users are rare, so we lazily allocate it. If that changes we michael@0: // can decide to just make it a field in the device (rather than a ptr) michael@0: if (NULL == fMetaData) { michael@0: fMetaData = new SkMetaData; michael@0: } michael@0: return *fMetaData; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkCanvas::flush() { michael@0: SkBaseDevice* device = this->getDevice(); michael@0: if (device) { michael@0: device->flush(); michael@0: } michael@0: } michael@0: michael@0: SkISize SkCanvas::getTopLayerSize() const { michael@0: SkBaseDevice* d = this->getTopDevice(); michael@0: return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); michael@0: } michael@0: michael@0: SkIPoint SkCanvas::getTopLayerOrigin() const { michael@0: SkBaseDevice* d = this->getTopDevice(); michael@0: return d ? d->getOrigin() : SkIPoint::Make(0, 0); michael@0: } michael@0: michael@0: SkISize SkCanvas::getBaseLayerSize() const { michael@0: SkBaseDevice* d = this->getDevice(); michael@0: return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); michael@0: } michael@0: michael@0: SkBaseDevice* SkCanvas::getDevice() const { michael@0: // return root device michael@0: MCRec* rec = (MCRec*) fMCStack.front(); michael@0: SkASSERT(rec && rec->fLayer); michael@0: return rec->fLayer->fDevice; michael@0: } michael@0: michael@0: SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const { michael@0: if (updateMatrixClip) { michael@0: const_cast(this)->updateDeviceCMCache(); michael@0: } michael@0: return fMCRec->fTopLayer->fDevice; michael@0: } michael@0: michael@0: SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) { michael@0: // return root device michael@0: SkDeque::F2BIter iter(fMCStack); michael@0: MCRec* rec = (MCRec*)iter.next(); michael@0: SkASSERT(rec && rec->fLayer); michael@0: SkBaseDevice* rootDevice = rec->fLayer->fDevice; michael@0: michael@0: if (rootDevice == device) { michael@0: return device; michael@0: } michael@0: michael@0: if (device) { michael@0: device->onAttachToCanvas(this); michael@0: } michael@0: if (rootDevice) { michael@0: rootDevice->onDetachFromCanvas(); michael@0: } michael@0: michael@0: SkRefCnt_SafeAssign(rec->fLayer->fDevice, device); michael@0: rootDevice = device; michael@0: michael@0: fDeviceCMDirty = true; michael@0: michael@0: /* Now we update our initial region to have the bounds of the new device, michael@0: and then intersect all of the clips in our stack with these bounds, michael@0: to ensure that we can't draw outside of the device's bounds (and trash michael@0: memory). michael@0: michael@0: NOTE: this is only a partial-fix, since if the new device is larger than michael@0: the previous one, we don't know how to "enlarge" the clips in our stack, michael@0: so drawing may be artificially restricted. Without keeping a history of michael@0: all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly michael@0: reconstruct the correct clips, so this approximation will have to do. michael@0: The caller really needs to restore() back to the base if they want to michael@0: accurately take advantage of the new device bounds. michael@0: */ michael@0: michael@0: SkIRect bounds; michael@0: if (device) { michael@0: bounds.set(0, 0, device->width(), device->height()); michael@0: } else { michael@0: bounds.setEmpty(); michael@0: } michael@0: // now jam our 1st clip to be bounds, and intersect the rest with that michael@0: rec->fRasterClip->setRect(bounds); michael@0: while ((rec = (MCRec*)iter.next()) != NULL) { michael@0: (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op); michael@0: } michael@0: michael@0: return device; michael@0: } michael@0: michael@0: bool SkCanvas::readPixels(SkBitmap* bitmap, michael@0: int x, int y, michael@0: Config8888 config8888) { michael@0: SkBaseDevice* device = this->getDevice(); michael@0: if (!device) { michael@0: return false; michael@0: } michael@0: return device->readPixels(bitmap, x, y, config8888); michael@0: } michael@0: michael@0: bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { michael@0: SkBaseDevice* device = this->getDevice(); michael@0: if (!device) { michael@0: return false; michael@0: } michael@0: michael@0: SkIRect bounds; michael@0: bounds.set(0, 0, device->width(), device->height()); michael@0: if (!bounds.intersect(srcRect)) { michael@0: return false; michael@0: } michael@0: michael@0: SkBitmap tmp; michael@0: tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), michael@0: bounds.height()); michael@0: if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) { michael@0: bitmap->swap(tmp); michael@0: return true; michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: #ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG michael@0: void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y, michael@0: Config8888 config8888) { michael@0: SkBaseDevice* device = this->getDevice(); michael@0: if (device) { michael@0: if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()), michael@0: SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) { michael@0: device->accessBitmap(true); michael@0: device->writePixels(bitmap, x, y, config8888); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) { michael@0: if (bitmap.getTexture()) { michael@0: return false; michael@0: } michael@0: SkBitmap bm(bitmap); michael@0: bm.lockPixels(); michael@0: if (bm.getPixels()) { michael@0: return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes, michael@0: int x, int y) { michael@0: switch (origInfo.colorType()) { michael@0: case kUnknown_SkColorType: michael@0: case kIndex_8_SkColorType: michael@0: return false; michael@0: default: michael@0: break; michael@0: } michael@0: if (NULL == pixels || rowBytes < origInfo.minRowBytes()) { michael@0: return false; michael@0: } michael@0: michael@0: const SkISize size = this->getBaseLayerSize(); michael@0: SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height()); michael@0: if (!target.intersect(0, 0, size.width(), size.height())) { michael@0: return false; michael@0: } michael@0: michael@0: SkBaseDevice* device = this->getDevice(); michael@0: if (!device) { michael@0: return false; michael@0: } michael@0: michael@0: SkImageInfo info = origInfo; michael@0: // the intersect may have shrunk info's logical size michael@0: info.fWidth = target.width(); michael@0: info.fHeight = target.height(); michael@0: michael@0: // if x or y are negative, then we have to adjust pixels michael@0: if (x > 0) { michael@0: x = 0; michael@0: } michael@0: if (y > 0) { michael@0: y = 0; michael@0: } michael@0: // here x,y are either 0 or negative michael@0: pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel()); michael@0: michael@0: // The device can assert that the requested area is always contained in its bounds michael@0: return device->writePixelsDirect(info, pixels, rowBytes, target.x(), target.y()); michael@0: } michael@0: michael@0: SkCanvas* SkCanvas::canvasForDrawIter() { michael@0: return this; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkCanvas::updateDeviceCMCache() { michael@0: if (fDeviceCMDirty) { michael@0: const SkMatrix& totalMatrix = this->getTotalMatrix(); michael@0: const SkRasterClip& totalClip = *fMCRec->fRasterClip; michael@0: DeviceCM* layer = fMCRec->fTopLayer; michael@0: michael@0: if (NULL == layer->fNext) { // only one layer michael@0: layer->updateMC(totalMatrix, totalClip, fClipStack, NULL); michael@0: } else { michael@0: SkRasterClip clip(totalClip); michael@0: do { michael@0: layer->updateMC(totalMatrix, clip, fClipStack, &clip); michael@0: } while ((layer = layer->fNext) != NULL); michael@0: } michael@0: fDeviceCMDirty = false; michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: int SkCanvas::internalSave(SaveFlags flags) { michael@0: int saveCount = this->getSaveCount(); // record this before the actual save michael@0: michael@0: MCRec* newTop = (MCRec*)fMCStack.push_back(); michael@0: new (newTop) MCRec(fMCRec, flags); // balanced in restore() michael@0: michael@0: fMCRec = newTop; michael@0: michael@0: if (SkCanvas::kClip_SaveFlag & flags) { michael@0: fClipStack.save(); michael@0: } michael@0: michael@0: return saveCount; michael@0: } michael@0: michael@0: void SkCanvas::willSave(SaveFlags) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: int SkCanvas::save(SaveFlags flags) { michael@0: this->willSave(flags); michael@0: // call shared impl michael@0: return this->internalSave(flags); michael@0: } michael@0: michael@0: static bool bounds_affects_clip(SkCanvas::SaveFlags flags) { michael@0: #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG michael@0: return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0; michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags, michael@0: SkIRect* intersection, const SkImageFilter* imageFilter) { michael@0: SkIRect clipBounds; michael@0: SkRegion::Op op = SkRegion::kIntersect_Op; michael@0: if (!this->getClipDeviceBounds(&clipBounds)) { michael@0: return false; michael@0: } michael@0: michael@0: if (imageFilter) { michael@0: imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds); michael@0: // Filters may grow the bounds beyond the device bounds. michael@0: op = SkRegion::kReplace_Op; michael@0: } michael@0: SkIRect ir; michael@0: if (NULL != bounds) { michael@0: SkRect r; michael@0: michael@0: this->getTotalMatrix().mapRect(&r, *bounds); michael@0: r.roundOut(&ir); michael@0: // early exit if the layer's bounds are clipped out michael@0: if (!ir.intersect(clipBounds)) { michael@0: if (bounds_affects_clip(flags)) { michael@0: fMCRec->fRasterClip->setEmpty(); michael@0: } michael@0: return false; michael@0: } michael@0: } else { // no user bounds, so just use the clip michael@0: ir = clipBounds; michael@0: } michael@0: michael@0: if (bounds_affects_clip(flags)) { michael@0: fClipStack.clipDevRect(ir, op); michael@0: // early exit if the clip is now empty michael@0: if (!fMCRec->fRasterClip->op(ir, op)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (intersection) { michael@0: *intersection = ir; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) { michael@0: michael@0: // Do nothing. Subclasses may do something. michael@0: return kFullLayer_SaveLayerStrategy; michael@0: } michael@0: michael@0: int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, michael@0: SaveFlags flags) { michael@0: // Overriding classes may return false to signal that we don't need to create a layer. michael@0: SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags); michael@0: return this->internalSaveLayer(bounds, paint, flags, false, strategy); michael@0: } michael@0: michael@0: static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas, michael@0: const SkImageInfo& info) { michael@0: SkBaseDevice* device = canvas->getDevice(); michael@0: return device ? device->createCompatibleDevice(info) : NULL; michael@0: } michael@0: michael@0: int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags, michael@0: bool justForImageFilter, SaveLayerStrategy strategy) { michael@0: #ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG michael@0: flags = (SaveFlags)(flags | kClipToLayer_SaveFlag); michael@0: #endif michael@0: michael@0: // do this before we create the layer. We don't call the public save() since michael@0: // that would invoke a possibly overridden virtual michael@0: int count = this->internalSave(flags); michael@0: michael@0: fDeviceCMDirty = true; michael@0: michael@0: SkIRect ir; michael@0: if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) { michael@0: return count; michael@0: } michael@0: michael@0: // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about michael@0: // the clipRectBounds() call above? michael@0: if (kNoLayer_SaveLayerStrategy == strategy) { michael@0: return count; michael@0: } michael@0: michael@0: // Kill the imagefilter if our device doesn't allow it michael@0: SkLazyPaint lazyP; michael@0: if (paint && paint->getImageFilter()) { michael@0: if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) { michael@0: if (justForImageFilter) { michael@0: // early exit if the layer was just for the imageFilter michael@0: return count; michael@0: } michael@0: SkPaint* p = lazyP.set(*paint); michael@0: p->setImageFilter(NULL); michael@0: paint = p; michael@0: } michael@0: } michael@0: michael@0: bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag); michael@0: SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(), michael@0: isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); michael@0: michael@0: SkBaseDevice* device; michael@0: if (paint && paint->getImageFilter()) { michael@0: device = createCompatibleDevice(this, info); michael@0: } else { michael@0: device = this->createLayerDevice(info); michael@0: } michael@0: if (NULL == device) { michael@0: SkDebugf("Unable to create device for layer."); michael@0: return count; michael@0: } michael@0: michael@0: device->setOrigin(ir.fLeft, ir.fTop); michael@0: DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this)); michael@0: device->unref(); michael@0: michael@0: layer->fNext = fMCRec->fTopLayer; michael@0: fMCRec->fLayer = layer; michael@0: fMCRec->fTopLayer = layer; // this field is NOT an owner of layer michael@0: michael@0: fSaveLayerCount += 1; michael@0: return count; michael@0: } michael@0: michael@0: int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha, michael@0: SaveFlags flags) { michael@0: if (0xFF == alpha) { michael@0: return this->saveLayer(bounds, NULL, flags); michael@0: } else { michael@0: SkPaint tmpPaint; michael@0: tmpPaint.setAlpha(alpha); michael@0: return this->saveLayer(bounds, &tmpPaint, flags); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::willRestore() { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: void SkCanvas::restore() { michael@0: // check for underflow michael@0: if (fMCStack.count() > 1) { michael@0: this->willRestore(); michael@0: this->internalRestore(); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::internalRestore() { michael@0: SkASSERT(fMCStack.count() != 0); michael@0: michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: michael@0: if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) { michael@0: fClipStack.restore(); michael@0: } michael@0: michael@0: // reserve our layer (if any) michael@0: DeviceCM* layer = fMCRec->fLayer; // may be null michael@0: // now detach it from fMCRec so we can pop(). Gets freed after its drawn michael@0: fMCRec->fLayer = NULL; michael@0: michael@0: // now do the normal restore() michael@0: fMCRec->~MCRec(); // balanced in save() michael@0: fMCStack.pop_back(); michael@0: fMCRec = (MCRec*)fMCStack.back(); michael@0: michael@0: /* Time to draw the layer's offscreen. We can't call the public drawSprite, michael@0: since if we're being recorded, we don't want to record this (the michael@0: recorder will have already recorded the restore). michael@0: */ michael@0: if (NULL != layer) { michael@0: if (layer->fNext) { michael@0: const SkIPoint& origin = layer->fDevice->getOrigin(); michael@0: this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), michael@0: layer->fPaint); michael@0: // reset this, since internalDrawDevice will have set it to true michael@0: fDeviceCMDirty = true; michael@0: michael@0: SkASSERT(fSaveLayerCount > 0); michael@0: fSaveLayerCount -= 1; michael@0: } michael@0: SkDELETE(layer); michael@0: } michael@0: } michael@0: michael@0: int SkCanvas::getSaveCount() const { michael@0: return fMCStack.count(); michael@0: } michael@0: michael@0: void SkCanvas::restoreToCount(int count) { michael@0: // sanity check michael@0: if (count < 1) { michael@0: count = 1; michael@0: } michael@0: michael@0: int n = this->getSaveCount() - count; michael@0: for (int i = 0; i < n; ++i) { michael@0: this->restore(); michael@0: } michael@0: } michael@0: michael@0: bool SkCanvas::isDrawingToLayer() const { michael@0: return fSaveLayerCount > 0; michael@0: } michael@0: michael@0: SkSurface* SkCanvas::newSurface(const SkImageInfo& info) { michael@0: return this->onNewSurface(info); michael@0: } michael@0: michael@0: SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) { michael@0: SkBaseDevice* dev = this->getDevice(); michael@0: return dev ? dev->newSurface(info) : NULL; michael@0: } michael@0: michael@0: SkImageInfo SkCanvas::imageInfo() const { michael@0: SkBaseDevice* dev = this->getDevice(); michael@0: if (dev) { michael@0: return dev->imageInfo(); michael@0: } else { michael@0: return SkImageInfo::MakeUnknown(0, 0); michael@0: } michael@0: } michael@0: michael@0: const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) { michael@0: return this->onPeekPixels(info, rowBytes); michael@0: } michael@0: michael@0: const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) { michael@0: SkBaseDevice* dev = this->getDevice(); michael@0: return dev ? dev->peekPixels(info, rowBytes) : NULL; michael@0: } michael@0: michael@0: void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) { michael@0: return this->onAccessTopLayerPixels(info, rowBytes); michael@0: } michael@0: michael@0: void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) { michael@0: SkBaseDevice* dev = this->getTopDevice(); michael@0: return dev ? dev->accessPixels(info, rowBytes) : NULL; michael@0: } michael@0: michael@0: SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) { michael@0: fAddr = canvas->peekPixels(&fInfo, &fRowBytes); michael@0: if (NULL == fAddr) { michael@0: fInfo = canvas->imageInfo(); michael@0: if (kUnknown_SkColorType == fInfo.colorType() || michael@0: !fBitmap.allocPixels(fInfo)) michael@0: { michael@0: return; // failure, fAddr is NULL michael@0: } michael@0: fBitmap.lockPixels(); michael@0: if (!canvas->readPixels(&fBitmap, 0, 0)) { michael@0: return; // failure, fAddr is NULL michael@0: } michael@0: fAddr = fBitmap.getPixels(); michael@0: fRowBytes = fBitmap.rowBytes(); michael@0: } michael@0: SkASSERT(fAddr); // success michael@0: } michael@0: michael@0: bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const { michael@0: if (fAddr) { michael@0: return bitmap->installPixels(fInfo, const_cast(fAddr), fRowBytes, michael@0: NULL, NULL); michael@0: } else { michael@0: bitmap->reset(); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::onPushCull(const SkRect& cullRect) { michael@0: // do nothing. Subclasses may do something michael@0: } michael@0: michael@0: void SkCanvas::onPopCull() { michael@0: // do nothing. Subclasses may do something michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, michael@0: const SkMatrix& matrix, const SkPaint* paint) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: michael@0: SkLazyPaint lazy; michael@0: if (NULL == paint) { michael@0: paint = lazy.init(); michael@0: } michael@0: michael@0: SkDEBUGCODE(bitmap.validate();) michael@0: CHECK_LOCKCOUNT_BALANCE(bitmap); michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = NULL; michael@0: if (paint && paint->canComputeFastBounds()) { michael@0: bitmap.getBounds(&storage); michael@0: matrix.mapRect(&storage); michael@0: bounds = &paint->computeFastBounds(storage, &storage); michael@0: } michael@0: michael@0: LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, michael@0: const SkPaint* paint) { michael@0: SkPaint tmp; michael@0: if (NULL == paint) { michael@0: tmp.setDither(true); michael@0: paint = &tmp; michael@0: } michael@0: michael@0: LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) michael@0: while (iter.next()) { michael@0: SkBaseDevice* dstDev = iter.fDevice; michael@0: paint = &looper.paint(); michael@0: SkImageFilter* filter = paint->getImageFilter(); michael@0: SkIPoint pos = { x - iter.getX(), y - iter.getY() }; michael@0: if (filter && !dstDev->canHandleImageFilter(filter)) { michael@0: SkDeviceImageFilterProxy proxy(dstDev); michael@0: SkBitmap dst; michael@0: SkIPoint offset = SkIPoint::Make(0, 0); michael@0: const SkBitmap& src = srcDev->accessBitmap(false); michael@0: SkMatrix matrix = *iter.fMatrix; michael@0: matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); michael@0: SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height()); michael@0: SkImageFilter::Context ctx(matrix, clipBounds); michael@0: if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) { michael@0: SkPaint tmpUnfiltered(*paint); michael@0: tmpUnfiltered.setImageFilter(NULL); michael@0: dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), michael@0: tmpUnfiltered); michael@0: } michael@0: } else { michael@0: dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint); michael@0: } michael@0: } michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, michael@0: const SkPaint* paint) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: SkDEBUGCODE(bitmap.validate();) michael@0: CHECK_LOCKCOUNT_BALANCE(bitmap); michael@0: michael@0: SkPaint tmp; michael@0: if (NULL == paint) { michael@0: paint = &tmp; michael@0: } michael@0: michael@0: LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type) michael@0: michael@0: while (iter.next()) { michael@0: paint = &looper.paint(); michael@0: SkImageFilter* filter = paint->getImageFilter(); michael@0: SkIPoint pos = { x - iter.getX(), y - iter.getY() }; michael@0: if (filter && !iter.fDevice->canHandleImageFilter(filter)) { michael@0: SkDeviceImageFilterProxy proxy(iter.fDevice); michael@0: SkBitmap dst; michael@0: SkIPoint offset = SkIPoint::Make(0, 0); michael@0: SkMatrix matrix = *iter.fMatrix; michael@0: matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); michael@0: SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); michael@0: SkImageFilter::Context ctx(matrix, clipBounds); michael@0: if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) { michael@0: SkPaint tmpUnfiltered(*paint); michael@0: tmpUnfiltered.setImageFilter(NULL); michael@0: iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), michael@0: tmpUnfiltered); michael@0: } michael@0: } else { michael@0: iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint); michael@0: } michael@0: } michael@0: LOOPER_END michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: void SkCanvas::didTranslate(SkScalar, SkScalar) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: bool SkCanvas::translate(SkScalar dx, SkScalar dy) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: bool res = fMCRec->fMatrix->preTranslate(dx, dy); michael@0: michael@0: this->didTranslate(dx, dy); michael@0: return res; michael@0: } michael@0: michael@0: void SkCanvas::didScale(SkScalar, SkScalar) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: bool SkCanvas::scale(SkScalar sx, SkScalar sy) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: bool res = fMCRec->fMatrix->preScale(sx, sy); michael@0: michael@0: this->didScale(sx, sy); michael@0: return res; michael@0: } michael@0: michael@0: void SkCanvas::didRotate(SkScalar) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: bool SkCanvas::rotate(SkScalar degrees) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: bool res = fMCRec->fMatrix->preRotate(degrees); michael@0: michael@0: this->didRotate(degrees); michael@0: return res; michael@0: } michael@0: michael@0: void SkCanvas::didSkew(SkScalar, SkScalar) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: bool SkCanvas::skew(SkScalar sx, SkScalar sy) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: bool res = fMCRec->fMatrix->preSkew(sx, sy); michael@0: michael@0: this->didSkew(sx, sy); michael@0: return res; michael@0: } michael@0: michael@0: void SkCanvas::didConcat(const SkMatrix&) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: bool SkCanvas::concat(const SkMatrix& matrix) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: bool res = fMCRec->fMatrix->preConcat(matrix); michael@0: michael@0: this->didConcat(matrix); michael@0: return res; michael@0: } michael@0: michael@0: void SkCanvas::didSetMatrix(const SkMatrix&) { michael@0: // Do nothing. Subclasses may do something. michael@0: } michael@0: michael@0: void SkCanvas::setMatrix(const SkMatrix& matrix) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: *fMCRec->fMatrix = matrix; michael@0: this->didSetMatrix(matrix); michael@0: } michael@0: michael@0: void SkCanvas::resetMatrix() { michael@0: SkMatrix matrix; michael@0: michael@0: matrix.reset(); michael@0: this->setMatrix(matrix); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { michael@0: ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; michael@0: this->onClipRect(rect, op, edgeStyle); michael@0: } michael@0: michael@0: void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { michael@0: #ifdef SK_ENABLE_CLIP_QUICKREJECT michael@0: if (SkRegion::kIntersect_Op == op) { michael@0: if (fMCRec->fRasterClip->isEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: if (this->quickReject(rect)) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: michael@0: fClipStack.clipEmpty(); michael@0: return fMCRec->fRasterClip->setEmpty(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: AutoValidateClip avc(this); michael@0: michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: if (!fAllowSoftClip) { michael@0: edgeStyle = kHard_ClipEdgeStyle; michael@0: } michael@0: michael@0: if (fMCRec->fMatrix->rectStaysRect()) { michael@0: // for these simpler matrices, we can stay a rect even after applying michael@0: // the matrix. This means we don't have to a) make a path, and b) tell michael@0: // the region code to scan-convert the path, only to discover that it michael@0: // is really just a rect. michael@0: SkRect r; michael@0: michael@0: fMCRec->fMatrix->mapRect(&r, rect); michael@0: fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: } else { michael@0: // since we're rotated or some such thing, we convert the rect to a path michael@0: // and clip against that, since it can handle any matrix. However, to michael@0: // avoid recursion in the case where we are subclassed (e.g. Pictures) michael@0: // we explicitly call "our" version of clipPath. michael@0: SkPath path; michael@0: michael@0: path.addRect(rect); michael@0: this->SkCanvas::onClipPath(path, op, edgeStyle); michael@0: } michael@0: } michael@0: michael@0: static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip, michael@0: const SkPath& devPath, SkRegion::Op op, bool doAA) { michael@0: // base is used to limit the size (and therefore memory allocation) of the michael@0: // region that results from scan converting devPath. michael@0: SkRegion base; michael@0: michael@0: if (SkRegion::kIntersect_Op == op) { michael@0: // since we are intersect, we can do better (tighter) with currRgn's michael@0: // bounds, than just using the device. However, if currRgn is complex, michael@0: // our region blitter may hork, so we do that case in two steps. michael@0: if (currClip->isRect()) { michael@0: // FIXME: we should also be able to do this when currClip->isBW(), michael@0: // but relaxing the test above triggers GM asserts in michael@0: // SkRgnBuilder::blitH(). We need to investigate what's going on. michael@0: currClip->setPath(devPath, currClip->bwRgn(), doAA); michael@0: } else { michael@0: base.setRect(currClip->getBounds()); michael@0: SkRasterClip clip; michael@0: clip.setPath(devPath, base, doAA); michael@0: currClip->op(clip, op); michael@0: } michael@0: } else { michael@0: const SkBaseDevice* device = canvas->getDevice(); michael@0: if (!device) { michael@0: currClip->setEmpty(); michael@0: return; michael@0: } michael@0: michael@0: base.setRect(0, 0, device->width(), device->height()); michael@0: michael@0: if (SkRegion::kReplace_Op == op) { michael@0: currClip->setPath(devPath, base, doAA); michael@0: } else { michael@0: SkRasterClip clip; michael@0: clip.setPath(devPath, base, doAA); michael@0: currClip->op(clip, op); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { michael@0: ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; michael@0: if (rrect.isRect()) { michael@0: this->onClipRect(rrect.getBounds(), op, edgeStyle); michael@0: } else { michael@0: this->onClipRRect(rrect, op, edgeStyle); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { michael@0: SkRRect transformedRRect; michael@0: if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) { michael@0: AutoValidateClip avc(this); michael@0: michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: if (!fAllowSoftClip) { michael@0: edgeStyle = kHard_ClipEdgeStyle; michael@0: } michael@0: michael@0: fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: michael@0: SkPath devPath; michael@0: devPath.addRRect(transformedRRect); michael@0: michael@0: clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: return; michael@0: } michael@0: michael@0: SkPath path; michael@0: path.addRRect(rrect); michael@0: // call the non-virtual version michael@0: this->SkCanvas::onClipPath(path, op, edgeStyle); michael@0: } michael@0: michael@0: void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { michael@0: ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; michael@0: SkRect r; michael@0: if (!path.isInverseFillType() && path.isRect(&r)) { michael@0: this->onClipRect(r, op, edgeStyle); michael@0: } else { michael@0: this->onClipPath(path, op, edgeStyle); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { michael@0: #ifdef SK_ENABLE_CLIP_QUICKREJECT michael@0: if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) { michael@0: if (fMCRec->fRasterClip->isEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: if (this->quickReject(path.getBounds())) { michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: michael@0: fClipStack.clipEmpty(); michael@0: return fMCRec->fRasterClip->setEmpty(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: AutoValidateClip avc(this); michael@0: michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: if (!fAllowSoftClip) { michael@0: edgeStyle = kHard_ClipEdgeStyle; michael@0: } michael@0: michael@0: SkPath devPath; michael@0: path.transform(*fMCRec->fMatrix, &devPath); michael@0: michael@0: // Check if the transfomation, or the original path itself michael@0: // made us empty. Note this can also happen if we contained NaN michael@0: // values. computing the bounds detects this, and will set our michael@0: // bounds to empty if that is the case. (see SkRect::set(pts, count)) michael@0: if (devPath.getBounds().isEmpty()) { michael@0: // resetting the path will remove any NaN or other wanky values michael@0: // that might upset our scan converter. michael@0: devPath.reset(); michael@0: } michael@0: michael@0: // if we called path.swap() we could avoid a deep copy of this path michael@0: fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: michael@0: if (fAllowSimplifyClip) { michael@0: devPath.reset(); michael@0: devPath.setFillType(SkPath::kInverseEvenOdd_FillType); michael@0: const SkClipStack* clipStack = getClipStack(); michael@0: SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart); michael@0: const SkClipStack::Element* element; michael@0: while ((element = iter.next())) { michael@0: SkClipStack::Element::Type type = element->getType(); michael@0: if (type == SkClipStack::Element::kEmpty_Type) { michael@0: continue; michael@0: } michael@0: SkPath operand; michael@0: element->asPath(&operand); michael@0: SkRegion::Op elementOp = element->getOp(); michael@0: if (elementOp == SkRegion::kReplace_Op) { michael@0: devPath = operand; michael@0: } else { michael@0: Op(devPath, operand, (SkPathOp) elementOp, &devPath); michael@0: } michael@0: // if the prev and curr clips disagree about aa -vs- not, favor the aa request. michael@0: // perhaps we need an API change to avoid this sort of mixed-signals about michael@0: // clipping. michael@0: if (element->isAA()) { michael@0: edgeStyle = kSoft_ClipEdgeStyle; michael@0: } michael@0: } michael@0: op = SkRegion::kReplace_Op; michael@0: } michael@0: michael@0: clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle); michael@0: } michael@0: michael@0: void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op, michael@0: bool inverseFilled) { michael@0: // This is for updating the clip conservatively using only bounds michael@0: // information. michael@0: // Contract: michael@0: // The current clip must contain the true clip. The true michael@0: // clip is the clip that would have normally been computed michael@0: // by calls to clipPath and clipRRect michael@0: // Objective: michael@0: // Keep the current clip as small as possible without michael@0: // breaking the contract, using only clip bounding rectangles michael@0: // (for performance). michael@0: michael@0: // N.B.: This *never* calls back through a virtual on canvas, so subclasses michael@0: // don't have to worry about getting caught in a loop. Thus anywhere michael@0: // we call a virtual method, we explicitly prefix it with michael@0: // SkCanvas:: to be sure to call the base-class. michael@0: michael@0: if (inverseFilled) { michael@0: switch (op) { michael@0: case SkRegion::kIntersect_Op: michael@0: case SkRegion::kDifference_Op: michael@0: // These ops can only shrink the current clip. So leaving michael@0: // the clip unchanged conservatively respects the contract. michael@0: break; michael@0: case SkRegion::kUnion_Op: michael@0: case SkRegion::kReplace_Op: michael@0: case SkRegion::kReverseDifference_Op: michael@0: case SkRegion::kXOR_Op: { michael@0: // These ops can grow the current clip up to the extents of michael@0: // the input clip, which is inverse filled, so we just set michael@0: // the current clip to the device bounds. michael@0: SkRect deviceBounds; michael@0: SkIRect deviceIBounds; michael@0: this->getDevice()->getGlobalBounds(&deviceIBounds); michael@0: deviceBounds = SkRect::Make(deviceIBounds); michael@0: this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag); michael@0: // set the clip in device space michael@0: this->SkCanvas::setMatrix(SkMatrix::I()); michael@0: this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op, michael@0: kHard_ClipEdgeStyle); michael@0: this->SkCanvas::restore(); //pop the matrix, but keep the clip michael@0: break; michael@0: } michael@0: default: michael@0: SkASSERT(0); // unhandled op? michael@0: } michael@0: } else { michael@0: // Not inverse filled michael@0: switch (op) { michael@0: case SkRegion::kIntersect_Op: michael@0: case SkRegion::kUnion_Op: michael@0: case SkRegion::kReplace_Op: michael@0: this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle); michael@0: break; michael@0: case SkRegion::kDifference_Op: michael@0: // Difference can only shrink the current clip. michael@0: // Leaving clip unchanged conservatively fullfills the contract. michael@0: break; michael@0: case SkRegion::kReverseDifference_Op: michael@0: // To reverse, we swap in the bounds with a replace op. michael@0: // As with difference, leave it unchanged. michael@0: this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle); michael@0: break; michael@0: case SkRegion::kXOR_Op: michael@0: // Be conservative, based on (A XOR B) always included in (A union B), michael@0: // which is always included in (bounds(A) union bounds(B)) michael@0: this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle); michael@0: break; michael@0: default: michael@0: SkASSERT(0); // unhandled op? michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { michael@0: this->onClipRegion(rgn, op); michael@0: } michael@0: michael@0: void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) { michael@0: AutoValidateClip avc(this); michael@0: michael@0: fDeviceCMDirty = true; michael@0: fCachedLocalClipBoundsDirty = true; michael@0: michael@0: // todo: signal fClipStack that we have a region, and therefore (I guess) michael@0: // we have to ignore it, and use the region directly? michael@0: fClipStack.clipDevRect(rgn.getBounds(), op); michael@0: michael@0: fMCRec->fRasterClip->op(rgn, op); michael@0: } michael@0: michael@0: #ifdef SK_DEBUG michael@0: void SkCanvas::validateClip() const { michael@0: // construct clipRgn from the clipstack michael@0: const SkBaseDevice* device = this->getDevice(); michael@0: if (!device) { michael@0: SkASSERT(this->isClipEmpty()); michael@0: return; michael@0: } michael@0: michael@0: SkIRect ir; michael@0: ir.set(0, 0, device->width(), device->height()); michael@0: SkRasterClip tmpClip(ir); michael@0: michael@0: SkClipStack::B2TIter iter(fClipStack); michael@0: const SkClipStack::Element* element; michael@0: while ((element = iter.next()) != NULL) { michael@0: switch (element->getType()) { michael@0: case SkClipStack::Element::kRect_Type: michael@0: element->getRect().round(&ir); michael@0: tmpClip.op(ir, element->getOp()); michael@0: break; michael@0: case SkClipStack::Element::kEmpty_Type: michael@0: tmpClip.setEmpty(); michael@0: break; michael@0: default: { michael@0: SkPath path; michael@0: element->asPath(&path); michael@0: clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA()); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void SkCanvas::replayClips(ClipVisitor* visitor) const { michael@0: SkClipStack::B2TIter iter(fClipStack); michael@0: const SkClipStack::Element* element; michael@0: michael@0: static const SkRect kEmpty = { 0, 0, 0, 0 }; michael@0: while ((element = iter.next()) != NULL) { michael@0: switch (element->getType()) { michael@0: case SkClipStack::Element::kPath_Type: michael@0: visitor->clipPath(element->getPath(), element->getOp(), element->isAA()); michael@0: break; michael@0: case SkClipStack::Element::kRRect_Type: michael@0: visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA()); michael@0: break; michael@0: case SkClipStack::Element::kRect_Type: michael@0: visitor->clipRect(element->getRect(), element->getOp(), element->isAA()); michael@0: break; michael@0: case SkClipStack::Element::kEmpty_Type: michael@0: visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool SkCanvas::isClipEmpty() const { michael@0: return fMCRec->fRasterClip->isEmpty(); michael@0: } michael@0: michael@0: bool SkCanvas::isClipRect() const { michael@0: return fMCRec->fRasterClip->isRect(); michael@0: } michael@0: michael@0: bool SkCanvas::quickReject(const SkRect& rect) const { michael@0: michael@0: if (!rect.isFinite()) michael@0: return true; michael@0: michael@0: if (fMCRec->fRasterClip->isEmpty()) { michael@0: return true; michael@0: } michael@0: michael@0: if (fMCRec->fMatrix->hasPerspective()) { michael@0: SkRect dst; michael@0: fMCRec->fMatrix->mapRect(&dst, rect); michael@0: SkIRect idst; michael@0: dst.roundOut(&idst); michael@0: return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds()); michael@0: } else { michael@0: const SkRect& clipR = this->getLocalClipBounds(); michael@0: michael@0: // for speed, do the most likely reject compares first michael@0: // TODO: should we use | instead, or compare all 4 at once? michael@0: if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) { michael@0: return true; michael@0: } michael@0: if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool SkCanvas::quickReject(const SkPath& path) const { michael@0: return path.isEmpty() || this->quickReject(path.getBounds()); michael@0: } michael@0: michael@0: bool SkCanvas::getClipBounds(SkRect* bounds) const { michael@0: SkIRect ibounds; michael@0: if (!this->getClipDeviceBounds(&ibounds)) { michael@0: return false; michael@0: } michael@0: michael@0: SkMatrix inverse; michael@0: // if we can't invert the CTM, we can't return local clip bounds michael@0: if (!fMCRec->fMatrix->invert(&inverse)) { michael@0: if (bounds) { michael@0: bounds->setEmpty(); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: if (NULL != bounds) { michael@0: SkRect r; michael@0: // adjust it outwards in case we are antialiasing michael@0: const int inset = 1; michael@0: michael@0: r.iset(ibounds.fLeft - inset, ibounds.fTop - inset, michael@0: ibounds.fRight + inset, ibounds.fBottom + inset); michael@0: inverse.mapRect(bounds, r); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const { michael@0: const SkRasterClip& clip = *fMCRec->fRasterClip; michael@0: if (clip.isEmpty()) { michael@0: if (bounds) { michael@0: bounds->setEmpty(); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: if (NULL != bounds) { michael@0: *bounds = clip.getBounds(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: const SkMatrix& SkCanvas::getTotalMatrix() const { michael@0: return *fMCRec->fMatrix; michael@0: } michael@0: michael@0: #ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE michael@0: SkCanvas::ClipType SkCanvas::getClipType() const { michael@0: if (fMCRec->fRasterClip->isEmpty()) { michael@0: return kEmpty_ClipType; michael@0: } michael@0: if (fMCRec->fRasterClip->isRect()) { michael@0: return kRect_ClipType; michael@0: } michael@0: return kComplex_ClipType; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP michael@0: const SkRegion& SkCanvas::getTotalClip() const { michael@0: return fMCRec->fRasterClip->forceGetBW(); michael@0: } michael@0: #endif michael@0: michael@0: const SkRegion& SkCanvas::internal_private_getTotalClip() const { michael@0: return fMCRec->fRasterClip->forceGetBW(); michael@0: } michael@0: michael@0: void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const { michael@0: path->reset(); michael@0: michael@0: const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW(); michael@0: if (rgn.isEmpty()) { michael@0: return; michael@0: } michael@0: (void)rgn.getBoundaryPath(path); michael@0: } michael@0: michael@0: GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() { michael@0: SkBaseDevice* dev = this->getTopDevice(); michael@0: return dev ? dev->accessRenderTarget() : NULL; michael@0: } michael@0: michael@0: SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) { michael@0: SkBaseDevice* device = this->getTopDevice(); michael@0: return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL; michael@0: } michael@0: michael@0: GrContext* SkCanvas::getGrContext() { michael@0: #if SK_SUPPORT_GPU michael@0: SkBaseDevice* device = this->getTopDevice(); michael@0: if (NULL != device) { michael@0: GrRenderTarget* renderTarget = device->accessRenderTarget(); michael@0: if (NULL != renderTarget) { michael@0: return renderTarget->getContext(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return NULL; michael@0: michael@0: } michael@0: michael@0: void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner, michael@0: const SkPaint& paint) { michael@0: if (outer.isEmpty()) { michael@0: return; michael@0: } michael@0: if (inner.isEmpty()) { michael@0: this->drawRRect(outer, paint); michael@0: return; michael@0: } michael@0: michael@0: // We don't have this method (yet), but technically this is what we should michael@0: // be able to assert... michael@0: // SkASSERT(outer.contains(inner)); michael@0: // michael@0: // For now at least check for containment of bounds michael@0: SkASSERT(outer.getBounds().contains(inner.getBounds())); michael@0: michael@0: this->onDrawDRRect(outer, inner, paint); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // These are the virtual drawing methods michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkCanvas::clear(SkColor color) { michael@0: SkDrawIter iter(this); michael@0: this->predrawNotify(); michael@0: while (iter.next()) { michael@0: iter.fDevice->clear(color); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::drawPaint(const SkPaint& paint) { michael@0: this->internalDrawPaint(paint); michael@0: } michael@0: michael@0: void SkCanvas::internalDrawPaint(const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawPaint(iter, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], michael@0: const SkPaint& paint) { michael@0: if ((long)count <= 0) { michael@0: return; michael@0: } michael@0: michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: SkRect r, storage; michael@0: const SkRect* bounds = NULL; michael@0: if (paint.canComputeFastBounds()) { michael@0: // special-case 2 points (common for drawing a single line) michael@0: if (2 == count) { michael@0: r.set(pts[0], pts[1]); michael@0: } else { michael@0: r.set(pts, SkToInt(count)); michael@0: } michael@0: bounds = &paint.computeFastStrokeBounds(r, &storage); michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: SkASSERT(pts != NULL); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = NULL; michael@0: if (paint.canComputeFastBounds()) { michael@0: bounds = &paint.computeFastBounds(r, &storage); michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawRect(iter, r, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = NULL; michael@0: if (paint.canComputeFastBounds()) { michael@0: bounds = &paint.computeFastBounds(oval, &storage); michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawOval(iter, oval, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = NULL; michael@0: if (paint.canComputeFastBounds()) { michael@0: bounds = &paint.computeFastBounds(rrect.getBounds(), &storage); michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (rrect.isRect()) { michael@0: // call the non-virtual version michael@0: this->SkCanvas::drawRect(rrect.getBounds(), paint); michael@0: return; michael@0: } else if (rrect.isOval()) { michael@0: // call the non-virtual version michael@0: this->SkCanvas::drawOval(rrect.getBounds(), paint); michael@0: return; michael@0: } michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawRRect(iter, rrect, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, michael@0: const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = NULL; michael@0: if (paint.canComputeFastBounds()) { michael@0: bounds = &paint.computeFastBounds(outer.getBounds(), &storage); michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawDRRect(iter, outer, inner, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: if (!path.isFinite()) { michael@0: return; michael@0: } michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = NULL; michael@0: if (!path.isInverseFillType() && paint.canComputeFastBounds()) { michael@0: const SkRect& pathBounds = path.getBounds(); michael@0: bounds = &paint.computeFastBounds(pathBounds, &storage); michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: const SkRect& r = path.getBounds(); michael@0: if (r.width() <= 0 && r.height() <= 0) { michael@0: if (path.isInverseFillType()) { michael@0: this->internalDrawPaint(paint); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawPath(iter, path, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, michael@0: const SkPaint* paint) { michael@0: SkDEBUGCODE(bitmap.validate();) michael@0: michael@0: if (NULL == paint || paint->canComputeFastBounds()) { michael@0: SkRect bounds = { michael@0: x, y, michael@0: x + SkIntToScalar(bitmap.width()), michael@0: y + SkIntToScalar(bitmap.height()) michael@0: }; michael@0: if (paint) { michael@0: (void)paint->computeFastBounds(bounds, &bounds); michael@0: } michael@0: if (this->quickReject(bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: SkMatrix matrix; michael@0: matrix.setTranslate(x, y); michael@0: this->internalDrawBitmap(bitmap, matrix, paint); michael@0: } michael@0: michael@0: // this one is non-virtual, so it can be called safely by other canvas apis michael@0: void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, michael@0: const SkRect& dst, const SkPaint* paint, michael@0: DrawBitmapRectFlags flags) { michael@0: if (bitmap.drawsNothing() || dst.isEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: CHECK_LOCKCOUNT_BALANCE(bitmap); michael@0: michael@0: SkRect storage; michael@0: const SkRect* bounds = &dst; michael@0: if (NULL == paint || paint->canComputeFastBounds()) { michael@0: if (paint) { michael@0: bounds = &paint->computeFastBounds(dst, &storage); michael@0: } michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: SkLazyPaint lazy; michael@0: if (NULL == paint) { michael@0: paint = lazy.init(); michael@0: } michael@0: michael@0: LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, michael@0: const SkRect& dst, const SkPaint* paint, michael@0: DrawBitmapRectFlags flags) { michael@0: SkDEBUGCODE(bitmap.validate();) michael@0: this->internalDrawBitmapRect(bitmap, src, dst, paint, flags); michael@0: } michael@0: michael@0: void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, michael@0: const SkPaint* paint) { michael@0: SkDEBUGCODE(bitmap.validate();) michael@0: this->internalDrawBitmap(bitmap, matrix, paint); michael@0: } michael@0: michael@0: void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap, michael@0: const SkIRect& center, const SkRect& dst, michael@0: const SkPaint* paint) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: if (NULL == paint || paint->canComputeFastBounds()) { michael@0: SkRect storage; michael@0: const SkRect* bounds = &dst; michael@0: if (paint) { michael@0: bounds = &paint->computeFastBounds(dst, &storage); michael@0: } michael@0: if (this->quickReject(*bounds)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: const int32_t w = bitmap.width(); michael@0: const int32_t h = bitmap.height(); michael@0: michael@0: SkIRect c = center; michael@0: // pin center to the bounds of the bitmap michael@0: c.fLeft = SkMax32(0, center.fLeft); michael@0: c.fTop = SkMax32(0, center.fTop); michael@0: c.fRight = SkPin32(center.fRight, c.fLeft, w); michael@0: c.fBottom = SkPin32(center.fBottom, c.fTop, h); michael@0: michael@0: const SkScalar srcX[4] = { michael@0: 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w) michael@0: }; michael@0: const SkScalar srcY[4] = { michael@0: 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h) michael@0: }; michael@0: SkScalar dstX[4] = { michael@0: dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft), michael@0: dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight michael@0: }; michael@0: SkScalar dstY[4] = { michael@0: dst.fTop, dst.fTop + SkIntToScalar(c.fTop), michael@0: dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom michael@0: }; michael@0: michael@0: if (dstX[1] > dstX[2]) { michael@0: dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width()); michael@0: dstX[2] = dstX[1]; michael@0: } michael@0: michael@0: if (dstY[1] > dstY[2]) { michael@0: dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height()); michael@0: dstY[2] = dstY[1]; michael@0: } michael@0: michael@0: for (int y = 0; y < 3; y++) { michael@0: SkRect s, d; michael@0: michael@0: s.fTop = srcY[y]; michael@0: s.fBottom = srcY[y+1]; michael@0: d.fTop = dstY[y]; michael@0: d.fBottom = dstY[y+1]; michael@0: for (int x = 0; x < 3; x++) { michael@0: s.fLeft = srcX[x]; michael@0: s.fRight = srcX[x+1]; michael@0: d.fLeft = dstX[x]; michael@0: d.fRight = dstX[x+1]; michael@0: this->internalDrawBitmapRect(bitmap, &s, d, paint, michael@0: kNone_DrawBitmapRectFlag); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, michael@0: const SkRect& dst, const SkPaint* paint) { michael@0: SkDEBUGCODE(bitmap.validate();) michael@0: michael@0: // Need a device entry-point, so gpu can use a mesh michael@0: this->internalDrawBitmapNine(bitmap, center, dst, paint); michael@0: } michael@0: michael@0: class SkDeviceFilteredPaint { michael@0: public: michael@0: SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) { michael@0: SkBaseDevice::TextFlags flags; michael@0: if (device->filterTextFlags(paint, &flags)) { michael@0: SkPaint* newPaint = fLazy.set(paint); michael@0: newPaint->setFlags(flags.fFlags); michael@0: newPaint->setHinting(flags.fHinting); michael@0: fPaint = newPaint; michael@0: } else { michael@0: fPaint = &paint; michael@0: } michael@0: } michael@0: michael@0: const SkPaint& paint() const { return *fPaint; } michael@0: michael@0: private: michael@0: const SkPaint* fPaint; michael@0: SkLazyPaint fLazy; michael@0: }; michael@0: michael@0: void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint, michael@0: const SkRect& r, SkScalar textSize) { michael@0: if (paint.getStyle() == SkPaint::kFill_Style) { michael@0: draw.fDevice->drawRect(draw, r, paint); michael@0: } else { michael@0: SkPaint p(paint); michael@0: p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth())); michael@0: draw.fDevice->drawRect(draw, r, p); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint, michael@0: const char text[], size_t byteLength, michael@0: SkScalar x, SkScalar y) { michael@0: SkASSERT(byteLength == 0 || text != NULL); michael@0: michael@0: // nothing to draw michael@0: if (text == NULL || byteLength == 0 || michael@0: draw.fClip->isEmpty() || michael@0: (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { michael@0: return; michael@0: } michael@0: michael@0: SkScalar width = 0; michael@0: SkPoint start; michael@0: michael@0: start.set(0, 0); // to avoid warning michael@0: if (paint.getFlags() & (SkPaint::kUnderlineText_Flag | michael@0: SkPaint::kStrikeThruText_Flag)) { michael@0: width = paint.measureText(text, byteLength); michael@0: michael@0: SkScalar offsetX = 0; michael@0: if (paint.getTextAlign() == SkPaint::kCenter_Align) { michael@0: offsetX = SkScalarHalf(width); michael@0: } else if (paint.getTextAlign() == SkPaint::kRight_Align) { michael@0: offsetX = width; michael@0: } michael@0: start.set(x - offsetX, y); michael@0: } michael@0: michael@0: if (0 == width) { michael@0: return; michael@0: } michael@0: michael@0: uint32_t flags = paint.getFlags(); michael@0: michael@0: if (flags & (SkPaint::kUnderlineText_Flag | michael@0: SkPaint::kStrikeThruText_Flag)) { michael@0: SkScalar textSize = paint.getTextSize(); michael@0: SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); michael@0: SkRect r; michael@0: michael@0: r.fLeft = start.fX; michael@0: r.fRight = start.fX + width; michael@0: michael@0: if (flags & SkPaint::kUnderlineText_Flag) { michael@0: SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset, michael@0: start.fY); michael@0: r.fTop = offset; michael@0: r.fBottom = offset + height; michael@0: DrawRect(draw, paint, r, textSize); michael@0: } michael@0: if (flags & SkPaint::kStrikeThruText_Flag) { michael@0: SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, michael@0: start.fY); michael@0: r.fTop = offset; michael@0: r.fBottom = offset + height; michael@0: DrawRect(draw, paint, r, textSize); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::drawText(const void* text, size_t byteLength, michael@0: SkScalar x, SkScalar y, const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) michael@0: michael@0: while (iter.next()) { michael@0: SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint()); michael@0: iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint()); michael@0: DrawTextDecorations(iter, dfp.paint(), michael@0: static_cast(text), byteLength, x, y); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawPosText(const void* text, size_t byteLength, michael@0: const SkPoint pos[], const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) michael@0: michael@0: while (iter.next()) { michael@0: SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint()); michael@0: iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2, michael@0: dfp.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawPosTextH(const void* text, size_t byteLength, michael@0: const SkScalar xpos[], SkScalar constY, michael@0: const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) michael@0: michael@0: while (iter.next()) { michael@0: SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint()); michael@0: iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1, michael@0: dfp.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, michael@0: const SkPath& path, const SkMatrix* matrix, michael@0: const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawTextOnPath(iter, text, byteLength, path, michael@0: matrix, looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, michael@0: const SkPoint verts[], const SkPoint texs[], michael@0: const SkColor colors[], SkXfermode* xmode, michael@0: const uint16_t indices[], int indexCount, michael@0: const SkPaint& paint) { michael@0: CHECK_SHADER_NOSETCONTEXT(paint); michael@0: michael@0: LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL) michael@0: michael@0: while (iter.next()) { michael@0: iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs, michael@0: colors, xmode, indices, indexCount, michael@0: looper.paint()); michael@0: } michael@0: michael@0: LOOPER_END michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // These methods are NOT virtual, and therefore must call back into virtual michael@0: // methods, rather than actually drawing themselves. michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, michael@0: SkXfermode::Mode mode) { michael@0: SkPaint paint; michael@0: michael@0: paint.setARGB(a, r, g, b); michael@0: if (SkXfermode::kSrcOver_Mode != mode) { michael@0: paint.setXfermodeMode(mode); michael@0: } michael@0: this->drawPaint(paint); michael@0: } michael@0: michael@0: void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) { michael@0: SkPaint paint; michael@0: michael@0: paint.setColor(c); michael@0: if (SkXfermode::kSrcOver_Mode != mode) { michael@0: paint.setXfermodeMode(mode); michael@0: } michael@0: this->drawPaint(paint); michael@0: } michael@0: michael@0: void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { michael@0: SkPoint pt; michael@0: michael@0: pt.set(x, y); michael@0: this->drawPoints(kPoints_PointMode, 1, &pt, paint); michael@0: } michael@0: michael@0: void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) { michael@0: SkPoint pt; michael@0: SkPaint paint; michael@0: michael@0: pt.set(x, y); michael@0: paint.setColor(color); michael@0: this->drawPoints(kPoints_PointMode, 1, &pt, paint); michael@0: } michael@0: michael@0: void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, michael@0: const SkPaint& paint) { michael@0: SkPoint pts[2]; michael@0: michael@0: pts[0].set(x0, y0); michael@0: pts[1].set(x1, y1); michael@0: this->drawPoints(kLines_PointMode, 2, pts, paint); michael@0: } michael@0: michael@0: void SkCanvas::drawRectCoords(SkScalar left, SkScalar top, michael@0: SkScalar right, SkScalar bottom, michael@0: const SkPaint& paint) { michael@0: SkRect r; michael@0: michael@0: r.set(left, top, right, bottom); michael@0: this->drawRect(r, paint); michael@0: } michael@0: michael@0: void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, michael@0: const SkPaint& paint) { michael@0: if (radius < 0) { michael@0: radius = 0; michael@0: } michael@0: michael@0: SkRect r; michael@0: r.set(cx - radius, cy - radius, cx + radius, cy + radius); michael@0: this->drawOval(r, paint); michael@0: } michael@0: michael@0: void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry, michael@0: const SkPaint& paint) { michael@0: if (rx > 0 && ry > 0) { michael@0: if (paint.canComputeFastBounds()) { michael@0: SkRect storage; michael@0: if (this->quickReject(paint.computeFastBounds(r, &storage))) { michael@0: return; michael@0: } michael@0: } michael@0: SkRRect rrect; michael@0: rrect.setRectXY(r, rx, ry); michael@0: this->drawRRect(rrect, paint); michael@0: } else { michael@0: this->drawRect(r, paint); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle, michael@0: SkScalar sweepAngle, bool useCenter, michael@0: const SkPaint& paint) { michael@0: if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) { michael@0: this->drawOval(oval, paint); michael@0: } else { michael@0: SkPath path; michael@0: if (useCenter) { michael@0: path.moveTo(oval.centerX(), oval.centerY()); michael@0: } michael@0: path.arcTo(oval, startAngle, sweepAngle, !useCenter); michael@0: if (useCenter) { michael@0: path.close(); michael@0: } michael@0: this->drawPath(path, paint); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength, michael@0: const SkPath& path, SkScalar hOffset, michael@0: SkScalar vOffset, const SkPaint& paint) { michael@0: SkMatrix matrix; michael@0: michael@0: matrix.setTranslate(hOffset, vOffset); michael@0: this->drawTextOnPath(text, byteLength, path, &matrix, paint); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) { michael@0: SkBaseDevice* device = this->getDevice(); michael@0: if (NULL != device) { michael@0: device->EXPERIMENTAL_optimize(picture); michael@0: } michael@0: } michael@0: michael@0: void SkCanvas::drawPicture(SkPicture& picture) { michael@0: SkBaseDevice* device = this->getTopDevice(); michael@0: if (NULL != device) { michael@0: // Canvas has to first give the device the opportunity to render michael@0: // the picture itself. michael@0: if (device->EXPERIMENTAL_drawPicture(picture)) { michael@0: return; // the device has rendered the entire picture michael@0: } michael@0: } michael@0: michael@0: picture.draw(this); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) { michael@0: SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small); michael@0: michael@0: SkASSERT(canvas); michael@0: michael@0: fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips); michael@0: fDone = !fImpl->next(); michael@0: } michael@0: michael@0: SkCanvas::LayerIter::~LayerIter() { michael@0: fImpl->~SkDrawIter(); michael@0: } michael@0: michael@0: void SkCanvas::LayerIter::next() { michael@0: fDone = !fImpl->next(); michael@0: } michael@0: michael@0: SkBaseDevice* SkCanvas::LayerIter::device() const { michael@0: return fImpl->getDevice(); michael@0: } michael@0: michael@0: const SkMatrix& SkCanvas::LayerIter::matrix() const { michael@0: return fImpl->getMatrix(); michael@0: } michael@0: michael@0: const SkPaint& SkCanvas::LayerIter::paint() const { michael@0: const SkPaint* paint = fImpl->getPaint(); michael@0: if (NULL == paint) { michael@0: paint = &fDefaultPaint; michael@0: } michael@0: return *paint; michael@0: } michael@0: michael@0: const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); } michael@0: int SkCanvas::LayerIter::x() const { return fImpl->getX(); } michael@0: int SkCanvas::LayerIter::y() const { return fImpl->getY(); } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkCanvas::ClipVisitor::~ClipVisitor() { } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static bool supported_for_raster_canvas(const SkImageInfo& info) { michael@0: switch (info.alphaType()) { michael@0: case kPremul_SkAlphaType: michael@0: case kOpaque_SkAlphaType: michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: switch (info.colorType()) { michael@0: case kAlpha_8_SkColorType: michael@0: case kRGB_565_SkColorType: michael@0: case kPMColor_SkColorType: michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) { michael@0: if (!supported_for_raster_canvas(info)) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkBitmap bitmap; michael@0: if (!bitmap.allocPixels(info)) { michael@0: return NULL; michael@0: } michael@0: michael@0: // should this functionality be moved into allocPixels()? michael@0: if (!bitmap.info().isOpaque()) { michael@0: bitmap.eraseColor(0); michael@0: } michael@0: return SkNEW_ARGS(SkCanvas, (bitmap)); michael@0: } michael@0: michael@0: SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) { michael@0: if (!supported_for_raster_canvas(info)) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkBitmap bitmap; michael@0: if (!bitmap.installPixels(info, pixels, rowBytes)) { michael@0: return NULL; michael@0: } michael@0: michael@0: // should this functionality be moved into allocPixels()? michael@0: if (!bitmap.info().isOpaque()) { michael@0: bitmap.eraseColor(0); michael@0: } michael@0: return SkNEW_ARGS(SkCanvas, (bitmap)); michael@0: }