am 8c4d7a6d: (-s ours) am b4f070ed: Do not merge.

* commit '8c4d7a6dc81ba9c684212ebc9af3a8cc039b8c36':
  Do not merge.
diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp
index 6f34eb5..3874bb3 100644
--- a/bench/RectBench.cpp
+++ b/bench/RectBench.cpp
@@ -89,14 +89,20 @@
 
 protected:
     virtual void onDraw(SkCanvas* canvas) {
-        static const SkScalar gSizes[] = {
+        SkScalar gSizes[] = {
             SkIntToScalar(7), 0
         };
+        size_t sizes = SK_ARRAY_COUNT(gSizes);
+
+        if (this->hasStrokeWidth()) {
+            gSizes[0] = this->getStrokeWidth();
+            sizes = 1;
+        }
 
         SkPaint paint;
         paint.setStrokeCap(SkPaint::kRound_Cap);
-        
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gSizes); i++) {
+
+        for (size_t i = 0; i < sizes; i++) {
             paint.setStrokeWidth(gSizes[i]);
             this->setupPaint(&paint);
             canvas->drawPoints(fMode, N * 2,
@@ -132,4 +138,3 @@
 static BenchRegistry gPointsReg(PointsFactory);
 static BenchRegistry gLinesReg(LinesFactory);
 static BenchRegistry gPolygonReg(PolygonFactory);
-
diff --git a/bench/SkBenchmark.cpp b/bench/SkBenchmark.cpp
index e8bea6e..230a7af 100644
--- a/bench/SkBenchmark.cpp
+++ b/bench/SkBenchmark.cpp
@@ -9,6 +9,7 @@
     fForceAlpha = 0xFF;
     fForceAA = true;
     fDither = SkTriState::kDefault;
+    fHasStrokeWidth = false;
 }
 
 const char* SkBenchmark::getName() {
diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h
index bc9794a..945db80 100644
--- a/bench/SkBenchmark.h
+++ b/bench/SkBenchmark.h
@@ -42,6 +42,19 @@
         fDither = state;
     }
 
+    void setStrokeWidth(SkScalar width) {
+      strokeWidth = width;
+      fHasStrokeWidth = true;
+    }
+
+    SkScalar getStrokeWidth() {
+      return strokeWidth;
+    }
+
+    bool hasStrokeWidth() {
+      return fHasStrokeWidth;
+    }
+
     const char* findDefine(const char* key) const;
     bool findDefine32(const char* key, int32_t* value) const;
     bool findDefineScalar(const char* key, SkScalar* value) const;
@@ -60,9 +73,10 @@
     bool    fForceAA;
     bool    fForceFilter;
     SkTriState::State  fDither;
+    bool    fHasStrokeWidth;
+    SkScalar strokeWidth;
 };
 
 typedef SkTRegistry<SkBenchmark*, void*> BenchRegistry;
 
 #endif
-
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 7443604..2f8b006 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -201,6 +201,8 @@
     bool doClip = false;
     bool doPict = false;
     const char* matchStr = NULL;
+    bool hasStrokeWidth = false;
+    float strokeWidth;
 
     SkString outDir;
     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
@@ -260,6 +262,19 @@
                 return -1;
             }
             forceAlpha = wantAlpha ? 0x80 : 0xFF;
