grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
new file mode 100644
index 0000000..4088416
--- /dev/null
+++ b/src/core/SkCanvas.cpp
@@ -0,0 +1,1420 @@
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkCanvas.h"
+#include "SkBounder.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkPicture.h"
+#include "SkScalarCompare.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include <new>
+
+//#define SK_TRACE_SAVERESTORE
+
+#ifdef SK_TRACE_SAVERESTORE
+    static int gLayerCounter;
+    static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
+    static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
+
+    static int gRecCounter;
+    static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
+    static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
+
+    static int gCanvasCounter;
+    static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
+    static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
+#else
+    #define inc_layer()
+    #define dec_layer()
+    #define inc_rec()
+    #define dec_rec()
+    #define inc_canvas()
+    #define dec_canvas()
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for computing fast bounds for quickReject tests
+
+static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
+    return paint != NULL && paint->isAntiAlias() ?
+            SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  This is the record we keep for each SkDevice that the user installs.
+    The clip/matrix/proc are fields that reflect the top of the save/restore
+    stack. Whenever the canvas changes, it marks a dirty flag, and then before
+    these are used (assuming we're not on a layer) we rebuild these cache
+    values: they reflect the top of the save stack, but translated and clipped
+    by the device's XY offset and bitmap-bounds.
+*/
+struct DeviceCM {
+    DeviceCM*           fNext;
+    SkDevice*           fDevice;
+    SkRegion            fClip;
+    const SkMatrix*     fMatrix;
+	SkPaint*			fPaint;	// may be null (in the future)
+    int16_t             fX, fY; // relative to base matrix/clip
+
+	DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
+            : fNext(NULL) {
+        if (NULL != device) {
+            device->ref();
+            device->lockPixels();
+        }
+        fDevice = device;        
+        fX = SkToS16(x);
+        fY = SkToS16(y);
+        fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
+	}
+
+	~DeviceCM() {
+        if (NULL != fDevice) {
+            fDevice->unlockPixels();
+            fDevice->unref();
+        }
+		SkDELETE(fPaint);
+	}
+    
+    void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
+                  SkRegion* updateClip) {
+        int x = fX;
+        int y = fY;
+        int width = fDevice->width();
+        int height = fDevice->height();
+    
+        if ((x | y) == 0) {
+            fMatrix = &totalMatrix;
+            fClip = totalClip;
+        } else {
+            fMatrixStorage = totalMatrix;
+            fMatrixStorage.postTranslate(SkIntToScalar(-x),
+                                         SkIntToScalar(-y));
+            fMatrix = &fMatrixStorage;
+            
+            totalClip.translate(-x, -y, &fClip);
+        }
+
+        fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
+
+        // intersect clip, but don't translate it (yet)
+        
+        if (updateClip) {
+            updateClip->op(x, y, x + width, y + height,
+                           SkRegion::kDifference_Op);
+        }
+        
+        fDevice->setMatrixClip(*fMatrix, fClip);
+
+#ifdef SK_DEBUG
+        if (!fClip.isEmpty()) {
+            SkIRect deviceR;
+            deviceR.set(0, 0, width, height);
+            SkASSERT(deviceR.contains(fClip.getBounds()));
+        }
+#endif
+    }
+    
+    void translateClip() {
+        if (fX | fY) {
+            fClip.translate(fX, fY);
+        }
+    }
+
+private:
+    SkMatrix    fMatrixStorage;
+};
+
+/*  This is the record we keep for each save/restore level in the stack.
+    Since a level optionally copies the matrix and/or stack, we have pointers
+    for these fields. If the value is copied for this level, the copy is
+    stored in the ...Storage field, and the pointer points to that. If the
+    value is not copied for this level, we ignore ...Storage, and just point
+    at the corresponding value in the previous level in the stack.
+*/
+class SkCanvas::MCRec {
+public:
+    MCRec*          fNext;
+    SkMatrix*       fMatrix;    // points to either fMatrixStorage or prev MCRec
+    SkRegion*       fRegion;    // points to either fRegionStorage or prev MCRec
+    SkDrawFilter*   fFilter;    // the current filter (or null)
+    
+    DeviceCM*   fLayer;
+    /*  If there are any layers in the stack, this points to the top-most
+        one that is at or below this level in the stack (so we know what
+        bitmap/device to draw into from this level. This value is NOT
+        reference counted, since the real owner is either our fLayer field,
+        or a previous one in a lower level.)
+    */
+    DeviceCM*	fTopLayer;
+
+    MCRec(const MCRec* prev, int flags) {
+        if (NULL != prev) {
+            if (flags & SkCanvas::kMatrix_SaveFlag) {
+                fMatrixStorage = *prev->fMatrix;
+                fMatrix = &fMatrixStorage;
+            } else {
+                fMatrix = prev->fMatrix;
+            }
+            
+            if (flags & SkCanvas::kClip_SaveFlag) {
+                fRegionStorage = *prev->fRegion;
+                fRegion = &fRegionStorage;
+            } else {
+                fRegion = prev->fRegion;
+            }
+
+            fFilter = prev->fFilter;
+            fFilter->safeRef();
+
+            fTopLayer = prev->fTopLayer;
+        } else {   // no prev
+            fMatrixStorage.reset();
+            
+            fMatrix     = &fMatrixStorage;
+            fRegion     = &fRegionStorage;
+            fFilter     = NULL;
+            fTopLayer   = NULL;
+        }
+        fLayer = NULL;
+
+        // don't bother initializing fNext
+        inc_rec();
+    }
+    ~MCRec() {
+        fFilter->safeUnref();
+        SkDELETE(fLayer);
+        dec_rec();
+    }
+	
+private:
+    SkMatrix    fMatrixStorage;
+    SkRegion    fRegionStorage;
+};
+
+class SkDrawIter : public SkDraw {
+public:
+    SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
+        fCanvas = canvas;
+        canvas->updateDeviceCMCache();
+
+        fBounder = canvas->getBounder();
+        fCurrLayer = canvas->fMCRec->fTopLayer;
+        fSkipEmptyClips = skipEmptyClips;
+    }
+    
+    bool next() {
+        // skip over recs with empty clips
+        if (fSkipEmptyClips) {
+            while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
+                fCurrLayer = fCurrLayer->fNext;
+            }
+        }
+
+        if (NULL != fCurrLayer) {
+            const DeviceCM* rec = fCurrLayer;
+
+            fMatrix = rec->fMatrix;
+            fClip   = &rec->fClip;
+            fDevice = rec->fDevice;
+            fBitmap = &fDevice->accessBitmap(true);
+            fLayerX = rec->fX;
+            fLayerY = rec->fY;
+            fPaint  = rec->fPaint;
+            SkDEBUGCODE(this->validate();)
+
+            fCurrLayer = rec->fNext;
+            if (fBounder) {
+                fBounder->setClip(fClip);
+            }
+
+            // fCurrLayer may be NULL now
+            
+            fCanvas->prepareForDeviceDraw(fDevice);
+            return true;
+        }
+        return false;
+    }
+    
+    int getX() const { return fLayerX; }
+    int getY() const { return fLayerY; }
+    SkDevice* getDevice() const { return fDevice; }
+    const SkMatrix& getMatrix() const { return *fMatrix; }
+    const SkRegion& getClip() const { return *fClip; }
+    const SkPaint* getPaint() const { return fPaint; }
+private:
+    SkCanvas*       fCanvas;
+    const DeviceCM* fCurrLayer;
+    const SkPaint*  fPaint;     // May be null.
+    int             fLayerX;
+    int             fLayerY;
+    SkBool8         fSkipEmptyClips;
+
+    typedef SkDraw INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class AutoDrawLooper {
+public:
+    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
+            : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
+        if ((fLooper = paint.getLooper()) != NULL) {
+            fLooper->init(canvas, (SkPaint*)&paint);
+        } else {
+            fOnce = true;
+        }
+        fFilter = canvas->getDrawFilter();
+        fNeedFilterRestore = false;
+    }
+
+    ~AutoDrawLooper() {
+        if (fNeedFilterRestore) {
+            SkASSERT(fFilter);
+            fFilter->restore(fCanvas, fPaint, fType);
+        }
+        if (NULL != fLooper) {
+            fLooper->restore();
+        }
+    }
+    
+    bool next() {
+        SkDrawFilter* filter = fFilter;
+
+        // if we drew earlier with a filter, then we need to restore first
+        if (fNeedFilterRestore) {
+            SkASSERT(filter);
+            filter->restore(fCanvas, fPaint, fType);
+            fNeedFilterRestore = false;
+        }
+            
+        bool result;
+        
+        if (NULL != fLooper) {
+            result = fLooper->next();
+        } else {
+            result = fOnce;
+            fOnce = false;
+        }
+
+        // if we're gonna draw, give the filter a chance to do its work
+        if (result && NULL != filter) {
+            fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
+                                                         fType);
+        }
+        return result;
+    }
+    
+private:
+    SkDrawLooper*   fLooper;
+    SkDrawFilter*   fFilter;
+    SkCanvas*       fCanvas;
+    SkPaint*        fPaint;
+    SkDrawFilter::Type  fType;
+    bool            fOnce;
+    bool            fNeedFilterRestore;
+    
+};
+
+/*  Stack helper for managing a SkBounder. In the destructor, if we were
+    given a bounder, we call its commit() method, signifying that we are
+    done accumulating bounds for that draw.
+*/
+class SkAutoBounderCommit {
+public:
+    SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
+    ~SkAutoBounderCommit() {
+        if (NULL != fBounder) {
+            fBounder->commit();
+        }
+    }
+private:
+    SkBounder*  fBounder;
+};
+
+#include "SkColorPriv.h"
+
+class AutoValidator {
+public:
+    AutoValidator(SkDevice* device) : fDevice(device) {}
+    ~AutoValidator() {
+#ifdef SK_DEBUG
+        const SkBitmap& bm = fDevice->accessBitmap(false);
+        if (bm.config() == SkBitmap::kARGB_4444_Config) {
+            for (int y = 0; y < bm.height(); y++) {
+                const SkPMColor16* p = bm.getAddr16(0, y);
+                for (int x = 0; x < bm.width(); x++) {
+                    SkPMColor16 c = p[x];
+                    SkPMColor16Assert(c);
+                }
+            }
+        }
+#endif
+    }
+private:
+    SkDevice* fDevice;
+};
+
+////////// macros to place around the internal draw calls //////////////////
+
+#define ITER_BEGIN(paint, type)                                     \
+/*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
+    AutoDrawLooper  looper(this, paint, type);                      \
+    while (looper.next()) {                                         \
+        SkAutoBounderCommit ac(fBounder);                           \
+        SkDrawIter          iter(this);
+    
+#define ITER_END    }
+
+////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::init(SkDevice* device) {
+    fBounder = NULL;
+    fLocalBoundsCompareTypeDirty = true;
+
+    fMCRec = (MCRec*)fMCStack.push_back();
+    new (fMCRec) MCRec(NULL, 0);
+
+    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
+    fMCRec->fTopLayer = fMCRec->fLayer;
+    fMCRec->fNext = NULL;
+
+    return this->setDevice(device);
+}
+
+SkCanvas::SkCanvas(SkDevice* device)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(device);
+}
+
+SkCanvas::SkCanvas(const SkBitmap& bitmap)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+}
+
+SkCanvas::~SkCanvas() {
+    // free up the contents of our deque
+    this->restoreToCount(1);    // restore everything but the last
+    this->internalRestore();    // restore the last, since we're going away
+
+    fBounder->safeUnref();
+    
+    dec_canvas();
+}
+
+SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
+    SkRefCnt_SafeAssign(fBounder, bounder);
+    return bounder;
+}
+
+SkDrawFilter* SkCanvas::getDrawFilter() const {
+    return fMCRec->fFilter;
+}
+
+SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
+    SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
+    return filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::getDevice() const {
+    // return root device
+    SkDeque::Iter   iter(fMCStack);
+    MCRec*          rec = (MCRec*)iter.next();    
+    SkASSERT(rec && rec->fLayer);
+    return rec->fLayer->fDevice;
+}
+
+SkDevice* SkCanvas::setDevice(SkDevice* device) {
+    // return root device
+    SkDeque::Iter   iter(fMCStack);
+    MCRec*          rec = (MCRec*)iter.next();    
+    SkASSERT(rec && rec->fLayer);
+    SkDevice*       rootDevice = rec->fLayer->fDevice;
+
+    if (rootDevice == device) {
+        return device;
+    }
+    
+    /* Notify the devices that they are going in/out of scope, so they can do
+       things like lock/unlock their pixels, etc.
+    */
+    if (device) {
+        device->lockPixels();
+    }
+    if (rootDevice) {
+        rootDevice->unlockPixels();
+    }
+
+    SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
+    rootDevice = device;
+
+    fDeviceCMDirty = true;
+    
+    /*  Now we update our initial region to have the bounds of the new device,
+        and then intersect all of the clips in our stack with these bounds,
+        to ensure that we can't draw outside of the device's bounds (and trash
+                                                                     memory).
+        
+    NOTE: this is only a partial-fix, since if the new device is larger than
+        the previous one, we don't know how to "enlarge" the clips in our stack,
+        so drawing may be artificially restricted. Without keeping a history of 
+        all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
+        reconstruct the correct clips, so this approximation will have to do.
+        The caller really needs to restore() back to the base if they want to
+        accurately take advantage of the new device bounds.
+    */
+
+    if (NULL == device) {
+        rec->fRegion->setEmpty();
+        while ((rec = (MCRec*)iter.next()) != NULL) {
+            (void)rec->fRegion->setEmpty();
+        }
+    } else {
+        // compute our total bounds for all devices
+        SkIRect bounds;
+        
+        bounds.set(0, 0, device->width(), device->height());
+
+        // now jam our 1st clip to be bounds, and intersect the rest with that
+        rec->fRegion->setRect(bounds);
+        while ((rec = (MCRec*)iter.next()) != NULL) {
+            (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
+        }
+    }
+    return device;
+}
+
+SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+    SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
+    device->unref();
+    return device;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::getViewport(SkIPoint* size) const {
+    return false;
+}
+
+bool SkCanvas::setViewport(int width, int height) {
+    return false;
+}
+
+void SkCanvas::updateDeviceCMCache() {
+    if (fDeviceCMDirty) {
+        const SkMatrix& totalMatrix = this->getTotalMatrix();
+        const SkRegion& totalClip = this->getTotalClip();
+        DeviceCM*       layer = fMCRec->fTopLayer;
+        
+        if (NULL == layer->fNext) {   // only one layer
+            layer->updateMC(totalMatrix, totalClip, NULL);
+        } else {
+            SkRegion clip;
+            clip = totalClip;  // make a copy
+            do {
+                layer->updateMC(totalMatrix, clip, &clip);
+            } while ((layer = layer->fNext) != NULL);
+        }
+        fDeviceCMDirty = false;
+    }
+}
+
+void SkCanvas::prepareForDeviceDraw(SkDevice* device) {
+    SkASSERT(device);
+    device->gainFocus(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkCanvas::internalSave(SaveFlags flags) {
+    int saveCount = this->getSaveCount(); // record this before the actual save
+    
+    MCRec* newTop = (MCRec*)fMCStack.push_back();
+    new (newTop) MCRec(fMCRec, flags);    // balanced in restore()
+    
+    newTop->fNext = fMCRec;
+    fMCRec = newTop;
+    
+    return saveCount;
+}
+
+int SkCanvas::save(SaveFlags flags) {
+    // call shared impl
+    return this->internalSave(flags);
+}
+
+#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
+#define C16MASK (1 << SkBitmap::kRGB_565_Config)
+#define C8MASK  (1 << SkBitmap::kA8_Config)
+
+static SkBitmap::Config resolve_config(SkCanvas* canvas,
+                                       const SkIRect& bounds,
+                                       SkCanvas::SaveFlags flags,
+                                       bool* isOpaque) {
+    *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
+
+#if 0
+    // loop through and union all the configs we may draw into
+    uint32_t configMask = 0;
+    for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
+    {
+        SkDevice* device = canvas->getLayerDevice(i);
+        if (device->intersects(bounds))
+            configMask |= 1 << device->config();
+    }
+
+    // if the caller wants alpha or fullcolor, we can't return 565
+    if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
+                 SkCanvas::kHasAlphaLayer_SaveFlag))
+        configMask &= ~C16MASK;
+
+    switch (configMask) {
+    case C8MASK:    // if we only have A8, return that
+        return SkBitmap::kA8_Config;
+
+    case C16MASK:   // if we only have 565, return that
+        return SkBitmap::kRGB_565_Config;
+
+    default:
+        return SkBitmap::kARGB_8888_Config; // default answer
+    }
+#else
+    return SkBitmap::kARGB_8888_Config; // default answer
+#endif
+}
+
+static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
+    return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
+}
+
+int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                        SaveFlags flags) {
+    // do this before we create the layer. We don't call the public save() since
+    // that would invoke a possibly overridden virtual
+    int count = this->internalSave(flags);
+
+    fDeviceCMDirty = true;
+
+    SkIRect         ir;
+    const SkIRect&  clipBounds = this->getTotalClip().getBounds();
+
+    if (NULL != bounds) {
+        SkRect r;
+        
+        this->getTotalMatrix().mapRect(&r, *bounds);
+        r.roundOut(&ir);
+        // early exit if the layer's bounds are clipped out
+        if (!ir.intersect(clipBounds)) {
+            if (bounds_affects_clip(flags))
+                fMCRec->fRegion->setEmpty();
+            return count;
+        }
+    } else {    // no user bounds, so just use the clip
+        ir = clipBounds;
+    }
+
+    // early exit if the clip is now empty
+    if (bounds_affects_clip(flags) &&
+        !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
+        return count;
+    }
+
+    bool isOpaque;
+    SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
+
+    SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
+                                          isOpaque, true);
+    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
+    device->unref();
+
+    layer->fNext = fMCRec->fTopLayer;
+    fMCRec->fLayer = layer;
+    fMCRec->fTopLayer = layer;    // this field is NOT an owner of layer
+
+    return count;
+}
+
+int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+                             SaveFlags flags) {
+    if (0xFF == alpha) {
+        return this->saveLayer(bounds, NULL, flags);
+    } else {
+        SkPaint tmpPaint;
+        tmpPaint.setAlpha(alpha);
+        return this->saveLayer(bounds, &tmpPaint, flags);
+    }
+}
+
+void SkCanvas::restore() {
+    // check for underflow
+    if (fMCStack.count() > 1) {
+        this->internalRestore();
+    }
+}
+
+void SkCanvas::internalRestore() {
+    SkASSERT(fMCStack.count() != 0);
+
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+	// reserve our layer (if any)
+    DeviceCM* layer = fMCRec->fLayer;   // may be null
+    // now detach it from fMCRec so we can pop(). Gets freed after its drawn
+    fMCRec->fLayer = NULL;
+
+    // now do the normal restore()
+    fMCRec->~MCRec();       // balanced in save()
+    fMCStack.pop_back();
+    fMCRec = (MCRec*)fMCStack.back();
+
+    /*  Time to draw the layer's offscreen. We can't call the public drawSprite,
+        since if we're being recorded, we don't want to record this (the
+        recorder will have already recorded the restore).
+    */
+    if (NULL != layer) {
+        if (layer->fNext) {
+            this->drawDevice(layer->fDevice, layer->fX, layer->fY,
+                             layer->fPaint);
+            // reset this, since drawDevice will have set it to true
+            fDeviceCMDirty = true;
+        }
+        SkDELETE(layer);
+	}
+}
+
+int SkCanvas::getSaveCount() const {
+    return fMCStack.count();
+}
+
+void SkCanvas::restoreToCount(int count) {
+    // sanity check
+    if (count < 1) {
+        count = 1;
+    }
+    while (fMCStack.count() > count) {
+        this->restore();
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// can't draw it if its empty, or its too big for a fixed-point width or height
+static bool reject_bitmap(const SkBitmap& bitmap) {
+    return  bitmap.width() <= 0 || bitmap.height() <= 0 ||
+            bitmap.width() > 32767 || bitmap.height() > 32767;
+}
+
+void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
+                                const SkMatrix& matrix, const SkPaint* paint) {
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+
+    if (NULL == paint) {
+        SkPaint tmpPaint;
+        this->commonDrawBitmap(bitmap, matrix, tmpPaint);
+    } else {
+        this->commonDrawBitmap(bitmap, matrix, *paint);
+    }
+}
+
+void SkCanvas::drawDevice(SkDevice* device, int x, int y,
+                          const SkPaint* paint) {
+    SkPaint tmp;
+    if (NULL == paint) {
+        tmp.setDither(true);
+        paint = &tmp;
+    }
+    
+    ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    while (iter.next()) {
+        iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
+                                 *paint);
+    }
+    ITER_END
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preTranslate(dx, dy);
+}
+
+bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preScale(sx, sy);
+}
+
+bool SkCanvas::rotate(SkScalar degrees) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preRotate(degrees);
+}
+
+bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preSkew(sx, sy);
+}
+
+bool SkCanvas::concat(const SkMatrix& matrix) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preConcat(matrix);
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    *fMCRec->fMatrix = matrix;
+}
+
+// this is not virtual, so it must call a virtual method so that subclasses
+// will see its action
+void SkCanvas::resetMatrix() {
+    SkMatrix matrix;
+    
+    matrix.reset();
+    this->setMatrix(matrix);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+    if (fMCRec->fMatrix->rectStaysRect()) {
+        SkRect      r;
+        SkIRect     ir;
+
+        fMCRec->fMatrix->mapRect(&r, rect);
+        r.round(&ir);
+        return fMCRec->fRegion->op(ir, op);
+    } else {
+        SkPath  path;
+
+        path.addRect(rect);
+        return this->clipPath(path, op);
+    }
+}
+
+bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+    SkPath devPath;
+    path.transform(*fMCRec->fMatrix, &devPath);
+
+    if (SkRegion::kIntersect_Op == op) {
+        return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
+    } else {
+        SkRegion base;
+        const SkBitmap& bm = this->getDevice()->accessBitmap(false);
+        base.setRect(0, 0, bm.width(), bm.height());
+        
+        if (SkRegion::kReplace_Op == op) {
+            return fMCRec->fRegion->setPath(devPath, base);
+        } else {
+            SkRegion rgn;
+            rgn.setPath(devPath, base);
+            return fMCRec->fRegion->op(rgn, op);
+        }
+    }
+}
+
+bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+
+    return fMCRec->fRegion->op(rgn, op);
+}
+
+void SkCanvas::computeLocalClipBoundsCompareType() const {
+    SkRect r;
+    
+    if (!this->getClipBounds(&r, kAA_EdgeType)) {
+        fLocalBoundsCompareType.setEmpty();
+    } else {
+        fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
+                                    SkScalarToCompareType(r.fTop),
+                                    SkScalarToCompareType(r.fRight),
+                                    SkScalarToCompareType(r.fBottom));
+    }
+}
+
+bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
+    /*  current impl ignores edgetype, and relies on
+        getLocalClipBoundsCompareType(), which always returns a value assuming
+        antialiasing (worst case)
+     */
+
+    if (fMCRec->fRegion->isEmpty()) {
+        return true;
+    }
+    
+    // check for empty user rect (horizontal)
+    SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
+    SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
+    if (userL >= userR) {
+        return true;
+    }
+
+    // check for empty user rect (vertical)
+    SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
+    SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
+    if (userT >= userB) {
+        return true;
+    }
+    
+    // check if we are completely outside of the local clip bounds
+    const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+    return  userL >= clipR.fRight || userT >= clipR.fBottom ||
+            userR <= clipR.fLeft  || userB <= clipR.fTop;
+}
+
+bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
+    if (fMCRec->fRegion->isEmpty() || path.isEmpty()) {
+        return true;
+    }
+
+    if (fMCRec->fMatrix->rectStaysRect()) {
+        SkRect  r;
+        path.computeBounds(&r, SkPath::kFast_BoundsType);
+        return this->quickReject(r, et);
+    }
+
+    SkPath      dstPath;
+    SkRect      r;
+    SkIRect     ir;
+
+    path.transform(*fMCRec->fMatrix, &dstPath);
+    dstPath.computeBounds(&r, SkPath::kFast_BoundsType);
+    r.round(&ir);
+    if (kAA_EdgeType == et) {
+        ir.inset(-1, -1);
+    }
+    return fMCRec->fRegion->quickReject(ir);
+}
+
+bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
+    /*  current impl ignores edgetype, and relies on
+        getLocalClipBoundsCompareType(), which always returns a value assuming
+        antialiasing (worst case)
+     */
+
+    if (fMCRec->fRegion->isEmpty()) {
+        return true;
+    }
+    
+    SkScalarCompareType userT = SkScalarAs2sCompliment(top);
+    SkScalarCompareType userB = SkScalarAs2sCompliment(bottom);
+    
+    // check for invalid user Y coordinates (i.e. empty)
+    if (userT >= userB) {
+        return true;
+    }
+    
+    // check if we are above or below the local clip bounds
+    const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+    return userT >= clipR.fBottom || userB <= clipR.fTop;
+}
+
+bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
+    const SkRegion& clip = *fMCRec->fRegion;
+    if (clip.isEmpty()) {
+        if (bounds) {
+            bounds->setEmpty();
+        }
+        return false;
+    }
+
+    if (NULL != bounds) {
+        SkMatrix inverse;
+        SkRect   r;
+
+        fMCRec->fMatrix->invert(&inverse);
+        
+        // get the clip's bounds
+        const SkIRect& ibounds = clip.getBounds();
+        // adjust it outwards if we are antialiasing
+        int inset = (kAA_EdgeType == et);
+        r.iset(ibounds.fLeft - inset,  ibounds.fTop - inset,
+               ibounds.fRight + inset, ibounds.fBottom + inset);
+        
+        // invert into local coordinates
+        inverse.mapRect(bounds, r);
+    }
+    return true;
+}
+
+const SkMatrix& SkCanvas::getTotalMatrix() const {
+    return *fMCRec->fMatrix;
+}
+
+const SkRegion& SkCanvas::getTotalClip() const {
+    return *fMCRec->fRegion;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width,
+                                 int height, bool isOpaque, bool isForLayer) {
+    SkBitmap bitmap;
+    
+    bitmap.setConfig(config, width, height);
+    bitmap.setIsOpaque(isOpaque);
+
+    // should this happen in the device subclass?
+    bitmap.allocPixels();   
+    if (!bitmap.isOpaque()) {
+        bitmap.eraseARGB(0, 0, 0, 0);
+    }
+
+    return SkNEW_ARGS(SkDevice, (bitmap));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//  These are the virtual drawing methods
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawPaint(const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPaint(iter, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                          const SkPaint& paint) {
+    if ((long)count <= 0) {
+        return;
+    }
+
+    SkASSERT(pts != NULL);
+
+    ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawPoints(iter, mode, count, pts, paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(r, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+        
+    ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawRect(iter, r, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect r;
+        path.computeBounds(&r, SkPath::kFast_BoundsType);
+        if (this->quickReject(paint.computeFastBounds(r, &r),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+
+    ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawPath(iter, path, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+
+    if (NULL == paint || (paint->getMaskFilter() == NULL)) {
+        SkRect fastBounds;
+        fastBounds.set(x, y,
+                       x + SkIntToScalar(bitmap.width()),
+                       y + SkIntToScalar(bitmap.height()));
+        if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
+            return;
+        }
+    }
+        
+    SkMatrix matrix;
+    matrix.setTranslate(x, y);
+    this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+                              const SkRect& dst, const SkPaint* paint) {
+    if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
+        return;
+    }
+    
+    // do this now, to avoid the cost of calling extract for RLE bitmaps
+    if (this->quickReject(dst, paint2EdgeType(paint))) {
+        return;
+    }
+    
+    SkBitmap        tmp;    // storage if we need a subset of bitmap
+    const SkBitmap* bitmapPtr = &bitmap;
+
+    if (NULL != src) {
+        if (!bitmap.extractSubset(&tmp, *src)) {
+            return;     // extraction failed
+        }
+        bitmapPtr = &tmp;
+    }
+    
+    SkScalar width = SkIntToScalar(bitmapPtr->width());
+    SkScalar height = SkIntToScalar(bitmapPtr->height());    
+    SkMatrix matrix;
+
+    if (dst.width() == width && dst.height() == height) {
+        matrix.setTranslate(dst.fLeft, dst.fTop);
+    } else {
+        SkRect tmpSrc;
+        tmpSrc.set(0, 0, width, height);
+        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+    }
+    this->internalDrawBitmap(*bitmapPtr, matrix, paint);
+}
+
+void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+                                const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+                                const SkPaint& paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    
+    ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawBitmap(iter, bitmap, matrix, paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+    
+    SkPaint tmp;
+    if (NULL == paint) {
+        paint = &tmp;
+    }
+    
+    ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
+                                 *paint);
+    }
+    ITER_END
+}
+
+void SkCanvas::drawText(const void* text, size_t byteLength,
+                        SkScalar x, SkScalar y, const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawPosText(const void* text, size_t byteLength,
+                           const SkPoint pos[], const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
+                                  paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
+                            const SkScalar xpos[], SkScalar constY,
+                            const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
+                                  paint);
+    }
+    
+    ITER_END
+}
+
+void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
+                              const SkPath& path, const SkMatrix* matrix,
+                              const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
+                                     matrix, paint);
+    }
+
+    ITER_END
+}
+
+void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
+                            const SkPoint verts[], const SkPoint texs[],
+                            const SkColor colors[], SkXfermode* xmode,
+                            const uint16_t indices[], int indexCount,
+                            const SkPaint& paint) {
+    ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
+                                   colors, xmode, indices, indexCount, paint);
+    }
+    
+    ITER_END
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These methods are NOT virtual, and therefore must call back into virtual
+// methods, rather than actually drawing themselves.
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+                        SkPorterDuff::Mode mode) {
+    SkPaint paint;
+
+    paint.setARGB(a, r, g, b);
+    if (SkPorterDuff::kSrcOver_Mode != mode) {
+        paint.setPorterDuffXfermode(mode);
+    }
+    this->drawPaint(paint);
+}
+
+void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) {
+    SkPaint paint;
+
+    paint.setColor(c);
+    if (SkPorterDuff::kSrcOver_Mode != mode) {
+        paint.setPorterDuffXfermode(mode);
+    }
+    this->drawPaint(paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
+    SkPoint pt;
+    
+    pt.set(x, y);
+    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
+    SkPoint pt;
+    SkPaint paint;
+    
+    pt.set(x, y);
+    paint.setColor(color);
+    this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+                        const SkPaint& paint) {
+    SkPoint pts[2];
+    
+    pts[0].set(x0, y0);
+    pts[1].set(x1, y1);
+    this->drawPoints(kLines_PointMode, 2, pts, paint);
+}
+
+void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
+                              SkScalar right, SkScalar bottom,
+                              const SkPaint& paint) {
+    SkRect  r;
+
+    r.set(left, top, right, bottom);
+    this->drawRect(r, paint);
+}
+
+void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+                          const SkPaint& paint) {
+    if (radius < 0) {
+        radius = 0;
+    }
+
+    SkRect  r;
+    r.set(cx - radius, cy - radius, cx + radius, cy + radius);
+    
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(r, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+    
+    SkPath  path;
+    path.addOval(r);
+    this->drawPath(path, paint);
+}
+
+void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
+                             const SkPaint& paint) {
+    if (rx > 0 && ry > 0) {
+        if (paint.canComputeFastBounds()) {
+            SkRect storage;
+            if (this->quickReject(paint.computeFastBounds(r, &storage),
+                                  paint2EdgeType(&paint))) {
+                return;
+            }
+        }
+
+        SkPath  path;
+        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+        this->drawPath(path, paint);
+    } else {
+        this->drawRect(r, paint);
+    }
+}
+
+void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(oval, &storage),
+                              paint2EdgeType(&paint))) {
+            return;
+        }
+    }
+
+    SkPath  path;
+    path.addOval(oval);
+    this->drawPath(path, paint);
+}
+
+void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
+                       SkScalar sweepAngle, bool useCenter,
+                       const SkPaint& paint) {
+    if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
+        this->drawOval(oval, paint);
+    } else {
+        SkPath  path;
+        if (useCenter) {
+            path.moveTo(oval.centerX(), oval.centerY());
+        }
+        path.arcTo(oval, startAngle, sweepAngle, !useCenter);
+        if (useCenter) {
+            path.close();
+        }
+        this->drawPath(path, paint);
+    }
+}
+
+void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
+                                const SkPath& path, SkScalar hOffset,
+                                SkScalar vOffset, const SkPaint& paint) {
+    SkMatrix    matrix;
+    
+    matrix.setTranslate(hOffset, vOffset);
+    this->drawTextOnPath(text, byteLength, path, &matrix, paint);
+}
+
+void SkCanvas::drawPicture(SkPicture& picture) {
+    int saveCount = save();
+    picture.draw(this);
+    restoreToCount(saveCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
+    // need COMPILE_TIME_ASSERT
+    SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
+
+    SkASSERT(canvas);
+
+    fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
+    fDone = !fImpl->next();
+}
+
+SkCanvas::LayerIter::~LayerIter() {
+    fImpl->~SkDrawIter();
+}
+
+void SkCanvas::LayerIter::next() {
+    fDone = !fImpl->next();
+}
+
+SkDevice* SkCanvas::LayerIter::device() const {
+    return fImpl->getDevice();
+}
+
+const SkMatrix& SkCanvas::LayerIter::matrix() const {
+    return fImpl->getMatrix();
+}
+
+const SkPaint& SkCanvas::LayerIter::paint() const {
+    const SkPaint* paint = fImpl->getPaint();
+    if (NULL == paint) {
+        paint = &fDefaultPaint;
+    }
+    return *paint;
+}
+
+const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
+int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
+int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
+