+        } else if (strcmp(*argv, "-strokeWidth") == 0) {
+            argv++;
+            if (argv < stop) {
+                const char *strokeWidthStr = *argv;
+                if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
+                  log_error("bad arg for -strokeWidth\n");
+                  return -1;
+                }
+                hasStrokeWidth = true;
+            } else {
+                log_error("missing arg for -strokeWidth\n");
+                return -1;
+            }
         } else if (strcmp(*argv, "-match") == 0) {
             argv++;
             if (argv < stop) {
@@ -314,6 +329,9 @@
         bench->setForceAA(forceAA);
         bench->setForceFilter(forceFilter);
         bench->setDither(forceDither);
+        if (hasStrokeWidth) {
+            bench->setStrokeWidth(strokeWidth);
+        }
 
         // only run benchmarks if their name contains matchStr
         if (matchStr && strstr(bench->getName(), matchStr) == NULL) {
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 1c16b78..1def198 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -396,10 +396,15 @@
     int extractMipLevel(SkBitmap* dst, SkFixed sx, SkFixed sy);
 
     void extractAlpha(SkBitmap* dst) const {
-        this->extractAlpha(dst, NULL, NULL);
+        this->extractAlpha(dst, NULL, NULL, NULL);
     }
 
     void extractAlpha(SkBitmap* dst, const SkPaint* paint,
+                      SkIPoint* offset) const {
+        this->extractAlpha(dst, paint, NULL, offset);
+    }
+
+    void extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator,
                       SkIPoint* offset) const;
 
     void flatten(SkFlattenableWriteBuffer&) const;
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index bfa0d10..b5ccca6 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -29,6 +29,7 @@
 
 class SkBounder;
 class SkDevice;
+class SkDeviceFactory;
 class SkDraw;
 class SkDrawFilter;
 class SkPicture;
@@ -51,29 +52,37 @@
 */
 class SkCanvas : public SkRefCnt {
 public:
-    /** Construct a canvas with the specified bitmap to draw into.
+    /** Construct a canvas with the given device factory.
+        @param factory  Specify the factory for generating additional devices.
+                        The factory may be null, in which case
+                        SkRasterDeviceFactory will be used.
+    */
+    explicit SkCanvas(SkDeviceFactory* factory = NULL);
+
+    /** Construct a canvas with the specified device to draw into.  The device
+        factory will be retrieved from the passed device.
+        @param device   Specifies a device for the canvas to draw into.
+    */
+    explicit SkCanvas(SkDevice* device);
+
+    /** Deprecated - Construct a canvas with the specified bitmap to draw into.
         @param bitmap   Specifies a bitmap for the canvas to draw into. Its
                         structure are copied to the canvas.
     */
     explicit SkCanvas(const SkBitmap& bitmap);
-    /** Construct a canvas with the specified device to draw into.
-        @param device   Specifies a device for the canvas to draw into. The
-                        device may be null.
-    */
-    explicit SkCanvas(SkDevice* device = NULL);
     virtual ~SkCanvas();
 
     ///////////////////////////////////////////////////////////////////////////
 
-    /** If this subclass of SkCanvas supports GL viewports, return true and set
-        size (if not null) to the size of the viewport. If it is not supported,
-        ignore vp and return false.
+    /** If the Device supports GL viewports, return true and set size (if not
+        null) to the size of the viewport. If it is not supported, ignore size
+        and return false.
     */
     virtual bool getViewport(SkIPoint* size) const;
-    
-    /** If this subclass of SkCanvas supports GL viewports, return true and set
-        the viewport to the specified x and y dimensions. If it is not
-        supported, ignore x and y and return false.
+
+    /** If the Device supports GL viewports, return true and set the viewport
+        to the specified x and y dimensions. If it is not supported, ignore x
+        and y and return false.
     */
     virtual bool setViewport(int x, int y);
 
@@ -88,15 +97,16 @@
         device, its reference count is decremented. The new device is returned.
     */
     SkDevice* setDevice(SkDevice* device);
-    
-    /** Specify a bitmap for the canvas to draw into. This is a help method for
-        setDevice(), and it creates a device for the bitmap by calling
-        createDevice(). The structure of the bitmap is copied into the device.
+
+    /** Deprecated - Specify a bitmap for the canvas to draw into. This is a
+        helper method for setDevice(), and it creates a device for the bitmap by
+        calling createDevice(). The structure of the bitmap is copied into the
+        device.
     */
     virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap);
 
     ///////////////////////////////////////////////////////////////////////////
-    
+
     enum SaveFlags {
         /** save the matrix state, restoring it on restore() */
         kMatrix_SaveFlag            = 0x01,
@@ -758,6 +768,7 @@
 
     SkBounder*  fBounder;
     SkDevice*   fLastDeviceToGainFocus;
+    SkDeviceFactory* fDeviceFactory;
 
     void prepareForDeviceDraw(SkDevice*);
     
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 0d724ba..dbc8fcf 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -22,26 +22,53 @@
 #include "SkCanvas.h"
 #include "SkColor.h"
 
+class SkDevice;
 class SkDraw;
 struct SkIRect;
 class SkMatrix;
 class SkRegion;
 
+/** \class SkDeviceFactory
+
+    Devices that extend SkDevice should also provide a SkDeviceFactory class
+    to pass into SkCanvas.  Doing so will eliminate the need to extend
+    SkCanvas as well.
+*/
+class SkDeviceFactory {
+public:
+    virtual ~SkDeviceFactory();
+    virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height,
+                                bool isOpaque, bool isForLayer) = 0;
+};
+
+class SkRasterDeviceFactory : public SkDeviceFactory {
+public:
+    virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height,
+                                bool isOpaque, bool isForLayer);
+};
+
 class SkDevice : public SkRefCnt {
 public:
     SkDevice();
-    /** Construct a new device, extracting the width/height/config/isOpaque values from
-        the bitmap. If transferPixelOwnership is true, and the bitmap claims to own its
-        own pixels (getOwnsPixels() == true), then transfer this responsibility to the
-        device, and call setOwnsPixels(false) on the bitmap.
-        
-        Subclasses may override the destructor, which is virtual, even though this class
-        doesn't have one. SkRefCnt does.
-    
+    /** Construct a new device, extracting the width/height/config/isOpaque
+        values from the bitmap.  Subclasses may override the destructor, which
+        is virtual, even though this class doesn't have one. SkRefCnt does.
+
         @param bitmap   A copy of this bitmap is made and stored in the device
     */
     SkDevice(const SkBitmap& bitmap);
 
+    virtual SkDeviceFactory* getDeviceFactory() {
+        return SkNEW(SkRasterDeviceFactory);
+    }
+
+    enum Capabilities {
+        kGL_Capability     = 0x1,  //!< mask indicating GL support
+        kVector_Capability = 0x2,  //!< mask indicating a vector representation
+        kAll_Capabilities  = 0x3
+    };
+    virtual uint32_t getDeviceCapabilities() { return 0; }
+
     /** Return the width of the device (in pixels).
     */
     int width() const { return fBitmap.width(); }
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
index dd5808a..25c926f 100644
--- a/include/core/SkGraphics.h
+++ b/include/core/SkGraphics.h
@@ -33,7 +33,12 @@
         Returns true if some amount was purged from the font cache.
     */
     static bool SetFontCacheUsed(size_t usageInBytes);
-    
+
+    /** Return the version numbers for the library. If the parameter is not
+        null, it is set to the version number.
+     */
+    static void GetVersion(int32_t* major, int32_t* minor, int32_t* patch);
+
 private:
     /** This is automatically called by SkGraphics::Init(), and must be
         implemented by the host OS. This allows the host OS to register a callback
diff --git a/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h
index b6a013d..1c36b74 100644
--- a/include/core/SkMallocPixelRef.h
+++ b/include/core/SkMallocPixelRef.h
@@ -49,8 +49,9 @@
 
     SkMallocPixelRef(SkFlattenableReadBuffer& buffer);
 
-private:
     void*           fStorage;
+
+private:
     size_t          fSize;
     SkColorTable*   fCTable;
 
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 5fef527..f2df226 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -93,7 +93,10 @@
 
     void setHinting(Hinting hintingLevel)
     {
-        fHinting = hintingLevel;
+        if ((unsigned) hintingLevel != fHinting) {
+            fGenerationID++;
+            fHinting = hintingLevel;
+        }
     }
 
     /** Specifies the bit values that are stored in the paint's flags.
@@ -806,6 +809,11 @@
     void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y,
                      SkPath* path) const;
 
+    const SkGlyph& getUnicharMetrics(SkUnichar);
+    const void* findImage(const SkGlyph&);
+
+    uint32_t getGenerationID() const;
+
 private:
     SkTypeface*     fTypeface;
     SkScalar        fTextSize;
@@ -830,6 +838,7 @@
     unsigned        fStyle : 2;
     unsigned        fTextEncoding : 2;  // 3 values
     unsigned        fHinting : 2;
+    uint32_t        fGenerationID;
 
     SkDrawCacheProc    getDrawCacheProc() const;
     SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir,
@@ -842,7 +851,7 @@
 
     void descriptorProc(const SkMatrix* deviceMatrix,
                         void (*proc)(const SkDescriptor*, void*),
-                        void* context) const;
+                        void* context, bool ignoreGamma = false) const;
 
     const SkRect& computeStrokeFastBounds(const SkRect& orig,
                                           SkRect* storage) const;
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 3afea09..eb5ff6d 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -571,6 +571,8 @@
     */
     void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
 
+    uint32_t getGenerationID() const;
+
     SkDEBUGCODE(void validate() const;)
 
 private:
@@ -580,6 +582,7 @@
     mutable uint8_t     fBoundsIsDirty;
     uint8_t             fFillType;
     uint8_t             fIsConvex;
+    uint32_t            fGenerationID;
 
     // called, if dirty, by getBounds()
     void computeBounds() const;
diff --git a/src/core/SkPictureFlat.h b/include/core/SkPictureFlat.h
similarity index 100%
rename from src/core/SkPictureFlat.h
rename to include/core/SkPictureFlat.h
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index 82e5ca7..f1dab36 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -112,6 +112,18 @@
     virtual Factory getFactory() const { return NULL; }
     virtual void flatten(SkFlattenableWriteBuffer&) const;
 
+    /** Acquire a "global" ref on this object.
+    * The default implementation just calls ref(), but subclasses can override
+    * this method to implement additional behavior.
+    */
+    virtual void globalRef(void* data=NULL);
+
+    /** Release a "global" ref on this object.
+    * The default implementation just calls unref(), but subclasses can override
+    * this method to implement additional behavior.
+    */
+    virtual void globalUnref();
+
     static Factory NameToFactory(const char name[]);
     static const char* FactoryToName(Factory);
     static void Register(const char name[], Factory);
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 879d81a..fbd9f7f 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -233,6 +233,18 @@
 struct SkRect {
     SkScalar    fLeft, fTop, fRight, fBottom;
 
+    static SkRect MakeEmpty() {
+        SkRect r;
+        r.setEmpty();
+        return r;
+    }
+
+    static SkRect MakeWH(SkScalar w, SkScalar h) {
+        SkRect r;
+        r.set(0, 0, w, h);
+        return r;
+    }
+
     static SkRect MakeSize(const SkSize& size) {
         SkRect r;
         r.set(0, 0, size.width(), size.height());
@@ -250,7 +262,7 @@
         r.set(x, y, x + w, y + h);
         return r;
     }
-    
+
     /** Return true if the rectangle's width or height are <= 0
     */
     bool        isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index 27ebd41..d484389 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -61,8 +61,10 @@
 
 template <typename T> class SkAutoTDelete : SkNoncopyable {
 public:
-    SkAutoTDelete(T* obj) : fObj(obj) {}
-    ~SkAutoTDelete() { delete fObj; }
+    SkAutoTDelete(T* obj, bool deleteWhenDone = true) : fObj(obj) {
+        this->deleteWhenDone = deleteWhenDone;
+    }
+    ~SkAutoTDelete() { if (deleteWhenDone) delete fObj; }
 
     T*      get() const { return fObj; }
     void    free() { delete fObj; fObj = NULL; }
@@ -70,6 +72,7 @@
 
 private:
     T*  fObj;
+    bool deleteWhenDone;
 };
 
 template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
index fa21adc..1f8ecaa 100644
--- a/include/core/SkTypes.h
+++ b/include/core/SkTypes.h
@@ -30,6 +30,12 @@
 /** \file SkTypes.h
 */
 
+/** See SkGraphics::GetVersion() to retrieve these at runtime
+ */
+#define SKIA_VERSION_MAJOR  1
+#define SKIA_VERSION_MINOR  0
+#define SKIA_VERSION_PATCH  0
+
 /*
     memory wrappers to be implemented by the porting layer (platform)
 */
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
index 7a06467..d92d346 100644
--- a/include/core/SkXfermode.h
+++ b/include/core/SkXfermode.h
@@ -137,6 +137,8 @@
         return false and ignore the mode parameter.
      */
     static bool IsMode(SkXfermode*, Mode* mode);
+    
+    Mode fMode;
 
 protected:
     SkXfermode(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
diff --git a/include/effects/SkPorterDuff.h b/include/effects/SkPorterDuff.h
index 6f4ac20..4d7fb31 100644
--- a/include/effects/SkPorterDuff.h
+++ b/include/effects/SkPorterDuff.h
@@ -52,6 +52,7 @@
         kMultiply_Mode, //!< [Sa * Da, Sc * Dc]
         kScreen_Mode,   //!< [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
         kAdd_Mode,      //!< Saturate(S + D)
+        kOverlay_Mode,
 
         kModeCount
     };
diff --git a/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h
index 3c904ca..0f71a3f 100644
--- a/include/images/SkImageDecoder.h
+++ b/include/images/SkImageDecoder.h
@@ -205,9 +205,9 @@
 
         note: document use of Allocator, Peeker and Chooser
     */
-    bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode);
-    bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode) {
-        return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode);
+    bool decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode, bool reuseBitmap = false);
+    bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode, bool reuseBitmap = false) {
+        return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode, reuseBitmap);
     }
 
     /**
diff --git a/include/utils/SkGLCanvas.h b/include/utils/SkGLCanvas.h
index 40fc52d..eb99527 100644
--- a/include/utils/SkGLCanvas.h
+++ b/include/utils/SkGLCanvas.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -19,31 +19,11 @@
 
 #include "SkCanvas.h"
 
-#ifdef SK_BUILD_FOR_MAC
-    #include <OpenGL/gl.h>
-#elif defined(ANDROID)
-    #include <GLES/gl.h>
-#endif
-
-class SkGLDevice;
-class SkGLClipIter;
-
+// Deprecated.  You should now use SkGLDevice and SkGLDeviceFactory with
+// SkCanvas.
 class SkGLCanvas : public SkCanvas {
 public:
-    // notice, we do NOT allow the SkCanvas(bitmap) constructor, since that
-    // does not create a SkGLDevice, which we require
     SkGLCanvas();
-    virtual ~SkGLCanvas();
-
-    // overrides from SkCanvas
-
-    virtual bool getViewport(SkIPoint*) const;
-    virtual bool setViewport(int width, int height);
-
-    virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
-                                   bool isOpaque, bool isForLayer);
-
-    // settings for the global texture cache
 
     static size_t GetTextureCacheMaxCount();
     static void SetTextureCacheMaxCount(size_t count);
@@ -51,30 +31,9 @@
     static size_t GetTextureCacheMaxSize();
     static void SetTextureCacheMaxSize(size_t size);
 
-    /** Call glDeleteTextures for all textures (including those for text)
-        This should be called while the gl-context is still valid. Its purpose
-        is to free up gl resources. Note that if a bitmap or text is drawn after
-        this call, new caches will be created.
-    */
     static void DeleteAllTextures();
 
-    /** Forget all textures without calling delete (including those for text).
-        This should be called if the gl-context has changed, and the texture
-        IDs that have been cached are no longer valid.
-    */
     static void AbandonAllTextures();
-
-private:
-    SkIPoint fViewportSize;
-
-    // need to disallow this guy
-    virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap) {
-        sk_throw();
-        return NULL;
-    }
-
-    typedef SkCanvas INHERITED;
 };
 
 #endif
-
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 0276897..e7d9537 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -1114,7 +1114,7 @@
 #include "SkMatrix.h"
 
 void SkBitmap::extractAlpha(SkBitmap* dst, const SkPaint* paint,
-                            SkIPoint* offset) const {
+                            Allocator *allocator, SkIPoint* offset) const {
     SkDEBUGCODE(this->validate();)
 
     SkMatrix    identity;
@@ -1138,7 +1138,7 @@
     NO_FILTER_CASE:
         dst->setConfig(SkBitmap::kA8_Config, this->width(), this->height(),
                        srcM.fRowBytes);
-        dst->allocPixels();
+        dst->allocPixels(allocator, NULL);
         GetBitmapAlpha(*this, dst->getAddr8(0, 0), srcM.fRowBytes);
         if (offset) {
             offset->set(0, 0);
@@ -1157,7 +1157,7 @@
 
     dst->setConfig(SkBitmap::kA8_Config, dstM.fBounds.width(),
                    dstM.fBounds.height(), dstM.fRowBytes);
-    dst->allocPixels();
+    dst->allocPixels(allocator, NULL);
     memcpy(dst->getPixels(), dstM.fImage, dstM.computeImageSize());
     if (offset) {
         offset->set(dstM.fBounds.fLeft, dstM.fBounds.fTop);
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index bd4fece..c3fd7d0 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -137,6 +137,15 @@
 
 #define BUF_MAX     128
 
+#define TEST_BUFFER_OVERRITEx
+
+#ifdef TEST_BUFFER_OVERRITE
+    #define TEST_BUFFER_EXTRA   32
+    #define TEST_PATTERN    0x88888888
+#else
+    #define TEST_BUFFER_EXTRA   0
+#endif
+
 void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
     const SkBitmapProcState& state = fState;
     if (state.fShaderProc32) {
@@ -144,10 +153,10 @@
         return;
     }
 
-    uint32_t buffer[BUF_MAX];
+    uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
     SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
     SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32;
-    int max = fState.maxCountForBufferSize(sizeof(buffer));
+    int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
 
     SkASSERT(state.fBitmap->getPixels());
     SkASSERT(state.fBitmap->pixelRef() == NULL ||
@@ -158,12 +167,24 @@
         if (n > max) {
             n = max;
         }
+        SkASSERT(n > 0 && n < BUF_MAX*2);
+#ifdef TEST_BUFFER_OVERRITE
+        for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
+            buffer[BUF_MAX + i] = TEST_PATTERN;
+        }
+#endif
         mproc(state, buffer, n, x, y);
+#ifdef TEST_BUFFER_OVERRITE
+        for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
+            SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
+        }
+#endif
         sproc(state, buffer, n, dstC);
         
         if ((count -= n) == 0) {
             break;
         }
+        SkASSERT(count > 0);
         x += n;
         dstC += n;
     }
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index eabd966..e54818d 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -543,7 +543,6 @@
  */
 int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const {
     int32_t size = static_cast<int32_t>(bufferSize);
-    int perElemShift;
 
     size &= ~3; // only care about 4-byte aligned chunks
     if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
@@ -551,11 +550,15 @@
         if (size < 0) {
             size = 0;
         }
-        perElemShift = fDoFilter ? 2 : 1;
+        size >>= 1;
     } else {
-        perElemShift = fDoFilter ? 3 : 2;
+        size >>= 2;
     }
 
-    return size >> perElemShift;
+    if (fDoFilter) {
+        size >>= 1;
+    }
+
+    return size;
 }
 
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
index dd73c33..303696f 100644
--- a/src/core/SkBitmapProcState.h
+++ b/src/core/SkBitmapProcState.h
@@ -91,10 +91,11 @@
      */
     void platformProcs();
 
-    /** Given the size of a buffer, to be used for calling the matrix and
-        sample procs, this return the maximum count that can be stored in the
-        buffer, taking into account that filtering and scale-vs-affine affect
-        this value.
+    /** Given the byte size of the index buffer to be passed to the matrix proc,
+        return the maximum number of resulting pixels that can be computed
+        (i.e. the number of SkPMColor values to be written by the sample proc).
+        This routine takes into account that filtering and scale-vs-affine
+        affect the amount of buffer space needed.
      
         Only valid to call after chooseProcs (setContext) has been called. It is
         safe to call this inside the shader's shadeSpan() method.
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
index 6654312..d0bc8d8 100644
--- a/src/core/SkBitmapProcState_matrixProcs.cpp
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -368,7 +368,7 @@
     }
 
     // fill the remaining with the max value
-    sk_memset16(xptr, width - 1, count * sizeof(uint16_t));
+    sk_memset16(xptr, width - 1, count);
 }
 
 static void repeatx_nofilter_trans(const SkBitmapProcState& s,
diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
index 18b0881..37ecdfc 100644
--- a/src/core/SkBlitter_A8.cpp
+++ b/src/core/SkBlitter_A8.cpp
@@ -271,7 +271,7 @@
 
 SkA8_Shader_Blitter::~SkA8_Shader_Blitter()
 {
-    fXfermode->safeUnref();
+    if (fXfermode) fXfermode->safeUnref();
     sk_free(fBuffer);
 }
 
@@ -377,7 +377,9 @@
     while (--height >= 0)
     {
         fShader->shadeSpan(x, y, span, width);
-        fXfermode->xferA8(device, span, width, alpha);
+        if (fXfermode) {
+            fXfermode->xferA8(device, span, width, alpha);
+        }
         
         y += 1;
         device += fDevice.rowBytes();
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 26e39b5..94c2439 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -404,8 +404,20 @@
     return this->setDevice(device);
 }
 
+SkCanvas::SkCanvas(SkDeviceFactory* factory)
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
+          fDeviceFactory(factory) {
+    inc_canvas();
+
+    if (!factory)
+        fDeviceFactory = SkNEW(SkRasterDeviceFactory);
+
+    this->init(NULL);
+}
+
 SkCanvas::SkCanvas(SkDevice* device)
-        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+        : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
+          fDeviceFactory(device->getDeviceFactory()) {
     inc_canvas();
 
     this->init(device);
@@ -415,7 +427,9 @@
         : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
     inc_canvas();
 
-    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+    SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap));
+    fDeviceFactory = device->getDeviceFactory();
+    this->init(device)->unref();
 }
 
 SkCanvas::~SkCanvas() {
@@ -423,8 +437,9 @@
     this->restoreToCount(1);    // restore everything but the last
     this->internalRestore();    // restore the last, since we're going away
 
-    fBounder->safeUnref();
-    
+    SkSafeUnref(fBounder);
+    SkDELETE(fDeviceFactory);
+
     dec_canvas();
 }
 
@@ -521,11 +536,19 @@
 //////////////////////////////////////////////////////////////////////////////
 
 bool SkCanvas::getViewport(SkIPoint* size) const {
-    return false;
+    if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
+        return false;
+    if (size)
+        size->set(getDevice()->width(), getDevice()->height());
+    return true;
 }
 
 bool SkCanvas::setViewport(int width, int height) {
-    return false;
+    if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
+        return false;
+    this->setDevice(createDevice(SkBitmap::kARGB_8888_Config, width, height,
+                                 false, false))->unref();
+    return true;
 }
 
 void SkCanvas::updateDeviceCMCache() {
@@ -1004,18 +1027,9 @@
 
 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));
+    return fDeviceFactory->newDevice(config, width, height, isOpaque,
+                                     isForLayer);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 8ce1bd8..b629e9a 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -2,6 +2,8 @@
 #include "SkDraw.h"
 #include "SkRect.h"
 
+SkDeviceFactory::~SkDeviceFactory() {}
+
 SkDevice::SkDevice() {}
 
 SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {}
@@ -113,4 +115,16 @@
     draw.drawSprite(device->accessBitmap(false), x, y, paint);
 }
 
+SkDevice* SkRasterDeviceFactory::newDevice(SkBitmap::Config config, int width,
+                                           int height, bool isOpaque,
+                                           bool isForLayer) {
+    SkBitmap bitmap;
+    bitmap.setConfig(config, width, height);
+    bitmap.setIsOpaque(isOpaque);
 
+    bitmap.allocPixels();
+    if (!bitmap.isOpaque())
+        bitmap.eraseARGB(0, 0, 0, 0);
+
+    return SkNEW_ARGS(SkDevice, (bitmap));
+}
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 301c0e4..fb5e045 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -789,7 +789,7 @@
     matrix.mapVectors(dst, src, 2);
     SkScalar len0 = fast_len(dst[0]);
     SkScalar len1 = fast_len(dst[1]);
-    if (len0 < SK_Scalar1 && len1 < SK_Scalar1) {
+    if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
         *value = SkScalarAve(len0, len1);
         return true;
     }
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
index 65a16e2..40f0ea3 100644
--- a/src/core/SkGraphics.cpp
+++ b/src/core/SkGraphics.cpp
@@ -374,3 +374,15 @@
     return SkGlyphCache::SetCacheUsed(usageInBytes);
 }
 
+void SkGraphics::GetVersion(int32_t* major, int32_t* minor, int32_t* patch) {
+    if (major) {
+        *major = SKIA_VERSION_MAJOR;
+    }
+    if (minor) {
+        *minor = SKIA_VERSION_MINOR;
+    }
+    if (patch) {
+        *patch = SKIA_VERSION_PATCH;
+    }
+}
+
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index da818c4..f203d35 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -63,6 +63,7 @@
     fStyle      = kFill_Style;
     fTextEncoding = kUTF8_TextEncoding;
     fHinting    = kNormal_Hinting;
+    fGenerationID = 0;
 }
 
 SkPaint::SkPaint(const SkPaint& src)
@@ -113,7 +114,9 @@
     fRasterizer->safeUnref();
     fLooper->safeUnref();
 
+    uint32_t oldGenerationID = fGenerationID;
     memcpy(this, &src, sizeof(src));
+    fGenerationID = oldGenerationID + 1;
 
     return *this;
 }
@@ -127,73 +130,119 @@
 {
     SkPaint init;
 
+    uint32_t oldGenerationID = fGenerationID;
     *this = init;
+    fGenerationID = oldGenerationID + 1;
+}
+
+uint32_t SkPaint::getGenerationID() const {
+    return fGenerationID;
 }
 
 void SkPaint::setFlags(uint32_t flags)
 {
-    fFlags = flags;
+    if (fFlags != flags) {
+        fFlags = flags;
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setAntiAlias(bool doAA)
 {
-    this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+    if (doAA != isAntiAlias()) {
+        this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setDither(bool doDither)
 {
-    this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+    if (doDither != isDither()) {
+        this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setSubpixelText(bool doSubpixel)
 {
-    this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+    if (doSubpixel != isSubpixelText()) {
+        this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setLCDRenderText(bool doLCDRender)
 {
-    this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
+    if (doLCDRender != isLCDRenderText()) {
+        this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText)
 {
-    this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
+    if (doEmbeddedBitmapText != isEmbeddedBitmapText()) {
+        this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setLinearText(bool doLinearText)
 {
-    this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+    if (doLinearText != isLinearText()) {
+        this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setUnderlineText(bool doUnderline)
 {
-    this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+    if (doUnderline != isUnderlineText()) {
+        this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setStrikeThruText(bool doStrikeThru)
 {
-    this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+    if (doStrikeThru != isStrikeThruText()) {
+        this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setFakeBoldText(bool doFakeBold)
 {
-    this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+    if (doFakeBold != isFakeBoldText()) {
+        this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setDevKernText(bool doDevKern)
 {
-    this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+    if (doDevKern != isDevKernText()) {
+        this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setFilterBitmap(bool doFilter)
 {
-    this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
+    if (doFilter != isFilterBitmap()) {
+        this->setFlags(SkSetClearMask(fFlags, doFilter, kFilterBitmap_Flag));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setStyle(Style style)
 {
-    if ((unsigned)style < kStyleCount)
-        fStyle = style;
+    if ((unsigned)style < kStyleCount) {
+        if ((unsigned)style != fStyle) {
+            fStyle = style;
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
@@ -202,23 +251,38 @@
 
 void SkPaint::setColor(SkColor color)
 {
-    fColor = color;
+    if (color != fColor) {
+        fColor = color;
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setAlpha(U8CPU a)
 {
-    fColor = SkColorSetARGB(a, SkColorGetR(fColor), SkColorGetG(fColor), SkColorGetB(fColor));
+    U8CPU oldA = SkColorGetA(fColor);
+    if (a != oldA) {
+        fColor = SkColorSetARGB(a, SkColorGetR(fColor), SkColorGetG(fColor), SkColorGetB(fColor));
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
 {
+    SkColor oldColor = fColor;
     fColor = SkColorSetARGB(a, r, g, b);
+    if (oldColor != fColor) {
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setStrokeWidth(SkScalar width)
 {
-    if (width >= 0)
-        fWidth = width;
+    if (width >= 0) {
+        if (width != fWidth) {
+            fWidth = width;
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
@@ -227,8 +291,12 @@
 
 void SkPaint::setStrokeMiter(SkScalar limit)
 {
-    if (limit >= 0)
-        fMiterLimit = limit;
+    if (limit >= 0) {
+        if (limit != fMiterLimit) {
+            fMiterLimit = limit;
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
@@ -237,8 +305,12 @@
 
 void SkPaint::setStrokeCap(Cap ct)
 {
-    if ((unsigned)ct < kCapCount)
-        fCapType = SkToU8(ct);
+    if ((unsigned)ct < kCapCount) {
+        if ((unsigned)ct != fCapType) {
+            fCapType = SkToU8(ct);
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
@@ -247,8 +319,12 @@
 
 void SkPaint::setStrokeJoin(Join jt)
 {
-    if ((unsigned)jt < kJoinCount)
-        fJoinType = SkToU8(jt);
+    if ((unsigned)jt < kJoinCount) {
+        if ((unsigned)jt != fJoinType) {
+            fJoinType = SkToU8(jt);
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
@@ -259,8 +335,12 @@
 
 void SkPaint::setTextAlign(Align align)
 {
-    if ((unsigned)align < kAlignCount)
-        fTextAlign = SkToU8(align);
+    if ((unsigned)align < kAlignCount) {
+        if ((unsigned)align != fTextAlign) {
+            fTextAlign = SkToU8(align);
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align);
@@ -269,8 +349,12 @@
 
 void SkPaint::setTextSize(SkScalar ts)
 {
-    if (ts > 0)
-        fTextSize = ts;
+    if (ts > 0) {
+        if (ts != fTextSize) {
+            fTextSize = ts;
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setTextSize() called with non-positive value\n");
@@ -279,18 +363,28 @@
 
 void SkPaint::setTextScaleX(SkScalar scaleX)
 {
-    fTextScaleX = scaleX;
+    if (scaleX != fTextScaleX) {
+        fTextScaleX = scaleX;
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setTextSkewX(SkScalar skewX)
 {
-    fTextSkewX = skewX;
+    if (skewX != fTextSkewX) {
+        fTextSkewX = skewX;
+        fGenerationID++;
+    }
 }
 
 void SkPaint::setTextEncoding(TextEncoding encoding)
 {
-    if ((unsigned)encoding <= kGlyphID_TextEncoding)
-        fTextEncoding = encoding;
+    if ((unsigned)encoding <= kGlyphID_TextEncoding) {
+        if ((unsigned)encoding != fTextEncoding) {
+            fTextEncoding = encoding;
+            fGenerationID++;
+        }
+    }
 #ifdef SK_DEBUG
     else
         SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
@@ -302,18 +396,21 @@
 SkTypeface* SkPaint::setTypeface(SkTypeface* font)
 {
     SkRefCnt_SafeAssign(fTypeface, font);
+    fGenerationID++;
     return font;
 }
 
 SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r)
 {
     SkRefCnt_SafeAssign(fRasterizer, r);
+    fGenerationID++;
     return r;
 }
 
 SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper)
 {
     SkRefCnt_SafeAssign(fLooper, looper);
+    fGenerationID++;
     return looper;
 }
 
@@ -322,6 +419,32 @@
 #include "SkGlyphCache.h"
 #include "SkUtils.h"
 
+static void DetachDescProc(const SkDescriptor* desc, void* context)
+{
+    *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
+}
+
+const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text) {
+    SkGlyphCache* cache;
+    descriptorProc(NULL, DetachDescProc, &cache, true);
+
+    const SkGlyph& glyph = cache->getUnicharMetrics(text);
+
+    SkGlyphCache::AttachCache(cache);
+    return glyph;
+}
+
+const void* SkPaint::findImage(const SkGlyph& glyph) {
+    // See ::detachCache()
+    SkGlyphCache* cache;
+    descriptorProc(NULL, DetachDescProc, &cache, true);
+
+    const void* image = cache->findImage(glyph);
+
+    SkGlyphCache::AttachCache(cache);
+    return image;
+}
+
 int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
                           uint16_t glyphs[]) const {
     if (byteLength == 0) {
@@ -1330,11 +1453,15 @@
 
 void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
                              void (*proc)(const SkDescriptor*, void*),
-                             void* context) const
+                             void* context, bool ignoreGamma) const
 {
     SkScalerContext::Rec    rec;
 
     SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+    if (ignoreGamma) {
+        rec.fFlags &= ~(SkScalerContext::kGammaForBlack_Flag |
+                SkScalerContext::kGammaForWhite_Flag);
+    }
 
     size_t          descSize = sizeof(rec);
     int             entryCount = 1;
@@ -1389,11 +1516,6 @@
     proc(desc, context);
 }
 
-static void DetachDescProc(const SkDescriptor* desc, void* context)
-{
-    *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc);
-}
-
 SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const
 {
     SkGlyphCache* cache;
@@ -1548,18 +1670,27 @@
 
 SkShader* SkPaint::setShader(SkShader* shader)
 {
+    if (shader != fShader) {
+        fGenerationID++;
+    }
     SkRefCnt_SafeAssign(fShader, shader);
     return shader;
 }
 
 SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter)
 {
+    if (filter != fColorFilter) {
+        fGenerationID++;
+    }
     SkRefCnt_SafeAssign(fColorFilter, filter);
     return filter;
 }
 
 SkXfermode* SkPaint::setXfermode(SkXfermode* mode)
 {
+    if (mode != fXfermode) {
+        fGenerationID++;
+    }
     SkRefCnt_SafeAssign(fXfermode, mode);
     return mode;
 }
@@ -1567,17 +1698,24 @@
 SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) {
     SkSafeUnref(fXfermode);
     fXfermode = SkXfermode::Create(mode);
+    fGenerationID++;
     return fXfermode;
 }
 
 SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect)
 {
+    if (effect != fPathEffect) {
+        fGenerationID++;
+    }
     SkRefCnt_SafeAssign(fPathEffect, effect);
     return effect;
 }
 
 SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter)
 {
+    if (filter != fMaskFilter) {
+        fGenerationID++;
+    }
     SkRefCnt_SafeAssign(fMaskFilter, filter);
     return filter;
 }
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 9c0b8af..62edb32 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -97,6 +97,7 @@
 
 SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {
     fIsConvex = false;
+    fGenerationID = 0;
 }
 
 SkPath::SkPath(const SkPath& src) {
@@ -118,6 +119,7 @@
         fFillType       = src.fFillType;
         fBoundsIsDirty  = src.fBoundsIsDirty;
         fIsConvex       = src.fIsConvex;
+        fGenerationID++;
     }
     SkDEBUGCODE(this->validate();)
     return *this;
@@ -140,14 +142,20 @@
         SkTSwap<uint8_t>(fFillType, other.fFillType);
         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
         SkTSwap<uint8_t>(fIsConvex, other.fIsConvex);
+        fGenerationID++;
     }
 }
 
+uint32_t SkPath::getGenerationID() const {
+    return fGenerationID;
+}
+
 void SkPath::reset() {
     SkDEBUGCODE(this->validate();)
 
     fPts.reset();
     fVerbs.reset();
+    fGenerationID++;
     fBoundsIsDirty = true;
 }
 
@@ -156,6 +164,7 @@
 
     fPts.rewind();
     fVerbs.rewind();
+    fGenerationID++;
     fBoundsIsDirty = true;
 }
 
@@ -212,6 +221,7 @@
         this->moveTo(x, y);
     } else {
         fPts[count - 1].set(x, y);
+        fGenerationID++;
     }
 }
 
@@ -249,6 +259,7 @@
     }
     pt->set(x, y);
 
+    fGenerationID++;
     fBoundsIsDirty = true;
 }
 
@@ -268,6 +279,7 @@
     fPts.append()->set(x, y);
     *fVerbs.append() = kLine_Verb;
 
+    fGenerationID++;
     fBoundsIsDirty = true;
 }
 
@@ -290,6 +302,7 @@
     pts[1].set(x2, y2);
     *fVerbs.append() = kQuad_Verb;
 
+    fGenerationID++;
     fBoundsIsDirty = true;
 }
 
@@ -313,6 +326,7 @@
     pts[2].set(x3, y3);
     *fVerbs.append() = kCubic_Verb;
 
+    fGenerationID++;
     fBoundsIsDirty = true;
 }
 
@@ -334,6 +348,7 @@
             case kQuad_Verb:
             case kCubic_Verb:
                 *fVerbs.append() = kClose_Verb;
+                fGenerationID++;
                 break;
             default:
                 // don't add a close if the prev wasn't a primitive
@@ -936,6 +951,7 @@
             matrix.mapRect(&dst->fBounds, fBounds);
             dst->fBoundsIsDirty = false;
         } else {
+            dst->fGenerationID++;
             dst->fBoundsIsDirty = true;
         }
 
@@ -1243,6 +1259,7 @@
     buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
     buffer.read(fVerbs.begin(), fVerbs.count());
     
+    fGenerationID++;
     fBoundsIsDirty = true;
 
     SkDEBUGCODE(this->validate();)
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index f488565..c73e21c 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -528,6 +528,8 @@
     SkipClipRec skipRect, skipRegion, skipPath;
 #endif
 
+    SkAutoMutexAcquire autoMutex(fDrawMutex);
+
     TextContainer text;
     fReader.rewind();
 
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index ae9641a..f5bf038 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -12,6 +12,7 @@
 #include "SkRegion.h"
 #include "SkPictureFlat.h"
 #include "SkShape.h"
+#include "SkThread.h"
 
 class SkPictureRecord;
 class SkStream;
@@ -172,6 +173,7 @@
     SkRefCntPlayback fRCPlayback;
     SkTypefacePlayback fTFPlayback;
     SkFactoryPlayback*   fFactoryPlayback;
+    SkMutex fDrawMutex;
 };
 
 #endif
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index e096d63..8bacfae 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -128,3 +128,10 @@
     return NULL;
 }
 
+void SkPixelRef::globalRef(void* data) {
+    ref();
+}
+
+void SkPixelRef::globalUnref() {
+    unref();
+}
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
index 032dc81..a5a1555 100644
--- a/src/core/SkRegion.cpp
+++ b/src/core/SkRegion.cpp
@@ -783,7 +783,13 @@
                     SkRegion::RunType dst[],
                     SkRegion::Op op)
 {
-    const SkRegion::RunType sentinel = SkRegion::kRunTypeSentinel;
+    const SkRegion::RunType gSentinel[] = {
+        SkRegion::kRunTypeSentinel,
+        // just need a 2nd value, since spanRec.init() reads 2 values, even
+        // though if the first value is the sentinel, it ignores the 2nd value.
+        // w/o the 2nd value here, we might read uninitialized memory.
+        0,
+    };
 
     int a_top = *a_runs++;
     int a_bot = *a_runs++;
@@ -803,8 +809,8 @@
     while (a_bot < SkRegion::kRunTypeSentinel || b_bot < SkRegion::kRunTypeSentinel)
     {
         int         top, bot SK_INIT_TO_AVOID_WARNING;
-        const SkRegion::RunType*    run0 = &sentinel;
-        const SkRegion::RunType*    run1 = &sentinel;
+        const SkRegion::RunType*    run0 = gSentinel;
+        const SkRegion::RunType*    run1 = gSentinel;
         bool        a_flush = false;
         bool        b_flush = false;
         int         inside;
@@ -854,7 +860,7 @@
         }
         
         if (top > prevBot)
-            oper.addSpan(top, &sentinel, &sentinel);
+            oper.addSpan(top, gSentinel, gSentinel);
 
 //      if ((unsigned)(inside - oper.fMin) <= (unsigned)(oper.fMax - oper.fMin))
         {
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 8d1531a..7ed77b2 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -315,10 +315,12 @@
 SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
         : SkXfermode(buffer) {
     fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+    fMode = (Mode) buffer.readInt();
 }
 
 void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) {
     buffer.writeFunctionPtr((void*)fProc);
+    buffer.writeInt(fMode);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -941,30 +943,36 @@
     SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
     SkASSERT((unsigned)mode < kModeCount);
 
+    SkXfermode* xferMode = NULL;
     switch (mode) {
         case kClear_Mode:
-            return SkNEW(SkClearXfermode);
+            xferMode = SkNEW(SkClearXfermode);
+            break;
         case kSrc_Mode:
-            return SkNEW(SkSrcXfermode);
+            xferMode = SkNEW(SkSrcXfermode);
+            break;
         case kSrcOver_Mode:
             return NULL;
         case kDstIn_Mode:
-            return SkNEW(SkDstInXfermode);
+            xferMode = SkNEW(SkDstInXfermode);
+            break;
         case kDstOut_Mode:
-            return SkNEW(SkDstOutXfermode);
+            xferMode = SkNEW(SkDstOutXfermode);
+            break;
         // use the table 
         default: {
             const ProcCoeff& rec = gProcCoeffs[mode];
             if ((unsigned)rec.fSC < SkXfermode::kCoeffCount &&
                     (unsigned)rec.fDC < SkXfermode::kCoeffCount) {
-                return SkNEW_ARGS(SkProcCoeffXfermode, (rec.fProc,
-                                                        rec.fSC,
-                                                        rec.fDC));
+                xferMode = SkNEW_ARGS(SkProcCoeffXfermode, (rec.fProc, rec.fSC, rec.fDC));
             } else {
-                return SkNEW_ARGS(SkProcXfermode, (rec.fProc));
+                xferMode = SkNEW_ARGS(SkProcXfermode, (rec.fProc));
             }
+            break;
         }
     }
+    xferMode->fMode = mode;
+    return xferMode;
 }
 
 bool SkXfermode::IsMode(SkXfermode* xfer, Mode* mode) {
diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
index 6ad0136..6ad4451 100644
--- a/src/effects/SkBlurDrawLooper.cpp
+++ b/src/effects/SkBlurDrawLooper.cpp
@@ -52,10 +52,17 @@
 
 bool SkBlurDrawLooper::next()
 {
+    SkColor blurColor;
+    SkAlpha alpha;
     switch (fState) {
     case kBeforeEdge:
         fSavedColor = fPaint->getColor();
-        fPaint->setColor(fBlurColor);
+        blurColor = fBlurColor;
+        alpha = SkColorGetA(blurColor);
+        if (alpha == 255) {
+            blurColor = SkColorSetA(blurColor, fPaint->getAlpha());
+        }
+        fPaint->setColor(blurColor);
         fPaint->setMaskFilter(fBlur);
         fCanvas->save(SkCanvas::kMatrix_SaveFlag);
         fCanvas->translate(fDx, fDy);
diff --git a/src/effects/SkPorterDuff.cpp b/src/effects/SkPorterDuff.cpp
index 58447ad..d0264ba 100644
--- a/src/effects/SkPorterDuff.cpp
+++ b/src/effects/SkPorterDuff.cpp
@@ -29,7 +29,8 @@
     MAKE_PAIR(Lighten),
     MAKE_PAIR(Multiply),
     MAKE_PAIR(Screen),
-    { SkPorterDuff::kAdd_Mode, SkXfermode::kPlus_Mode }
+    { SkPorterDuff::kAdd_Mode, SkXfermode::kPlus_Mode },
+    MAKE_PAIR(Overlay),
 };
 
 static bool find_pdmode(SkXfermode::Mode src, SkPorterDuff::Mode* dst) {
diff --git a/src/gl/SkGLCanvas.cpp b/src/gl/SkGLCanvas.cpp
index f7bc96d..17e6016 100644
--- a/src/gl/SkGLCanvas.cpp
+++ b/src/gl/SkGLCanvas.cpp
@@ -1,180 +1,50 @@
+/*
+ * Copyright (C) 2010 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 "SkGLCanvas.h"
 #include "SkGLDevice.h"
-#include "SkBlitter.h"
-#include "SkDraw.h"
-#include "SkDrawProcs.h"
-#include "SkGL.h"
-#include "SkGlyphCache.h"
-#include "SkTemplates.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
 
-#ifdef SK_GL_DEVICE_FBO
-    #define USE_FBO_DEVICE
-    #include "SkGLDevice_FBO.h"
-#else
-    #define USE_SWLAYER_DEVICE
-    #include "SkGLDevice_SWLayer.h"
-#endif
+SkGLCanvas::SkGLCanvas() : SkCanvas(SkNEW(SkGLDeviceFactory)) {}
 
-// maximum number of entries in our texture cache (before purging)
-#define kTexCountMax_Default    256
-// maximum number of bytes used (by gl) for the texture cache (before purging)
-#define kTexSizeMax_Default     (4 * 1024 * 1024)
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkGLCanvas::SkGLCanvas() {
-    glEnable(GL_TEXTURE_2D);
-    glEnable(GL_SCISSOR_TEST);
-    glEnableClientState(GL_VERTEX_ARRAY);
-
-    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-
-    fViewportSize.set(0, 0);
-}
-
-SkGLCanvas::~SkGLCanvas() {
-    // call this now, while our override of restore() is in effect
-    this->restoreToCount(1);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool SkGLCanvas::getViewport(SkIPoint* size) const {
-    if (size) {
-        *size = fViewportSize;
-    }
-    return true;
-}
-
-bool SkGLCanvas::setViewport(int width, int height) {
-    fViewportSize.set(width, height);
-
-    const bool isOpaque = false; // should this be a parameter to setViewport?
-    const bool isForLayer = false;   // viewport is the base layer
-    SkDevice* device = this->createDevice(SkBitmap::kARGB_8888_Config, width,
-                                          height, isOpaque, isForLayer);
-    this->setDevice(device)->unref();
-
-    return true;
-}
-
-SkDevice* SkGLCanvas::createDevice(SkBitmap::Config, int width, int height,
-                                   bool isOpaque, bool isForLayer) {
-    SkBitmap bitmap;
-    
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    bitmap.setIsOpaque(isOpaque);
-
-#ifdef USE_FBO_DEVICE
-    return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer));
-#elif defined(USE_SWLAYER_DEVICE)
-    if (isForLayer) {
-        bitmap.allocPixels();
-        if (!bitmap.isOpaque()) {
-            bitmap.eraseColor(0);
-        }
-        return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap));
-    } else {
-        return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
-    }
-#else
-    return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkTextureCache.h"
-#include "SkThread.h"
-
-static SkMutex gTextureCacheMutex;
-static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
-
-SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
-                                                 GLuint* name, SkPoint* size) {
-    SkAutoMutexAcquire amc(gTextureCacheMutex);
-    
-    SkTextureCache::Entry* entry = gTextureCache.lock(bitmap);
-    if (NULL != entry) {
-        if (name) {
-            *name = entry->name();
-        }
-        if (size) {
-            *size = entry->texSize();
-        }
-    }
-    return (TexCache*)entry;
-}
-
-void SkGLDevice::UnlockTexCache(TexCache* cache) {
-    SkAutoMutexAcquire amc(gTextureCacheMutex);
-    gTextureCache.unlock((SkTextureCache::Entry*)cache);
-}
-
-// public exposure of texture cache settings
-
+// static
 size_t SkGLCanvas::GetTextureCacheMaxCount() {
-    SkAutoMutexAcquire amc(gTextureCacheMutex);
-    return gTextureCache.getMaxCount();
+    return SkGLDevice::GetTextureCacheMaxCount();
 }
 
-size_t SkGLCanvas::GetTextureCacheMaxSize() {
-    SkAutoMutexAcquire amc(gTextureCacheMutex);
-    return gTextureCache.getMaxSize();
-}
-
+// static
 void SkGLCanvas::SetTextureCacheMaxCount(size_t count) {
-    SkAutoMutexAcquire amc(gTextureCacheMutex);
-    gTextureCache.setMaxCount(count);
+    SkGLDevice::SetTextureCacheMaxCount(count);
 }
 
+// static
+size_t SkGLCanvas::GetTextureCacheMaxSize() {
+    return SkGLDevice::GetTextureCacheMaxSize();
+}
+
+// static
 void SkGLCanvas::SetTextureCacheMaxSize(size_t size) {
-    SkAutoMutexAcquire amc(gTextureCacheMutex);
-    gTextureCache.setMaxSize(size);
+    SkGLDevice::SetTextureCacheMaxSize(size);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkGLTextCache.h"
-
-static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) {
-    void* auxData;
-    if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
-        bool valid = texturesAreValid != NULL;
-        SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData);
-        // call this before delete, in case valid is false
-        textCache->deleteAllStrikes(valid);
-        // now free the memory for the cache itself
-        SkDELETE(textCache);
-        // now remove the entry in the glyphcache (does not call the proc)
-        cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc);
-    }
-    return false;   // keep going
-}
-
+// static
 void SkGLCanvas::DeleteAllTextures() {
-    // free the textures in our cache
-
-    gTextureCacheMutex.acquire();
-    gTextureCache.deleteAllCaches(true);
-    gTextureCacheMutex.release();
-    
-    // now free the textures in the font cache
-    
-    SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true));
+    SkGLDevice::DeleteAllTextures();
 }
 
+// static
 void SkGLCanvas::AbandonAllTextures() {
-    // abandon the textures in our cache
-
-    gTextureCacheMutex.acquire();
-    gTextureCache.deleteAllCaches(false);
-    gTextureCacheMutex.release();
-
-    // abandon the textures in the font cache
-    
-    SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false));
+    SkGLDevice::AbandonAllTextures();
 }
-
diff --git a/src/gl/SkGLDevice.cpp b/src/gl/SkGLDevice.cpp
index d839498..ad4377c 100644
--- a/src/gl/SkGLDevice.cpp
+++ b/src/gl/SkGLDevice.cpp
@@ -4,6 +4,19 @@
 #include "SkRegion.h"
 #include "SkThread.h"
 
+#ifdef SK_GL_DEVICE_FBO
+    #define USE_FBO_DEVICE
+    #include "SkGLDevice_FBO.h"
+#else
+    #define USE_SWLAYER_DEVICE
+    #include "SkGLDevice_SWLayer.h"
+#endif
+
+// maximum number of entries in our texture cache (before purging)
+#define kTexCountMax_Default    256
+// maximum number of bytes used (by gl) for the texture cache (before purging)
+#define kTexSizeMax_Default     (4 * 1024 * 1024)
+
 static void TRACE_DRAW(const char func[], SkGLDevice* device,
                        const SkDraw& draw) {
     //    SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice);
@@ -69,8 +82,39 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkDevice* SkGLDeviceFactory::newDevice(SkBitmap::Config config, int width,
+                                       int height, bool isOpaque,
+                                       bool isForLayer) {
+    SkBitmap bitmap;
+
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    bitmap.setIsOpaque(isOpaque);
+
+#ifdef USE_FBO_DEVICE
+    return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer));
+#elif defined(USE_SWLAYER_DEVICE)
+    if (isForLayer) {
+        bitmap.allocPixels();
+        if (!bitmap.isOpaque()) {
+            bitmap.eraseColor(0);
+        }
+        return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap));
+    } else {
+        return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
+    }
+#else
+    return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
+#endif
+}
+
 SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen)
         : SkDevice(bitmap), fClipIter(bitmap.height()) {
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_SCISSOR_TEST);
+    glEnableClientState(GL_VERTEX_ARRAY);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
     fDrawProcs = NULL;
 }
 
@@ -811,3 +855,99 @@
     SkGL_unimpl("drawTextOnPath");
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTextureCache.h"
+#include "SkThread.h"
+
+static SkMutex gTextureCacheMutex;
+static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
+
+SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
+                                                 GLuint* name, SkPoint* size) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+
+    SkTextureCache::Entry* entry = gTextureCache.lock(bitmap);
+    if (NULL != entry) {
+        if (name) {
+            *name = entry->name();
+        }
+        if (size) {
+            *size = entry->texSize();
+        }
+    }
+    return (TexCache*)entry;
+}
+
+void SkGLDevice::UnlockTexCache(TexCache* cache) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    gTextureCache.unlock((SkTextureCache::Entry*)cache);
+}
+
+// public exposure of texture cache settings
+
+size_t SkGLDevice::GetTextureCacheMaxCount() {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    return gTextureCache.getMaxCount();
+}
+
+size_t SkGLDevice::GetTextureCacheMaxSize() {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    return gTextureCache.getMaxSize();
+}
+
+void SkGLDevice::SetTextureCacheMaxCount(size_t count) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    gTextureCache.setMaxCount(count);
+}
+
+void SkGLDevice::SetTextureCacheMaxSize(size_t size) {
+    SkAutoMutexAcquire amc(gTextureCacheMutex);
+    gTextureCache.setMaxSize(size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGLTextCache.h"
+
+static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) {
+    void* auxData;
+    if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
+        bool valid = texturesAreValid != NULL;
+        SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData);
+        // call this before delete, in case valid is false
+        textCache->deleteAllStrikes(valid);
+        // now free the memory for the cache itself
+        SkDELETE(textCache);
+        // now remove the entry in the glyphcache (does not call the proc)
+        cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc);
+    }
+    return false;   // keep going
+}
+
+void SkGLDevice::DeleteAllTextures() {
+    // free the textures in our cache
+
+    gTextureCacheMutex.acquire();
+    gTextureCache.deleteAllCaches(true);
+    gTextureCacheMutex.release();
+
+    // now free the textures in the font cache
+
+    SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true)
+);
+}
+
+void SkGLDevice::AbandonAllTextures() {
+    // abandon the textures in our cache
+
+    gTextureCacheMutex.acquire();
+    gTextureCache.deleteAllCaches(false);
+    gTextureCacheMutex.release();
+
+    // abandon the textures in the font cache
+
+    SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false
+));
+}
+
diff --git a/src/gl/SkGLDevice.h b/src/gl/SkGLDevice.h
index 0fc9e47..a939b07 100644
--- a/src/gl/SkGLDevice.h
+++ b/src/gl/SkGLDevice.h
@@ -5,6 +5,18 @@
 #include "SkGL.h"
 #include "SkRegion.h"
 
+#ifdef SK_BUILD_FOR_MAC
+    #include <OpenGL/gl.h>
+#elif defined(ANDROID)
+    #include <GLES/gl.h>
+#endif
+
+class SkGLDeviceFactory : public SkDeviceFactory {
+public:
+    virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height,
+                                bool isOpaque, bool isForLayer);
+};
+
 struct SkGLDrawProcs;
 
 class SkGLDevice : public SkDevice {
@@ -12,6 +24,12 @@
     SkGLDevice(const SkBitmap& bitmap, bool offscreen);
     virtual ~SkGLDevice();
 
+    virtual SkDeviceFactory* getDeviceFactory() {
+        return SkNEW(SkGLDeviceFactory);
+    }
+
+    virtual uint32_t getDeviceCapabilities() { return kGL_Capability; }
+
     // used to identify GLTextCache data in the glyphcache
     static void GlyphCacheAuxProc(void* data);    
     
@@ -63,6 +81,27 @@
     virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
                             const SkPaint&);
 
+    // settings for the global texture cache
+
+    static size_t GetTextureCacheMaxCount();
+    static void SetTextureCacheMaxCount(size_t count);
+
+    static size_t GetTextureCacheMaxSize();
+    static void SetTextureCacheMaxSize(size_t size);
+
+    /** Call glDeleteTextures for all textures (including those for text)
+        This should be called while the gl-context is still valid. Its purpose
+        is to free up gl resources. Note that if a bitmap or text is drawn after
+        this call, new caches will be created.
+    */
+    static void DeleteAllTextures();
+
+    /** Forget all textures without calling delete (including those for text).
+        This should be called if the gl-context has changed, and the texture
+        IDs that have been cached are no longer valid.
+    */
+    static void AbandonAllTextures();
+
 protected:
     /** Return the current glmatrix, from a previous call to setMatrixClip */
     const SkMatrix& matrix() const { return fMatrix; }
diff --git a/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp
index 6f96b03..39e1a12 100644
--- a/src/images/SkFlipPixelRef.cpp
+++ b/src/images/SkFlipPixelRef.cpp
@@ -125,4 +125,3 @@
         iter.next();
     }
 }
-
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 7713e86..c26c7f3 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -153,7 +153,7 @@
 }
 
 bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
-                            SkBitmap::Config pref, Mode mode) {
+                            SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
     // pass a temporary bitmap, so that if we return false, we are assured of
     // leaving the caller's bitmap untouched.
     SkBitmap    tmp;
@@ -163,6 +163,12 @@
     // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
     fDefaultPref = pref;
 
+    if (reuseBitmap) {
+        SkAutoLockPixels alp(*bm);
+        if (bm->getPixels() != NULL) {
+            return this->onDecode(stream, bm, mode);
+        }
+    }
     if (!this->onDecode(stream, &tmp, mode)) {
         return false;
     }
diff --git a/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp
index bd81d1f..e7c71e5 100644
--- a/src/images/SkImageDecoder_Factory.cpp
+++ b/src/images/SkImageDecoder_Factory.cpp
@@ -24,10 +24,15 @@
 
 template DecodeReg* DecodeReg::gHead;
 
+#ifdef SK_ENABLE_LIBPNG
+    extern SkImageDecoder* sk_libpng_dfactory(SkStream*);
+#endif
+
 SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    SkImageDecoder* codec = NULL;
     const DecodeReg* curr = DecodeReg::Head();
     while (curr) {
-        SkImageDecoder* codec = curr->factory()(stream);
+        codec = curr->factory()(stream);
         // we rewind here, because we promise later when we call "decode", that
         // the stream will be at its beginning.
         stream->rewind();
@@ -36,6 +41,13 @@
         }
         curr = curr->next();
     }
+#ifdef SK_ENABLE_LIBPNG
+    codec = sk_libpng_dfactory(stream);
+    stream->rewind();
+    if (codec) {
+        return codec;
+    }
+#endif
     return NULL;
 }
 
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index ef5b7ac..52e9e4f 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -351,7 +351,7 @@
     int alphaBit = (alphaByte & m) >> shift;
     //alphaBit == 1 => alpha = 0
     int alpha = (alphaBit-1) & 0xFF;
-    *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha);    
+    *address = SkPreMultiplyARGB(alpha, red, green, blue);    
 }
 
 static void editPixelBit32(const int pixelNo, const unsigned char* buf, 
@@ -364,7 +364,7 @@
     int red = readByte(buf, xorOffset + 4*pixelNo + 2);
     int alphaBit = (alphaByte & m) >> shift;
     int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF);
-    *address = SkPackARGB32(alpha, red & alpha, green & alpha, blue & alpha);
+    *address = SkPreMultiplyARGB(alpha, red, green, blue);
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 7e425c1..514118d 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -325,16 +325,30 @@
         (config == SkBitmap::kRGB_565_Config && 
                 cinfo.out_color_space == JCS_RGB_565)))
     {
-        bm->setConfig(config, cinfo.output_width, cinfo.output_height);
-        bm->setIsOpaque(true);
-        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        bm->lockPixels();
+        JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+        bm->unlockPixels();
+        bool reuseBitmap = (rowptr != NULL);
+        if (reuseBitmap && ((int) cinfo.output_width != bm->width() ||
+                (int) cinfo.output_height != bm->height())) {
+            // Dimensions must match
+            return false;
+        }
+
+        if (!reuseBitmap) {
+            bm->setConfig(config, cinfo.output_width, cinfo.output_height);
+            bm->setIsOpaque(true);
+            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+                return true;
+            }
+            if (!this->allocPixelRef(bm, NULL)) {
+                return return_false(cinfo, *bm, "allocPixelRef");
+            }
+        } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
             return true;
         }
-        if (!this->allocPixelRef(bm, NULL)) {
-            return return_false(cinfo, *bm, "allocPixelRef");
-        }
         SkAutoLockPixels alp(*bm);
-        JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+        rowptr = (JSAMPLE*)bm->getPixels();
         INT32 const bpr =  bm->rowBytes();
         
         while (cinfo.output_scanline < cinfo.output_height) {
@@ -349,6 +363,9 @@
             }
             rowptr += bpr;
         }
+        if (reuseBitmap) {
+            bm->notifyPixelsChanged();
+        }
         jpeg_finish_decompress(&cinfo);
         return true;
     }
@@ -374,15 +391,29 @@
     SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
                                   sampleSize);
 
-    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
-    // jpegs are always opauqe (i.e. have no per-pixel alpha)
-    bm->setIsOpaque(true);
-
-    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+    bm->lockPixels();
+    JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
+    bool reuseBitmap = (rowptr != NULL);
+    bm->unlockPixels();
+    if (reuseBitmap && (sampler.scaledWidth() != bm->width() ||
+            sampler.scaledHeight() != bm->height())) {
+        // Dimensions must match
+        return false;
     }
-    if (!this->allocPixelRef(bm, NULL)) {
-        return return_false(cinfo, *bm, "allocPixelRef");
+
+    if (!reuseBitmap) {
+        bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+        // jpegs are always opaque (i.e. have no per-pixel alpha)
+        bm->setIsOpaque(true);
+
+        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+            return true;
+        }
+        if (!this->allocPixelRef(bm, NULL)) {
+            return return_false(cinfo, *bm, "allocPixelRef");
+        }
+    } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
     }
 
     SkAutoLockPixels alp(*bm);                          
@@ -424,6 +455,9 @@
                        cinfo.output_height - cinfo.output_scanline)) {
         return return_false(cinfo, *bm, "skip rows");
     }
+    if (reuseBitmap) {
+        bm->notifyPixelsChanged();
+    }
     jpeg_finish_decompress(&cinfo);
 
 //    SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index cd240b4..5cd86f7 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -291,8 +291,20 @@
     const int sampleSize = this->getSampleSize();
     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
 
-    decodedBitmap->setConfig(config, sampler.scaledWidth(),
-                             sampler.scaledHeight(), 0);
+    decodedBitmap->lockPixels();
+    void* rowptr = (void*) decodedBitmap->getPixels();
+    bool reuseBitmap = (rowptr != NULL);
+    decodedBitmap->unlockPixels();
+    if (reuseBitmap && (sampler.scaledWidth() != decodedBitmap->width() ||
+            sampler.scaledHeight() != decodedBitmap->height())) {
+        // Dimensions must match
+        return false;
+    }
+
+    if (!reuseBitmap) {
+        decodedBitmap->setConfig(config, sampler.scaledWidth(),
+                                 sampler.scaledHeight(), 0);
+    }
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         return true;
     }
@@ -312,10 +324,12 @@
 
     SkAutoUnref aur(colorTable);
 
-    if (!this->allocPixelRef(decodedBitmap,
-                             SkBitmap::kIndex8_Config == config ?
-                                colorTable : NULL)) {
-        return false;
+    if (!reuseBitmap) {
+        if (!this->allocPixelRef(decodedBitmap,
+                                 SkBitmap::kIndex8_Config == config ?
+                                    colorTable : NULL)) {
+            return false;
+        }
     }
 
     SkAutoLockPixels alp(*decodedBitmap);
@@ -416,6 +430,9 @@
         reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
     }
     decodedBitmap->setIsOpaque(!reallyHasAlpha);
+    if (reuseBitmap) {
+        decodedBitmap->notifyPixelsChanged();
+    }
     return true;
 }
 
@@ -684,7 +701,7 @@
     png_ptr->pass = 0;
     png_read_update_info(png_ptr, info_ptr);
 
-    SkDebugf("Request size %d %d\n", requestedWidth, requestedHeight);
+    // SkDebugf("Request size %d %d\n", requestedWidth, requestedHeight);
 
     int actualTop = rect.fTop;
 
@@ -1128,7 +1145,12 @@
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* DFactory(SkStream* stream) {
+#ifdef SK_ENABLE_LIBPNG
+    SkImageDecoder* sk_libpng_dfactory(SkStream*);
+    SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
+#endif
+
+SkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
     char buf[PNG_BYTES_TO_CHECK];
     if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
         !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
@@ -1137,9 +1159,9 @@
     return NULL;
 }
 
-static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
+SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
     return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
 }
 
-static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
-static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libpng_efactory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libpng_dfactory);
diff --git a/src/images/SkImageEncoder_Factory.cpp b/src/images/SkImageEncoder_Factory.cpp
index d673167..0bb4d1a 100644
--- a/src/images/SkImageEncoder_Factory.cpp
+++ b/src/images/SkImageEncoder_Factory.cpp
@@ -21,15 +21,24 @@
 
 template EncodeReg* EncodeReg::gHead;
 
+#ifdef SK_ENABLE_LIBPNG
+    extern SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type);
+#endif
+
 SkImageEncoder* SkImageEncoder::Create(Type t) {
+    SkImageEncoder* codec = NULL;
     const EncodeReg* curr = EncodeReg::Head();
     while (curr) {
-        SkImageEncoder* codec = curr->factory()(t);
-        if (codec) {
+        if ((codec = curr->factory()(t)) != NULL) {
             return codec;
         }
         curr = curr->next();
     }
+#ifdef SK_ENABLE_LIBPNG
+    if ((codec = sk_libpng_efactory(t)) != NULL) {
+        return codec;
+    }
+#endif
     return NULL;
 }
 
diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
index 33a3c46..1f44a84 100644
--- a/src/images/SkImageRef_GlobalPool.cpp
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -80,4 +80,3 @@
     SkAutoMutexAcquire ac(gImageRefMutex);
     gGlobalImageRefPool.dump();
 }
-
diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp
index a904bae..5dd3a59 100644
--- a/src/ports/SkImageRef_ashmem.cpp
+++ b/src/ports/SkImageRef_ashmem.cpp
@@ -1,5 +1,6 @@
 #include "SkImageRef_ashmem.h"
 #include "SkImageDecoder.h"
+#include "SkFlattenable.h"
 #include "SkThread.h"
 
 #include <sys/mman.h>
@@ -201,3 +202,36 @@
     fBitmap.setPixels(NULL, NULL);
 }
 
+void SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    const char* uri = getURI();
+    if (uri) {
+        size_t len = strlen(uri);
+        buffer.write32(len);
+        buffer.writePad(uri, len);
+    } else {
+        buffer.write32(0);
+    }
+}
+
+SkImageRef_ashmem::SkImageRef_ashmem(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fRec.fFD = -1;
+    fRec.fAddr = NULL;
+    fRec.fSize = 0;
+    fRec.fPinned = false;
+    fCT = NULL;
+    size_t length = buffer.readU32();
+    if (length) {
+        char* buf = (char*) malloc(length);
+        buffer.read(buf, length);
+        setURI(buf, length);
+    }
+}
+
+SkPixelRef* SkImageRef_ashmem::Create(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkImageRef_ashmem, (buffer));
+}
+
+static SkPixelRef::Registrar reg("SkImageRef_ashmem",
+                                 SkImageRef_ashmem::Create);
diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h
index 193a01d..2c485e3 100644
--- a/src/ports/SkImageRef_ashmem.h
+++ b/src/ports/SkImageRef_ashmem.h
@@ -15,6 +15,13 @@
     SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1);
     virtual ~SkImageRef_ashmem();
     
+    // overrides
+    virtual void flatten(SkFlattenableWriteBuffer&) const;
+    virtual Factory getFactory() const {
+        return Create;
+    }
+    static SkPixelRef* Create(SkFlattenableReadBuffer&);
+
 protected:
     virtual bool onDecode(SkImageDecoder* codec, SkStream* stream,
                           SkBitmap* bitmap, SkBitmap::Config config,
@@ -24,6 +31,7 @@
     virtual void onUnlockPixels();
     
 private:
+    SkImageRef_ashmem(SkFlattenableReadBuffer&);
     void closeFD();
 
     SkColorTable* fCT;
diff --git a/src/utils/SkCullPoints.cpp b/src/utils/SkCullPoints.cpp
index 23d00b6..03bfa99 100644
--- a/src/utils/SkCullPoints.cpp
+++ b/src/utils/SkCullPoints.cpp
@@ -18,8 +18,7 @@
 #include "SkCullPoints.h"
 #include "Sk64.h"
 
-static bool cross_product_is_neg(const SkIPoint& v, int dx, int dy)
-{
+static bool cross_product_is_neg(const SkIPoint& v, int dx, int dy) {
 #if 0
     return v.fX * dy - v.fY * dx < 0;
 #else
@@ -32,19 +31,20 @@
 #endif
 }
 
-bool SkCullPoints::sect_test(int x0, int y0, int x1, int y1) const
-{
+bool SkCullPoints::sect_test(int x0, int y0, int x1, int y1) const {
     const SkIRect& r = fR;
 
-    if (x0 < r.fLeft    && x1 < r.fLeft ||
-        x0 > r.fRight   && x1 > r.fRight ||
-        y0 < r.fTop     && y1 < r.fTop ||
-        y0 > r.fBottom  && y1 > r.fBottom)
+    if ((x0 < r.fLeft    && x1 < r.fLeft) ||
+        (x0 > r.fRight   && x1 > r.fRight) ||
+        (y0 < r.fTop     && y1 < r.fTop) ||
+        (y0 > r.fBottom  && y1 > r.fBottom)) {
         return false;
+    }
 
     // since the crossprod test is a little expensive, check for easy-in cases first    
-    if (r.contains(x0, y0) || r.contains(x1, y1))
+    if (r.contains(x0, y0) || r.contains(x1, y1)) {
         return true;
+    }
 
     // At this point we're not sure, so we do a crossprod test
     SkIPoint           vec;
@@ -53,16 +53,14 @@
     vec.set(x1 - x0, y1 - y0);
     bool isNeg = cross_product_is_neg(vec, x0 - rAsQuad[0].fX, y0 - rAsQuad[0].fY);
     for (int i = 1; i < 4; i++) {
-        if (cross_product_is_neg(vec, x0 - rAsQuad[i].fX, y0 - rAsQuad[i].fY) != isNeg)
-        {
+        if (cross_product_is_neg(vec, x0 - rAsQuad[i].fX, y0 - rAsQuad[i].fY) != isNeg) {
             return true;
         }
     }
     return false;   // we didn't intersect
 }
 
-static void toQuad(const SkIRect& r, SkIPoint quad[4])
-{
+static void toQuad(const SkIRect& r, SkIPoint quad[4]) {
     SkASSERT(quad);
 
     quad[0].set(r.fLeft, r.fTop);
@@ -71,34 +69,29 @@
     quad[3].set(r.fLeft, r.fBottom);
 }
 
-SkCullPoints::SkCullPoints()
-{
+SkCullPoints::SkCullPoints() {
     SkIRect    r;
     r.setEmpty();
     this->reset(r);
 }
 
-SkCullPoints::SkCullPoints(const SkIRect& r)
-{
+SkCullPoints::SkCullPoints(const SkIRect& r) {
     this->reset(r);
 }
 
-void SkCullPoints::reset(const SkIRect& r)
-{
+void SkCullPoints::reset(const SkIRect& r) {
     fR = r;
     toQuad(fR, fAsQuad);
     fPrevPt.set(0, 0);
     fPrevResult = kNo_Result;
 }
 
-void SkCullPoints::moveTo(int x, int y)
-{
+void SkCullPoints::moveTo(int x, int y) {
     fPrevPt.set(x, y);
     fPrevResult = kNo_Result;   // so we trigger a movetolineto later
 }
 
-SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkIPoint line[])
-{
+SkCullPoints::LineToResult SkCullPoints::lineTo(int x, int y, SkIPoint line[]) {
     SkASSERT(line != NULL);
 
     LineToResult result = kNo_Result;
@@ -108,15 +101,15 @@
     // need to upgrade sect_test to chop the result
     // and to correctly return kLineTo_Result when the result is connected
     // to the previous call-out
-    if (this->sect_test(x0, y0, x, y))
-    {
+    if (this->sect_test(x0, y0, x, y)) {
         line[0].set(x0, y0);
         line[1].set(x, y);
         
-        if (fPrevResult != kNo_Result && fPrevPt.equals(x0, y0))
+        if (fPrevResult != kNo_Result && fPrevPt.equals(x0, y0)) {
             result = kLineTo_Result;
-        else
+        } else {
             result = kMoveToLineTo_Result;
+        }
     }
 
     fPrevPt.set(x, y);
@@ -130,28 +123,23 @@
 #include "SkPath.h"
 
 SkCullPointsPath::SkCullPointsPath()
-    : fCP(), fPath(NULL)
-{
+    : fCP(), fPath(NULL) {
 }
 
 SkCullPointsPath::SkCullPointsPath(const SkIRect& r, SkPath* dst)
-    : fCP(r), fPath(dst)
-{
+    : fCP(r), fPath(dst) {
 }
 
-void SkCullPointsPath::reset(const SkIRect& r, SkPath* dst)
-{
+void SkCullPointsPath::reset(const SkIRect& r, SkPath* dst) {
     fCP.reset(r);
     fPath = dst;
 }
-    
-void SkCullPointsPath::moveTo(int x, int y)
-{
+
+void SkCullPointsPath::moveTo(int x, int y) {
     fCP.moveTo(x, y);
 }
 
-void SkCullPointsPath::lineTo(int x, int y)
-{
+void SkCullPointsPath::lineTo(int x, int y) {
     SkIPoint   pts[2];
     
     switch (fCP.lineTo(x, y, pts)) {
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index 737307c..4ff7a50 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -121,8 +121,7 @@
 }
 
 static void toString(const void* text, size_t len, SkPaint::TextEncoding enc,
-                     SkString* str, const SkPaint& paint,
-                     const SkScalar xpos[] = NULL) {
+                     SkString* str) {
     switch (enc) {
         case SkPaint::kUTF8_TextEncoding:
             str->printf("\"%.*s\"%s", SkMax32(len, 32), text,
@@ -132,21 +131,9 @@
             str->printf("\"%.*S\"%s", SkMax32(len, 32), text,
                         len > 64 ? "..." : "");
             break;
-        case SkPaint::kGlyphID_TextEncoding: {
-            const uint16_t* glyphs = (const uint16_t*)text;
-            const int max = 32;
-            SkUnichar uni[max];
-            int count = SkMin32(len >> 1, max);
-            paint.glyphsToUnichars(glyphs, count, uni);
-            str->append("\"");
-            for (int i = 0; i < count; i++) {
-                str->appendUnichar(uni[i]);
-            }
-            if ((size_t)count < (len >> 1)) {
-                str->append("...");
-            }
-            str->append("\"");
-        } break;
+        case SkPaint::kGlyphID_TextEncoding:
+            str->set("<glyphs>");
+            break;
     }
 }
 
@@ -331,7 +318,7 @@
 void SkDumpCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
                              SkScalar y, const SkPaint& paint) {
     SkString str;
-    toString(text, byteLength, paint.getTextEncoding(), &str, paint);
+    toString(text, byteLength, paint.getTextEncoding(), &str);
     this->dump(kDrawText_Verb, &paint, "drawText(%s [%d] %g %g)", str.c_str(),
                byteLength, SkScalarToFloat(x), SkScalarToFloat(y));
 }
@@ -339,7 +326,7 @@
 void SkDumpCanvas::drawPosText(const void* text, size_t byteLength,
                                 const SkPoint pos[], const SkPaint& paint) {
     SkString str;
-    toString(text, byteLength, paint.getTextEncoding(), &str, paint);
+    toString(text, byteLength, paint.getTextEncoding(), &str);
     this->dump(kDrawText_Verb, &paint, "drawPosText(%s [%d] %g %g ...)",
                str.c_str(), byteLength, SkScalarToFloat(pos[0].fX),
                SkScalarToFloat(pos[0].fY));
@@ -349,7 +336,7 @@
                                  const SkScalar xpos[], SkScalar constY,
                                  const SkPaint& paint) {
     SkString str;
-    toString(text, byteLength, paint.getTextEncoding(), &str, paint, xpos);
+    toString(text, byteLength, paint.getTextEncoding(), &str);
     this->dump(kDrawText_Verb, &paint, "drawPosTextH(%s [%d] %g %g ...)",
                str.c_str(), byteLength, SkScalarToFloat(xpos[0]),
                SkScalarToFloat(constY));
@@ -359,7 +346,7 @@
                                    const SkPath& path, const SkMatrix* matrix,
                                    const SkPaint& paint) {
     SkString str;
-    toString(text, byteLength, paint.getTextEncoding(), &str, paint);
+    toString(text, byteLength, paint.getTextEncoding(), &str);
     this->dump(kDrawText_Verb, &paint, "drawTextOnPath(%s [%d])",
                str.c_str(), byteLength);
 }
diff --git a/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
index 3d85edc..0b601eb 100644
--- a/src/utils/SkNinePatch.cpp
+++ b/src/utils/SkNinePatch.cpp
@@ -235,15 +235,27 @@
     const int32_t srcY[4] = {
         0, margins.fTop, bitmap.height() - margins.fBottom, bitmap.height()
     };
-    const SkScalar dstX[4] = {
+    SkScalar dstX[4] = {
         dst.fLeft, dst.fLeft + SkIntToScalar(margins.fLeft),
         dst.fRight - SkIntToScalar(margins.fRight), dst.fRight
     };
-    const SkScalar dstY[4] = {
+    SkScalar dstY[4] = {
         dst.fTop, dst.fTop + SkIntToScalar(margins.fTop),
         dst.fBottom - SkIntToScalar(margins.fBottom), dst.fBottom
     };
-    
+
+    if (dstX[1] > dstX[2]) {
+        dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * SkIntToScalar(margins.fLeft) /
+            (SkIntToScalar(margins.fLeft) + SkIntToScalar(margins.fRight));
+        dstX[2] = dstX[1];
+    }
+
+    if (dstY[1] > dstY[2]) {
+        dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * SkIntToScalar(margins.fTop) /
+            (SkIntToScalar(margins.fTop) + SkIntToScalar(margins.fBottom));
+        dstY[2] = dstY[1];
+    }
+
     SkIRect s;
     SkRect  d;
     for (int y = 0; y < 3; y++) {
@@ -278,6 +290,17 @@
         xDivs[1] = bitmap.width() - margins.fRight;
         yDivs[0] = margins.fTop;
         yDivs[1] = bitmap.height() - margins.fBottom;
+
+        if (xDivs[0] > xDivs[1]) {
+            xDivs[0] = bitmap.width() * margins.fLeft /
+                (margins.fLeft + margins.fRight);
+            xDivs[1] = xDivs[0];
+        }
+        if (yDivs[0] > yDivs[1]) {
+            yDivs[0] = bitmap.height() * margins.fTop /
+                (margins.fTop + margins.fBottom);
+            yDivs[1] = yDivs[0];
+        }
         
         SkNinePatch::DrawMesh(canvas, bounds, bitmap,
                               xDivs, 2, yDivs, 2, paint);
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
index dec93de..4bfbd94 100644
--- a/tests/MathTest.cpp
+++ b/tests/MathTest.cpp
@@ -1,4 +1,5 @@
 #include "Test.h"
+#include "SkFloatingPoint.h"
 #include "SkPoint.h"
 #include "SkRandom.h"