Reverting r12427

git-svn-id: http://skia.googlecode.com/svn/trunk/src@12428 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/animator/SkDrawBitmap.cpp b/animator/SkDrawBitmap.cpp
index 568401d..327e813 100644
--- a/animator/SkDrawBitmap.cpp
+++ b/animator/SkDrawBitmap.cpp
@@ -75,11 +75,10 @@
     const char* formatName;
     switch (format) {
         case 0: formatName = "none"; break;
-        case 1: formatName = "A1"; break;
-        case 2: formatName = "A8"; break;
-        case 3: formatName = "Index8"; break;
-        case 4: formatName = "RGB16"; break;
-        case 5: formatName = "RGB32"; break;
+        case 1: formatName = "A8"; break;
+        case 2: formatName = "Index8"; break;
+        case 3: formatName = "RGB16"; break;
+        case 4: formatName = "RGB32"; break;
     }
     SkDebugf("format=\"%s\" />\n", formatName);
 }
diff --git a/core/SkBitmap.cpp b/core/SkBitmap.cpp
index 429d092..9d4aa87 100644
--- a/core/SkBitmap.cpp
+++ b/core/SkBitmap.cpp
@@ -161,7 +161,6 @@
     int bpp;
     switch (config) {
         case kNo_Config:
-        case kA1_Config:
             bpp = 0;   // not applicable
             break;
         case kA8_Config:
@@ -194,11 +193,6 @@
     switch (c) {
         case kNo_Config:
             break;
-        case kA1_Config:
-            rowBytes.set(width);
-            rowBytes.add(7);
-            rowBytes.shiftRight(3);
-            break;
         case kA8_Config:
         case kIndex8_Config:
             rowBytes.set(width);
@@ -275,7 +269,6 @@
         case SkBitmap::kNo_Config:
             alphaType = kIgnore_SkAlphaType;
             break;
-        case SkBitmap::kA1_Config:
         case SkBitmap::kA8_Config:
             if (kUnpremul_SkAlphaType == alphaType) {
                 alphaType = kPremul_SkAlphaType;
@@ -291,6 +284,8 @@
         case SkBitmap::kRGB_565_Config:
             alphaType = kOpaque_SkAlphaType;
             break;
+        default:
+            return false;
     }
     if (canonical) {
         *canonical = alphaType;
@@ -606,8 +601,6 @@
             case SkBitmap::kIndex8_Config:
                 base += x;
                 break;
-            case SkBitmap::kA1_Config:
-                base += x >> 3;
                 break;
             default:
                 SkDEBUGFAIL("Can't return addr for config");
@@ -623,15 +616,6 @@
     SkASSERT((unsigned)y < (unsigned)this->height());
 
     switch (this->config()) {
-        case SkBitmap::kA1_Config: {
-            uint8_t* addr = this->getAddr1(x, y);
-            uint8_t mask = 1 << (7  - (x % 8));
-            if (addr[0] & mask) {
-                return SK_ColorBLACK;
-            } else {
-                return 0;
-            }
-        }
         case SkBitmap::kA8_Config: {
             uint8_t* addr = this->getAddr8(x, y);
             return SkColorSetA(0, addr[0]);
@@ -654,6 +638,7 @@
             return SkUnPreMultiply::PMColorToColor(addr[0]);
         }
         case kNo_Config:
+        default:
             SkASSERT(false);
             return 0;
     }
@@ -671,9 +656,6 @@
     const int width = bm.width();
 
     switch (bm.config()) {
-        case SkBitmap::kA1_Config: {
-            // TODO
-        } break;
         case SkBitmap::kA8_Config: {
             unsigned a = 0xFF;
             for (int y = 0; y < height; ++y) {
@@ -779,38 +761,6 @@
     }
 
     switch (fConfig) {
-        case kA1_Config: {
-            uint8_t* p = this->getAddr1(area.fLeft, area.fTop);
-            const int left = area.fLeft >> 3;
-            const int right = area.fRight >> 3;
-
-            int middle = right - left - 1;
-
-            uint8_t leftMask = 0xFF >> (area.fLeft & 7);
-            uint8_t rightMask = ~(0xFF >> (area.fRight & 7));
-            if (left == right) {
-                leftMask &= rightMask;
-                rightMask = 0;
-            }
-
-            a = (a >> 7) ? 0xFF : 0;
-            while (--height >= 0) {
-                uint8_t* startP = p;
-
-                *p = (*p & ~leftMask) | (a & leftMask);
-                p++;
-                if (middle > 0) {
-                    memset(p, a, middle);
-                    p += middle;
-                }
-                if (rightMask) {
-                    *p = (*p & ~rightMask) | (a & rightMask);
-                }
-
-                p = startP + rowBytes;
-            }
-            break;
-        }
         case kA8_Config: {
             uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
             while (--height >= 0) {
@@ -896,7 +846,6 @@
             break;
 
         case SkBitmap::kNo_Config:
-        case SkBitmap::kA1_Config:
         default:
             return SUB_OFFSET_FAILURE;
     }
@@ -939,8 +888,6 @@
 
         case SkBitmap::kNo_Config:
             // Fall through.
-        case SkBitmap::kA1_Config:
-            // Fall through.
         default:
             return false;
     }
@@ -1021,7 +968,6 @@
         case kRGB_565_Config:
         case kARGB_8888_Config:
             break;
-        case kA1_Config:
         case kIndex8_Config:
             if (!sameConfigs) {
                 return false;
@@ -1032,12 +978,6 @@
         default:
             return false;
     }
-
-    // do not copy src if srcConfig == kA1_Config while dstConfig != kA1_Config
-    if (this->config() == kA1_Config && !sameConfigs) {
-        return false;
-    }
-
     return true;
 }
 
@@ -1683,7 +1623,7 @@
 void SkBitmap::toString(SkString* str) const {
 
     static const char* gConfigNames[kConfigCount] = {
-        "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888"
+        "NONE", "A8", "INDEX8", "565", "4444", "8888"
     };
 
     str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
diff --git a/core/SkBlitter.cpp b/core/SkBlitter.cpp
index dc7946a..9682d55 100644
--- a/core/SkBlitter.cpp
+++ b/core/SkBlitter.cpp
@@ -945,11 +945,6 @@
 
 
     switch (device.config()) {
-        case SkBitmap::kA1_Config:
-            SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter,
-                                  storage, storageSize, (device, *paint));
-            break;
-
         case SkBitmap::kA8_Config:
             if (drawCoverage) {
                 SkASSERT(NULL == shader);
diff --git a/core/SkBlitter_A1.cpp b/core/SkBlitter_A1.cpp
deleted file mode 100644
index b64afe2..0000000
--- a/core/SkBlitter_A1.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkCoreBlitters.h"
-
-SkA1_Blitter::SkA1_Blitter(const SkBitmap& device, const SkPaint& paint)
-        : INHERITED(device) {
-    fSrcA = paint.getAlpha();
-}
-
-void SkA1_Blitter::blitH(int x, int y, int width) {
-    SkASSERT(x >= 0 && y >= 0 &&
-             (unsigned)(x + width) <= (unsigned)fDevice.width());
-
-    if (fSrcA <= 0x7F) {
-        return;
-    }
-    uint8_t* dst = fDevice.getAddr1(x, y);
-    int right = x + width;
-
-    int left_mask = 0xFF >> (x & 7);
-    int rite_mask = 0xFF << (8 - (right & 7));
-    int full_runs = (right >> 3) - ((x + 7) >> 3);
-
-    // check for empty right mask, so we don't read off the end
-    // (or go slower than we need to)
-    if (rite_mask == 0) {
-        SkASSERT(full_runs >= 0);
-        full_runs -= 1;
-        rite_mask = 0xFF;
-    }
-    if (left_mask == 0xFF) {
-        full_runs -= 1;
-    }
-    if (full_runs < 0) {
-        SkASSERT((left_mask & rite_mask) != 0);
-        *dst |= (left_mask & rite_mask);
-    } else {
-        *dst++ |= left_mask;
-        memset(dst, 0xFF, full_runs);
-        dst += full_runs;
-        *dst |= rite_mask;
-    }
-}
diff --git a/core/SkCoreBlitters.h b/core/SkCoreBlitters.h
index 673b874..1605a52 100644
--- a/core/SkCoreBlitters.h
+++ b/core/SkCoreBlitters.h
@@ -162,22 +162,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkA1_Blitter : public SkRasterBlitter {
-public:
-    SkA1_Blitter(const SkBitmap& device, const SkPaint& paint);
-    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
-
-private:
-    uint8_t fSrcA;
-
-    // illegal
-    SkA1_Blitter& operator=(const SkA1_Blitter&);
-
-    typedef SkRasterBlitter INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
 /*  These return the correct subclass of blitter for their device config.
 
     Currently, they make the following assumptions about the state of the
diff --git a/core/SkGlyphCache.cpp b/core/SkGlyphCache.cpp
index faa3f89..a78b197 100644
--- a/core/SkGlyphCache.cpp
+++ b/core/SkGlyphCache.cpp
@@ -87,8 +87,6 @@
 
     fGlyphArray.setReserve(kMinGlyphCount);
 
-    fMetricsCount = 0;
-    fAdvanceCount = 0;
     fAuxProcList = NULL;
 }
 
@@ -320,11 +318,9 @@
 
     if (kJustAdvance_MetricsType == mtype) {
         fScalerContext->getAdvance(glyph);
-        fAdvanceCount += 1;
     } else {
         SkASSERT(kFull_MetricsType == mtype);
         fScalerContext->getMetrics(glyph);
-        fMetricsCount += 1;
     }
 
     return glyph;
diff --git a/core/SkGlyphCache.h b/core/SkGlyphCache.h
index 52a8132..7b2ebb8 100644
--- a/core/SkGlyphCache.h
+++ b/core/SkGlyphCache.h
@@ -211,8 +211,6 @@
     SkTDArray<SkGlyph*> fGlyphArray;
     SkChunkAlloc        fGlyphAlloc;
 
-    int fMetricsCount, fAdvanceCount;
-
     struct CharGlyphRec {
         uint32_t    fID;    // unichar + subpixel
         SkGlyph*    fGlyph;
diff --git a/core/SkPaint.cpp b/core/SkPaint.cpp
index d6948dd..91a76e1 100644
--- a/core/SkPaint.cpp
+++ b/core/SkPaint.cpp
@@ -2005,11 +2005,7 @@
 };
 
 // The size of a flat paint's POD fields
-// Include an SkScalar for hinting scale factor whether it is
-// supported or not so that an SKP is valid whether it was
-// created with support or not.
-
-static const uint32_t kPODPaintSize =   6 * sizeof(SkScalar) +
+static const uint32_t kPODPaintSize =   5 * sizeof(SkScalar) +
                                         1 * sizeof(SkColor) +
                                         1 * sizeof(uint16_t) +
                                         6 * sizeof(uint8_t);
@@ -2046,8 +2042,6 @@
         ptr = write_scalar(ptr, this->getTextSize());
         ptr = write_scalar(ptr, this->getTextScaleX());
         ptr = write_scalar(ptr, this->getTextSkewX());
-        // Dummy value for obsolete hinting scale factor.  TODO: remove with next picture version
-        ptr = write_scalar(ptr, SK_Scalar1);
         ptr = write_scalar(ptr, this->getStrokeWidth());
         ptr = write_scalar(ptr, this->getStrokeMiter());
         *ptr++ = this->getColor();
@@ -2064,8 +2058,6 @@
         buffer.writeScalar(fTextSize);
         buffer.writeScalar(fTextScaleX);
         buffer.writeScalar(fTextSkewX);
-        // Dummy value for obsolete hinting scale factor.  TODO: remove with next picture version
-        buffer.writeScalar(SK_Scalar1);
         buffer.writeScalar(fWidth);
         buffer.writeScalar(fMiterLimit);
         buffer.writeColor(fColor);
@@ -2120,8 +2112,6 @@
         this->setTextSize(read_scalar(pod));
         this->setTextScaleX(read_scalar(pod));
         this->setTextSkewX(read_scalar(pod));
-        // Skip the hinting scalar factor, which is not supported.
-        read_scalar(pod);
         this->setStrokeWidth(read_scalar(pod));
         this->setStrokeMiter(read_scalar(pod));
         this->setColor(*pod++);
diff --git a/core/SkPicture.cpp b/core/SkPicture.cpp
index 4acc549..2520e6b 100644
--- a/core/SkPicture.cpp
+++ b/core/SkPicture.cpp
@@ -266,11 +266,20 @@
 
 #include "SkStream.h"
 
+static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
+
 bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
     if (NULL == stream) {
         return false;
     }
 
+    // Check magic bytes.
+    char magic[sizeof(kMagic)];
+    stream->read(magic, sizeof(kMagic));
+    if (0 != memcmp(magic, kMagic, sizeof(kMagic))) {
+        return false;
+    }
+
     SkPictInfo info;
     if (!stream->read(&info, sizeof(SkPictInfo))) {
         return false;
@@ -341,6 +350,10 @@
         info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
     }
 
+    // Write 8 magic bytes to ID this file format.
+    SkASSERT(sizeof(kMagic) == 8);
+    stream->write(kMagic, sizeof(kMagic));
+
     stream->write(&info, sizeof(info));
     if (playback) {
         stream->writeBool(true);
diff --git a/core/SkScaledImageCache.cpp b/core/SkScaledImageCache.cpp
index ea29843..55eadb8 100644
--- a/core/SkScaledImageCache.cpp
+++ b/core/SkScaledImageCache.cpp
@@ -49,7 +49,7 @@
     return hash;
 }
 
-struct Key {
+struct SkScaledImageCache::Key {
     Key(uint32_t genID,
         SkScalar scaleX,
         SkScalar scaleY,
@@ -129,22 +129,24 @@
 #include "SkTDynamicHash.h"
 
 namespace { // can't use static functions w/ template parameters
-const Key& key_from_rec(const SkScaledImageCache::Rec& rec) {
+const SkScaledImageCache::Key& key_from_rec(const SkScaledImageCache::Rec& rec) {
     return rec.fKey;
 }
 
-uint32_t hash_from_key(const Key& key) {
+uint32_t hash_from_key(const SkScaledImageCache::Key& key) {
     return key.fHash;
 }
 
-bool eq_rec_key(const SkScaledImageCache::Rec& rec, const Key& key) {
+bool eq_rec_key(const SkScaledImageCache::Rec& rec, const SkScaledImageCache::Key& key) {
     return rec.fKey == key;
 }
 }
 
 class SkScaledImageCache::Hash : public SkTDynamicHash<SkScaledImageCache::Rec,
-                                   Key, key_from_rec, hash_from_key,
-                                   eq_rec_key> {};
+                                                       SkScaledImageCache::Key,
+                                                       key_from_rec,
+                                                       hash_from_key,
+                                                       eq_rec_key> {};
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -187,17 +189,22 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-/**
-   This private method is the fully general record finder. All other
-   record finders should call this funtion. */
+
 SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(uint32_t genID,
                                                         SkScalar scaleX,
                                                         SkScalar scaleY,
                                                         const SkIRect& bounds) {
-    if (bounds.isEmpty()) {
+    const Key key(genID, scaleX, scaleY, bounds);
+    return this->findAndLock(key);
+}
+
+/**
+   This private method is the fully general record finder. All other
+   record finders should call this function or the one above. */
+SkScaledImageCache::Rec* SkScaledImageCache::findAndLock(const SkScaledImageCache::Key& key) {
+    if (key.fBounds.isEmpty()) {
         return NULL;
     }
-    Key key(genID, scaleX, scaleY, bounds);
 #ifdef USE_HASH
     Rec* rec = fHash->find(key);
 #else
@@ -275,8 +282,14 @@
 /**
    This private method is the fully general record adder. All other
    record adders should call this funtion. */
-void SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
+SkScaledImageCache::ID* SkScaledImageCache::addAndLock(SkScaledImageCache::Rec* rec) {
     SkASSERT(rec);
+    // See if we already have this key (racy inserts, etc.)
+    Rec* existing = this->findAndLock(rec->fKey);
+    if (existing != NULL) {
+        return rec_to_id(existing);
+    }
+
     this->addToHead(rec);
     SkASSERT(1 == rec->fLockCount);
 #ifdef USE_HASH
@@ -285,6 +298,7 @@
 #endif
     // We may (now) be overbudget, so see if we need to purge something.
     this->purgeAsNeeded();
+    return rec_to_id(rec);
 }
 
 SkScaledImageCache::ID* SkScaledImageCache::addAndLock(uint32_t genID,
@@ -293,8 +307,7 @@
                                                        const SkBitmap& bitmap) {
     Key key(genID, SK_Scalar1, SK_Scalar1, SkIRect::MakeWH(width, height));
     Rec* rec = SkNEW_ARGS(Rec, (key, bitmap));
-    this->addAndLock(rec);
-    return rec_to_id(rec);
+    return this->addAndLock(rec);
 }
 
 SkScaledImageCache::ID* SkScaledImageCache::addAndLock(const SkBitmap& orig,
@@ -311,8 +324,7 @@
     }
     Key key(orig.getGenerationID(), scaleX, scaleY, bounds);
     Rec* rec = SkNEW_ARGS(Rec, (key, scaled));
-    this->addAndLock(rec);
-    return rec_to_id(rec);
+    return this->addAndLock(rec);
 }
 
 SkScaledImageCache::ID* SkScaledImageCache::addAndLockMip(const SkBitmap& orig,
@@ -323,8 +335,7 @@
     }
     Key key(orig.getGenerationID(), 0, 0, bounds);
     Rec* rec = SkNEW_ARGS(Rec, (key, mip));
-    this->addAndLock(rec);
-    return rec_to_id(rec);
+    return this->addAndLock(rec);
 }
 
 void SkScaledImageCache::unlock(SkScaledImageCache::ID* id) {
diff --git a/core/SkScaledImageCache.h b/core/SkScaledImageCache.h
index fee69d2..44ef1f8 100644
--- a/core/SkScaledImageCache.h
+++ b/core/SkScaledImageCache.h
@@ -126,6 +126,7 @@
 
 public:
     struct Rec;
+    struct Key;
 private:
     Rec*    fHead;
     Rec*    fTail;
@@ -139,7 +140,8 @@
 
     Rec* findAndLock(uint32_t generationID, SkScalar sx, SkScalar sy,
                      const SkIRect& bounds);
-    void addAndLock(Rec* rec);
+    Rec* findAndLock(const Key& key);
+    ID* addAndLock(Rec* rec);
 
     void purgeAsNeeded();
 
diff --git a/core/SkScalerContext.cpp b/core/SkScalerContext.cpp
index 1d6c2f7..4996a73 100644
--- a/core/SkScalerContext.cpp
+++ b/core/SkScalerContext.cpp
@@ -352,16 +352,16 @@
             glyph->fHeight  = SkToU16(ir.height());
 
             if (glyph->fWidth > 0) {
-            switch (fRec.fMaskFormat) {
-            case SkMask::kLCD16_Format:
-            case SkMask::kLCD32_Format:
-                glyph->fWidth += 2;
-                glyph->fLeft -= 1;
-                break;
-            default:
-                break;
+                switch (fRec.fMaskFormat) {
+                case SkMask::kLCD16_Format:
+                case SkMask::kLCD32_Format:
+                    glyph->fWidth += 2;
+                    glyph->fLeft -= 1;
+                    break;
+                default:
+                    break;
+                }
             }
-    }
         }
     }
 
@@ -523,10 +523,54 @@
     }
 }
 
+static inline int convert_8_to_1(unsigned byte) {
+    SkASSERT(byte <= 0xFF);
+    return byte >> 7;
+}
+
+static uint8_t pack_8_to_1(const uint8_t alpha[8]) {
+    unsigned bits = 0;
+    for (int i = 0; i < 8; ++i) {
+        bits <<= 1;
+        bits |= convert_8_to_1(alpha[i]);
+    }
+    return SkToU8(bits);
+}
+
+static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) {
+    const int height = mask.fBounds.height();
+    const int width = mask.fBounds.width();
+    const int octs = width >> 3;
+    const int leftOverBits = width & 7;
+
+    uint8_t* dst = mask.fImage;
+    const int dstPad = mask.fRowBytes - SkAlign8(width)/8;
+    SkASSERT(dstPad >= 0);
+
+    const int srcPad = srcRB - width;
+    SkASSERT(srcPad >= 0);
+
+    for (int y = 0; y < height; ++y) {
+        for (int i = 0; i < octs; ++i) {
+            *dst++ = pack_8_to_1(src);
+            src += 8;
+        }
+        if (leftOverBits > 0) {
+            unsigned bits = 0;
+            int shift = 7;
+            for (int i = 0; i < leftOverBits; ++i, --shift) {
+                bits |= convert_8_to_1(*src++ >> 7) << shift;
+            }
+            *dst++ = bits;
+        }
+        src += srcPad;
+        dst += dstPad;
+    }
+}
+
 static void generateMask(const SkMask& mask, const SkPath& path,
                          const SkMaskGamma::PreBlend& maskPreBlend) {
-    SkBitmap::Config config;
-    SkPaint     paint;
+    SkPaint paint;
 
     int srcW = mask.fBounds.width();
     int srcH = mask.fBounds.height();
@@ -538,27 +582,25 @@
     matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
                         -SkIntToScalar(mask.fBounds.fTop));
 
-    if (SkMask::kBW_Format == mask.fFormat) {
-        config = SkBitmap::kA1_Config;
-        paint.setAntiAlias(false);
-    } else {
-        config = SkBitmap::kA8_Config;
-        paint.setAntiAlias(true);
-        switch (mask.fFormat) {
-            case SkMask::kA8_Format:
-                break;
-            case SkMask::kLCD16_Format:
-            case SkMask::kLCD32_Format:
-                // TODO: trigger off LCD orientation
-                dstW = 4*dstW - 8;
-                matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1),
-                                    -SkIntToScalar(mask.fBounds.fTop));
-                matrix.postScale(SkIntToScalar(4), SK_Scalar1);
-                dstRB = 0;  // signals we need a copy
-                break;
-            default:
-                SkDEBUGFAIL("unexpected mask format");
-        }
+    SkBitmap::Config config = SkBitmap::kA8_Config;
+    paint.setAntiAlias(SkMask::kBW_Format != mask.fFormat);
+    switch (mask.fFormat) {
+        case SkMask::kBW_Format:
+            dstRB = 0;  // signals we need a copy
+            break;
+        case SkMask::kA8_Format:
+            break;
+        case SkMask::kLCD16_Format:
+        case SkMask::kLCD32_Format:
+            // TODO: trigger off LCD orientation
+            dstW = 4*dstW - 8;
+            matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1),
+                                -SkIntToScalar(mask.fBounds.fTop));
+            matrix.postScale(SkIntToScalar(4), SK_Scalar1);
+            dstRB = 0;  // signals we need a copy
+            break;
+        default:
+            SkDEBUGFAIL("unexpected mask format");
     }
 
     SkRasterClip clip;
@@ -587,6 +629,9 @@
     draw.drawPath(path, paint);
 
     switch (mask.fFormat) {
+        case SkMask::kBW_Format:
+            packA8ToA1(mask, bm.getAddr8(0, 0), bm.rowBytes());
+            break;
         case SkMask::kA8_Format:
             if (maskPreBlend.isApplicable()) {
                 applyLUTToA8Mask(mask, maskPreBlend.fG);
diff --git a/core/SkScalerContext.h b/core/SkScalerContext.h
index 2820b5a..e4950ed 100644
--- a/core/SkScalerContext.h
+++ b/core/SkScalerContext.h
@@ -202,16 +202,57 @@
     Rec         fRec;
     unsigned    fBaseGlyphCount;
 
-    virtual unsigned generateGlyphCount() = 0;
-    virtual uint16_t generateCharToGlyph(SkUnichar) = 0;
-    virtual void generateAdvance(SkGlyph*) = 0;
-    virtual void generateMetrics(SkGlyph*) = 0;
-    virtual void generateImage(const SkGlyph&) = 0;
-    virtual void generatePath(const SkGlyph&, SkPath*) = 0;
+    /** Generates the contents of glyph.fAdvanceX and glyph.fAdvanceY.
+     *  May call getMetrics if that would be just as fast.
+     */
+    virtual void generateAdvance(SkGlyph* glyph) = 0;
+
+    /** Generates the contents of glyph.fWidth, fHeight, fTop, fLeft,
+     *  as well as fAdvanceX and fAdvanceY if not already set.
+     *
+     *  TODO: fMaskFormat is set by getMetrics later; cannot be set here.
+     */
+    virtual void generateMetrics(SkGlyph* glyph) = 0;
+
+    /** Generates the contents of glyph.fImage.
+     *  When called, glyph.fImage will be pointing to a pre-allocated,
+     *  uninitialized region of memory of size glyph.computeImageSize().
+     *  This method may change glyph.fMaskFormat if the new image size is
+     *  less than or equal to the old image size.
+     *
+     *  Because glyph.computeImageSize() will determine the size of fImage,
+     *  generateMetrics will be called before generateImage.
+     */
+    virtual void generateImage(const SkGlyph& glyph) = 0;
+
+    /** Sets the passed path to the glyph outline.
+     *  If this cannot be done the path is set to empty;
+     *  this is indistinguishable from a glyph with an empty path.
+     *  This does not set glyph.fPath.
+     *
+     *  TODO: path is always glyph.fPath, no reason to pass separately.
+     */
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) = 0;
+
+    /** Retrieves font metrics.
+     *  TODO: there is now a vertical bit, no need for two parameters.
+     */
     virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
                                      SkPaint::FontMetrics* mY) = 0;
-    // default impl returns 0, indicating failure.
-    virtual SkUnichar generateGlyphToChar(uint16_t);
+
+    /** Returns the number of glyphs in the font. */
+    virtual unsigned generateGlyphCount() = 0;
+
+    /** Returns the glyph id for the given unichar.
+     *  If there is no 1:1 mapping from the unichar to a glyph id, returns 0.
+     */
+    virtual uint16_t generateCharToGlyph(SkUnichar unichar) = 0;
+
+    /** Returns the unichar for the given glyph id.
+     *  If there is no 1:1 mapping from the glyph id to a unichar, returns 0.
+     *  The default implementation always returns 0, indicating failure.
+     */
+    virtual SkUnichar generateGlyphToChar(uint16_t glyphId);
 
     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
 
diff --git a/core/SkString.cpp b/core/SkString.cpp
index e30b89f..643dfb1 100644
--- a/core/SkString.cpp
+++ b/core/SkString.cpp
@@ -634,5 +634,17 @@
     return formattedOutput;
 }
 
+void SkStrSplit(const char* str, const char* delimiters, SkTArray<SkString>* out) {
+    const char* end = str + strlen(str);
+    while (str != end) {
+        // Find a token.
+        const size_t len = strcspn(str, delimiters);
+        out->push_back().set(str, len);
+        str += len;
+        // Skip any delimiters.
+        str += strspn(str, delimiters);
+    }
+}
+
 #undef VSNPRINTF
 #undef SNPRINTF
diff --git a/core/SkValidatingReadBuffer.cpp b/core/SkValidatingReadBuffer.cpp
index a92c1b9..4a8ac47 100644
--- a/core/SkValidatingReadBuffer.cpp
+++ b/core/SkValidatingReadBuffer.cpp
@@ -203,7 +203,7 @@
 uint32_t SkValidatingReadBuffer::getArrayCount() {
     const size_t inc = sizeof(uint32_t);
     fError = fError || !IsPtrAlign4(fReader.peek()) || !fReader.isAvailable(inc);
-    return *(uint32_t*)fReader.peek();
+    return fError ? 0 : *(uint32_t*)fReader.peek();
 }
 
 void SkValidatingReadBuffer::readBitmap(SkBitmap* bitmap) {
diff --git a/effects/SkDropShadowImageFilter.cpp b/effects/SkDropShadowImageFilter.cpp
index b4d8689..5be633e 100644
--- a/effects/SkDropShadowImageFilter.cpp
+++ b/effects/SkDropShadowImageFilter.cpp
@@ -15,10 +15,21 @@
 #include "SkFlattenableBuffers.h"
 
 SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigma, SkColor color, SkImageFilter* input)
-    : SkImageFilter(input)
+    : INHERITED(input)
     , fDx(dx)
     , fDy(dy)
-    , fSigma(sigma)
+    , fSigmaX(sigma)
+    , fSigmaY(sigma)
+    , fColor(color)
+{
+}
+
+SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor color, SkImageFilter* input, const CropRect* cropRect)
+    : INHERITED(input, cropRect)
+    , fDx(dx)
+    , fDy(dy)
+    , fSigmaX(sigmaX)
+    , fSigmaY(sigmaY)
     , fColor(color)
 {
 }
@@ -27,11 +38,13 @@
  : INHERITED(1, buffer) {
     fDx = buffer.readScalar();
     fDy = buffer.readScalar();
-    fSigma = buffer.readScalar();
+    fSigmaX = buffer.readScalar();
+    fSigmaY = buffer.readScalar();
     fColor = buffer.readColor();
     buffer.validate(SkScalarIsFinite(fDx) &&
                     SkScalarIsFinite(fDy) &&
-                    SkScalarIsFinite(fSigma));
+                    SkScalarIsFinite(fSigmaX) &&
+                    SkScalarIsFinite(fSigmaY));
 }
 
 void SkDropShadowImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const
@@ -39,7 +52,8 @@
     this->INHERITED::flatten(buffer);
     buffer.writeScalar(fDx);
     buffer.writeScalar(fDy);
-    buffer.writeScalar(fSigma);
+    buffer.writeScalar(fSigmaX);
+    buffer.writeScalar(fSigmaY);
     buffer.writeColor(fColor);
 }
 
@@ -49,17 +63,26 @@
     if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc))
         return false;
 
-    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(src.width(), src.height()));
+    SkIRect bounds;
+    src.getBounds(&bounds);
+    if (!this->applyCropRect(&bounds, matrix)) {
+        return false;
+    }
+
+    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
     SkCanvas canvas(device.get());
 
-    SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(fSigma, fSigma));
+    SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(fSigmaX, fSigmaY));
     SkAutoTUnref<SkColorFilter> colorFilter(SkColorFilter::CreateModeFilter(fColor, SkXfermode::kSrcIn_Mode));
     SkPaint paint;
     paint.setImageFilter(blurFilter.get());
     paint.setColorFilter(colorFilter.get());
     paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+    canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
     canvas.drawBitmap(src, fDx, fDy, &paint);
     canvas.drawBitmap(src, 0, 0);
     *result = device->accessBitmap(false);
+    loc->fX += bounds.fLeft;
+    loc->fY += bounds.fTop;
     return true;
 }
diff --git a/effects/SkTransparentShader.cpp b/effects/SkTransparentShader.cpp
index 970e74f..1d7e808 100644
--- a/effects/SkTransparentShader.cpp
+++ b/effects/SkTransparentShader.cpp
@@ -94,9 +94,6 @@
             }
             break;
         }
-        case SkBitmap::kA1_Config:
-            SkDEBUGFAIL("kA1_Config umimplemented at this time");
-            break;
         default:    // to avoid warnings
             break;
     }
diff --git a/fonts/SkGScalerContext.cpp b/fonts/SkGScalerContext.cpp
index f0543c5..551b01c 100644
--- a/fonts/SkGScalerContext.cpp
+++ b/fonts/SkGScalerContext.cpp
@@ -176,6 +176,8 @@
 
 void SkGTypeface::onFilterRec(SkScalerContextRec* rec) const {
     fProxy->filterRec(rec);
+    rec->setHinting(SkPaint::kNo_Hinting);
+    rec->fMaskFormat = SkMask::kARGB32_Format;
 }
 
 SkAdvancedTypefaceMetrics* SkGTypeface::onGetAdvancedTypefaceMetrics(
diff --git a/gpu/GrBinHashKey.h b/gpu/GrBinHashKey.h
index 7d4aa0f..585a1a1 100644
--- a/gpu/GrBinHashKey.h
+++ b/gpu/GrBinHashKey.h
@@ -13,37 +13,19 @@
 #include "GrTypes.h"
 
 /**
- *  Hash function class that can take a data chunk of any predetermined length. The hash function
- *  used is the One-at-a-Time Hash (http://burtleburtle.net/bob/hash/doobs.html).
- *
- *  Keys are computed from ENTRY objects. ENTRY must be fully ordered by a member:
- *      int compare(const GrTBinHashKey<ENTRY, ..>& k);
- *  which returns negative if the ENTRY < k, 0 if it equals k, and positive if k < the ENTRY.
- *  Additionally, ENTRY must be flattenable into the key using setKeyData.
- *
- *  This class satisfies the requirements to be a key for a GrTHashTable.
+ *  GrBinHashKey is a hash key class that can take a data chunk of any predetermined
+ *  length. The hash function used is the One-at-a-Time Hash
+ *  (http://burtleburtle.net/bob/hash/doobs.html).
  */
-template<typename ENTRY, size_t KEY_SIZE>
-class GrTBinHashKey {
+template<size_t KEY_SIZE>
+class GrBinHashKey {
 public:
     enum { kKeySize = KEY_SIZE };
 
-    GrTBinHashKey() {
+    GrBinHashKey() {
         this->reset();
     }
 
-    GrTBinHashKey(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) {
-        *this = other;
-    }
-
-    GrTBinHashKey<ENTRY, KEY_SIZE>& operator=(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) {
-        memcpy(this, &other, sizeof(*this));
-        return *this;
-    }
-
-    ~GrTBinHashKey() {
-    }
-
     void reset() {
         fHash = 0;
 #ifdef SK_DEBUG
@@ -52,39 +34,49 @@
     }
 
     void setKeyData(const uint32_t* SK_RESTRICT data) {
-        SkASSERT(GrIsALIGN4(KEY_SIZE));
+        SK_COMPILE_ASSERT(KEY_SIZE % 4 == 0, key_size_mismatch);
         memcpy(&fData, data, KEY_SIZE);
 
         uint32_t hash = 0;
         size_t len = KEY_SIZE;
         while (len >= 4) {
             hash += *data++;
-            hash += (fHash << 10);
+            hash += (hash << 10);
             hash ^= (hash >> 6);
             len -= 4;
         }
-        hash += (fHash << 3);
-        hash ^= (fHash >> 11);
-        hash += (fHash << 15);
+        hash += (hash << 3);
+        hash ^= (hash >> 11);
+        hash += (hash << 15);
 #ifdef SK_DEBUG
         fIsValid = true;
 #endif
         fHash = hash;
     }
 
-    int compare(const GrTBinHashKey<ENTRY, KEY_SIZE>& key) const {
+    bool operator==(const GrBinHashKey<KEY_SIZE>& key) const {
         SkASSERT(fIsValid && key.fIsValid);
-        return memcmp(fData, key.fData, KEY_SIZE);
+        if (fHash != key.fHash) {
+            return false;
+        }
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fData); ++i) {
+            if (fData[i] != key.fData[i]) {
+                return false;
+            }
+        }
+        return true;
     }
 
-    static bool EQ(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) {
-        SkASSERT(key.fIsValid);
-        return 0 == entry.compare(key);
-    }
-
-    static bool LT(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) {
-        SkASSERT(key.fIsValid);
-        return entry.compare(key) < 0;
+    bool operator<(const GrBinHashKey<KEY_SIZE>& key) const {
+        SkASSERT(fIsValid && key.fIsValid);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fData); ++i) {
+            if (fData[i] < key.fData[i]) {
+                return true;
+            } else if (fData[i] > key.fData[i]) {
+                return false;
+            }
+        }
+        return false;
     }
 
     uint32_t getHash() const {
@@ -94,12 +86,12 @@
 
     const uint8_t* getData() const {
         SkASSERT(fIsValid);
-        return fData;
+        return reinterpret_cast<const uint8_t*>(fData);
     }
 
 private:
     uint32_t            fHash;
-    uint8_t             fData[KEY_SIZE];  // Buffer for key storage
+    uint32_t            fData[KEY_SIZE / sizeof(uint32_t)];  // Buffer for key storage.
 
 #ifdef SK_DEBUG
 public:
diff --git a/gpu/GrResourceCache.h b/gpu/GrResourceCache.h
index 38378ac..ca30732 100644
--- a/gpu/GrResourceCache.h
+++ b/gpu/GrResourceCache.h
@@ -54,7 +54,7 @@
     }
 
     GrResourceKey() {
-        fKey.fHashedKey.reset();
+        fKey.reset();
     }
 
     void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
@@ -63,41 +63,34 @@
 
     //!< returns hash value [0..kHashMask] for the key
     int getHash() const {
-        return fKey.fHashedKey.getHash() & kHashMask;
+        return fKey.getHash() & kHashMask;
     }
 
     bool isScratch() const {
         return ScratchDomain() ==
-            *reinterpret_cast<const GrCacheID::Domain*>(fKey.fHashedKey.getData() +
+            *reinterpret_cast<const GrCacheID::Domain*>(fKey.getData() +
                                                         kCacheIDDomainOffset);
     }
 
     ResourceType getResourceType() const {
-        return *reinterpret_cast<const ResourceType*>(fKey.fHashedKey.getData() +
+        return *reinterpret_cast<const ResourceType*>(fKey.getData() +
                                                       kResourceTypeOffset);
     }
 
     ResourceFlags getResourceFlags() const {
-        return *reinterpret_cast<const ResourceFlags*>(fKey.fHashedKey.getData() +
+        return *reinterpret_cast<const ResourceFlags*>(fKey.getData() +
                                                        kResourceFlagsOffset);
     }
 
-    int compare(const GrResourceKey& other) const {
-        return fKey.fHashedKey.compare(other.fKey.fHashedKey);
-    }
+    bool operator==(const GrResourceKey& other) const { return fKey == other.fKey; }
+    bool operator<(const GrResourceKey& other) const { return fKey < other.fKey; }
 
-    static bool LT(const GrResourceKey& a, const GrResourceKey& b) {
-        return a.compare(b) < 0;
-    }
-
-    static bool EQ(const GrResourceKey& a, const GrResourceKey& b) {
-        return 0 == a.compare(b);
-    }
-
-    inline static bool LT(const GrResourceEntry& entry, const GrResourceKey& key);
-    inline static bool EQ(const GrResourceEntry& entry, const GrResourceKey& key);
-    inline static bool LT(const GrResourceEntry& a, const GrResourceEntry& b);
-    inline static bool EQ(const GrResourceEntry& a, const GrResourceEntry& b);
+    static bool LessThan(const GrResourceEntry& entry, const GrResourceKey& key);
+    static bool Equals(const GrResourceEntry& entry, const GrResourceKey& key);
+#ifdef SK_DEBUG
+    static bool LessThan(const GrResourceEntry& a, const GrResourceEntry& b);
+    static bool Equals(const GrResourceEntry& a, const GrResourceEntry& b);
+#endif
 
 private:
     enum {
@@ -125,21 +118,9 @@
         memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
         memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
         memset(k + kPadOffset, 0, kPadSize);
-        fKey.fHashedKey.setKeyData(keyData.fKey32);
+        fKey.setKeyData(keyData.fKey32);
     }
-
-    struct Key;
-    typedef GrTBinHashKey<Key, kKeySize> HashedKey;
-
-    struct Key {
-        int compare(const HashedKey& hashedKey) const {
-            return fHashedKey.compare(hashedKey);
-        }
-
-        HashedKey fHashedKey;
-    };
-
-    Key fKey;
+    GrBinHashKey<kKeySize> fKey;
 };
 
 // The cache listens for these messages to purge junk resources proactively.
@@ -174,21 +155,23 @@
     friend class GrDLinkedList;
 };
 
-bool GrResourceKey::LT(const GrResourceEntry& entry, const GrResourceKey& key) {
-    return LT(entry.key(), key);
+inline bool GrResourceKey::LessThan(const GrResourceEntry& entry, const GrResourceKey& key) {
+    return entry.key() < key;
 }
 
-bool GrResourceKey::EQ(const GrResourceEntry& entry, const GrResourceKey& key) {
-    return EQ(entry.key(), key);
+inline bool GrResourceKey::Equals(const GrResourceEntry& entry, const GrResourceKey& key) {
+    return entry.key() == key;
 }
 
-bool GrResourceKey::LT(const GrResourceEntry& a, const GrResourceEntry& b) {
-    return LT(a.key(), b.key());
+#ifdef SK_DEBUG
+inline bool GrResourceKey::LessThan(const GrResourceEntry& a, const GrResourceEntry& b) {
+    return a.key() < b.key();
 }
 
-bool GrResourceKey::EQ(const GrResourceEntry& a, const GrResourceEntry& b) {
-    return EQ(a.key(), b.key());
+inline bool GrResourceKey::Equals(const GrResourceEntry& a, const GrResourceEntry& b) {
+    return a.key() == b.key();
 }
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/gpu/GrTHashTable.h b/gpu/GrTHashTable.h
index 3b32977..83462c7 100644
--- a/gpu/GrTHashTable.h
+++ b/gpu/GrTHashTable.h
@@ -16,8 +16,10 @@
 
 /**
  *  Key needs
- *      static bool EQ(const Entry&, const HashKey&);
- *      static bool LT(const Entry&, const HashKey&);
+ *      static bool Equals(const Entry&, const Key&);
+ *      static bool LessThan(const Entry&, const Key&);
+ *      static bool Equals(const Entry&, const Entry&); for SK_DEBUG if GrTHashTable::validate() is called
+ *      static bool LessThan(const Entry&, const Entry&); for SK_DEBUG if GrTHashTable::validate() is called
  *      uint32_t getHash() const;
  *
  *  Allows duplicate key entries but on find you may get
@@ -90,7 +92,7 @@
     int low = 0;
     while (high > low) {
         int index = (low + high) >> 1;
-        if (Key::LT(*array[index], key)) {
+        if (Key::LessThan(*array[index], key)) {
             low = index + 1;
         } else {
             high = index;
@@ -98,15 +100,15 @@
     }
 
     // check if we found it
-    if (Key::EQ(*array[high], key)) {
+    if (Key::Equals(*array[high], key)) {
         // above search should have found the first occurrence if there
         // are multiple.
-        SkASSERT(0 == high || Key::LT(*array[high - 1], key));
+        SkASSERT(0 == high || Key::LessThan(*array[high - 1], key));
         return high;
     }
 
     // now return the ~ of where we should insert it
-    if (Key::LT(*array[high], key)) {
+    if (Key::LessThan(*array[high], key)) {
         high += 1;
     }
     return ~high;
@@ -119,7 +121,7 @@
     int hashIndex = hash2Index(key.getHash());
     T* elem = fHash[hashIndex];
 
-    if (NULL != elem && Key::EQ(*elem, key) && filter(elem)) {
+    if (NULL != elem && Key::Equals(*elem, key) && filter(elem)) {
         return elem;
     }
 
@@ -133,9 +135,9 @@
 
     // above search should have found the first occurrence if there
     // are multiple.
-    SkASSERT(0 == index || Key::LT(*array[index - 1], key));
+    SkASSERT(0 == index || Key::LessThan(*array[index - 1], key));
 
-    for ( ; index < count() && Key::EQ(*array[index], key); ++index) {
+    for ( ; index < count() && Key::Equals(*array[index], key); ++index) {
         if (filter(fSorted[index])) {
             // update the hash
             fHash[hashIndex] = fSorted[index];
@@ -192,8 +194,8 @@
 void GrTHashTable<T, Key, kHashBits>::validate() const {
     int count = fSorted.count();
     for (int i = 1; i < count; i++) {
-        SkASSERT(Key::LT(*fSorted[i - 1], *fSorted[i]) ||
-                 Key::EQ(*fSorted[i - 1], *fSorted[i]));
+        SkASSERT(Key::LessThan(*fSorted[i - 1], *fSorted[i]) ||
+                 Key::Equals(*fSorted[i - 1], *fSorted[i]));
     }
 }
 
diff --git a/gpu/GrTextStrike_impl.h b/gpu/GrTextStrike_impl.h
index 4297185..0691eaa 100644
--- a/gpu/GrTextStrike_impl.h
+++ b/gpu/GrTextStrike_impl.h
@@ -19,10 +19,10 @@
 
     intptr_t getHash() const { return fFontScalerKey->getHash(); }
 
-    static bool LT(const GrTextStrike& strike, const Key& key) {
+    static bool LessThan(const GrTextStrike& strike, const Key& key) {
         return *strike.getFontScalerKey() < *key.fFontScalerKey;
     }
-    static bool EQ(const GrTextStrike& strike, const Key& key) {
+    static bool Equals(const GrTextStrike& strike, const Key& key) {
         return *strike.getFontScalerKey() == *key.fFontScalerKey;
     }
 
@@ -88,10 +88,10 @@
 
     uint32_t getHash() const { return fPackedID; }
 
-    static bool LT(const GrGlyph& glyph, const Key& key) {
+    static bool LessThan(const GrGlyph& glyph, const Key& key) {
         return glyph.fPackedID < key.fPackedID;
     }
-    static bool EQ(const GrGlyph& glyph, const Key& key) {
+    static bool Equals(const GrGlyph& glyph, const Key& key) {
         return glyph.fPackedID == key.fPackedID;
     }
 
diff --git a/gpu/SkGrFontScaler.cpp b/gpu/SkGrFontScaler.cpp
index 6514866..1ca9357 100644
--- a/gpu/SkGrFontScaler.cpp
+++ b/gpu/SkGrFontScaler.cpp
@@ -85,6 +85,8 @@
             return kA8_GrMaskFormat;
         case SkMask::kLCD16_Format:
             return kA565_GrMaskFormat;
+        // TODO: properly support kARGB32_Format.
+        case SkMask::kARGB32_Format:
         case SkMask::kLCD32_Format:
             return kA888_GrMaskFormat;
         default:
diff --git a/gpu/effects/GrTextureStripAtlas.h b/gpu/effects/GrTextureStripAtlas.h
index e56e736..e06e273 100644
--- a/gpu/effects/GrTextureStripAtlas.h
+++ b/gpu/effects/GrTextureStripAtlas.h
@@ -136,12 +136,15 @@
 
     // Hash table entry for atlases
     class AtlasEntry;
-    typedef GrTBinHashKey<AtlasEntry, sizeof(GrTextureStripAtlas::Desc)> AtlasHashKey;
+    class AtlasHashKey : public GrBinHashKey<sizeof(GrTextureStripAtlas::Desc)> {
+    public:
+        static bool Equals(const AtlasEntry& entry, const AtlasHashKey& key);
+        static bool LessThan(const AtlasEntry& entry, const AtlasHashKey& key);
+    };
     class AtlasEntry : public ::SkNoncopyable {
     public:
         AtlasEntry() : fAtlas(NULL) {}
         ~AtlasEntry() { SkDELETE(fAtlas); }
-        int compare(const AtlasHashKey& key) const { return fKey.compare(key); }
         AtlasHashKey fKey;
         GrTextureStripAtlas* fAtlas;
     };
@@ -178,4 +181,14 @@
     SkTDArray<AtlasRow*> fKeyTable;
 };
 
+inline bool GrTextureStripAtlas::AtlasHashKey::Equals(const AtlasEntry& entry,
+                                                      const AtlasHashKey& key) {
+    return entry.fKey == key;
+}
+
+inline bool GrTextureStripAtlas::AtlasHashKey::LessThan(const AtlasEntry& entry,
+                                                        const AtlasHashKey& key) {
+    return entry.fKey < key;
+}
+
 #endif
diff --git a/gpu/gl/GrGpuGL.cpp b/gpu/gl/GrGpuGL.cpp
index 9cf39b6..00d27b3 100644
--- a/gpu/gl/GrGpuGL.cpp
+++ b/gpu/gl/GrGpuGL.cpp
@@ -1276,6 +1276,7 @@
             return;
         }
     }
+
     this->flushRenderTarget(rect);
     GrAutoTRestore<ScissorState> asr(&fScissorState);
     fScissorState.fEnabled = (NULL != rect);
@@ -1522,10 +1523,16 @@
     if (fHWBoundRenderTarget != rt) {
         GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID()));
 #ifdef SK_DEBUG
-        GrGLenum status;
-        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
-        if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
-            GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+        // don't do this check in Chromium -- this is causing
+        // lots of repeated command buffer flushes when the compositor is
+        // rendering with Ganesh, which is really slow; even too slow for
+        // Debug mode.
+        if (!this->glContext().info().isChromium()) {
+            GrGLenum status;
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+                GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+            }
         }
 #endif
         fHWBoundRenderTarget = rt;
diff --git a/gpu/gr_unittests.cpp b/gpu/gr_unittests.cpp
deleted file mode 100644
index ae9f67f..0000000
--- a/gpu/gr_unittests.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrBinHashKey.h"
-#include "GrDrawTarget.h"
-#include "SkMatrix.h"
-#include "GrRedBlackTree.h"
-
-// FIXME: needs to be in a header
-void gr_run_unittests();
-
-// If we aren't inheriting these as #defines from elsewhere,
-// clang demands they be declared before we #include the template
-// that relies on them.
-#ifdef SK_DEBUG
-static bool LT(const int& elem, int value) {
-    return elem < value;
-}
-static bool EQ(const int& elem, int value) {
-    return elem == value;
-}
-#include "GrTBSearch.h"
-
-static void test_bsearch() {
-    const int array[] = {
-        1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99
-    };
-
-    for (int n = 0; n < static_cast<int>(GR_ARRAY_COUNT(array)); ++n) {
-        for (int i = 0; i < n; i++) {
-            int index = GrTBSearch<int, int>(array, n, array[i]);
-            SkASSERT(index == (int) i);
-            index = GrTBSearch<int, int>(array, n, -array[i]);
-            SkASSERT(index < 0);
-        }
-    }
-}
-#endif
-
-// bogus empty class for GrBinHashKey
-class BogusEntry {};
-
-static void test_binHashKey()
-{
-    const char* testStringA_ = "abcdABCD";
-    const char* testStringB_ = "abcdBBCD";
-    const uint32_t* testStringA = reinterpret_cast<const uint32_t*>(testStringA_);
-    const uint32_t* testStringB = reinterpret_cast<const uint32_t*>(testStringB_);
-    enum {
-        kDataLenUsedForKey = 8
-    };
-
-    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA;
-    keyA.setKeyData(testStringA);
-    // test copy constructor and comparison
-    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA);
-    SkASSERT(keyA.compare(keyA2) == 0);
-    SkASSERT(keyA.getHash() == keyA2.getHash());
-    // test re-init
-    keyA2.setKeyData(testStringA);
-    SkASSERT(keyA.compare(keyA2) == 0);
-    SkASSERT(keyA.getHash() == keyA2.getHash());
-    // test sorting
-    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyB;
-    keyB.setKeyData(testStringB);
-    SkASSERT(keyA.compare(keyB) < 0);
-    SkASSERT(keyA.getHash() != keyB.getHash());
-}
-
-
-void gr_run_unittests() {
-    SkDEBUGCODE(test_bsearch();)
-    test_binHashKey();
-    GrRedBlackTree<int>::UnitTest();
-}
diff --git a/opts/SkBlitMask_opts_arm.cpp b/opts/SkBlitMask_opts_arm.cpp
index 0ad0919..2bf7603 100644
--- a/opts/SkBlitMask_opts_arm.cpp
+++ b/opts/SkBlitMask_opts_arm.cpp
@@ -1,14 +1,39 @@
 
+#include "SkColor.h"
+#include "SkColorPriv.h"
 #include "SkBlitMask.h"
+#include "SkUtilsArm.h"
+#include "SkBlitMask_opts_arm_neon.h"
 
 SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkBitmap::Config dstConfig,
                                                      SkMask::Format maskFormat,
                                                      SkColor color) {
+#if SK_ARM_NEON_IS_NONE
+    return NULL;
+#else
+#if SK_ARM_NEON_IS_DYNAMIC
+    if (!sk_cpu_arm_has_neon()) {
+        return NULL;
+    }
+#endif
+    if ((SkBitmap::kARGB_8888_Config == dstConfig) &&
+        (SkMask::kA8_Format == maskFormat)) {
+            return D32_A8_Factory_neon(color);
+    }
+#endif
+
+    // We don't need to handle the SkMask::kLCD16_Format case as the default
+    // LCD16 will call us through SkBlitMask::PlatformBlitRowProcs16()
+
     return NULL;
 }
 
 SkBlitMask::BlitLCD16RowProc SkBlitMask::PlatformBlitRowProcs16(bool isOpaque) {
-    return NULL;
+    if (isOpaque) {
+        return SK_ARM_NEON_WRAP(SkBlitLCD16OpaqueRow);
+    } else {
+        return SK_ARM_NEON_WRAP(SkBlitLCD16Row);
+    }
 }
 
 SkBlitMask::RowProc SkBlitMask::PlatformRowProcs(SkBitmap::Config dstConfig,
diff --git a/opts/SkColor_opts_neon.h b/opts/SkColor_opts_neon.h
index f812397..85752f5 100644
--- a/opts/SkColor_opts_neon.h
+++ b/opts/SkColor_opts_neon.h
@@ -2,6 +2,7 @@
 #define SkColor_opts_neon_DEFINED
 
 #include "SkTypes.h"
+#include "SkColorPriv.h"
 
 #include <arm_neon.h>
 
@@ -65,4 +66,20 @@
     return ret;
 }
 
+/* This function blends 8 pixels of the same channel in the exact same way as
+ * SkBlend32.
+ */
+static inline uint8x8_t SkBlend32_neon8(uint8x8_t src, uint8x8_t dst, uint16x8_t scale) {
+    int16x8_t src_wide, dst_wide;
+
+    src_wide = vreinterpretq_s16_u16(vmovl_u8(src));
+    dst_wide = vreinterpretq_s16_u16(vmovl_u8(dst));
+
+    src_wide = (src_wide - dst_wide) * vreinterpretq_s16_u16(scale);
+
+    dst_wide += vshrq_n_s16(src_wide, 5);
+
+    return vmovn_u16(vreinterpretq_u16_s16(dst_wide));
+}
+
 #endif /* #ifndef SkColor_opts_neon_DEFINED */
diff --git a/pdf/SkPDFImage.cpp b/pdf/SkPDFImage.cpp
index a99c9fe..81adcc2 100644
--- a/pdf/SkPDFImage.cpp
+++ b/pdf/SkPDFImage.cpp
@@ -36,7 +36,6 @@
             return srcRect.width() * 3 * srcRect.height();
         case SkBitmap::kARGB_8888_Config:
             return srcRect.width() * 3 * srcRect.height();
-        case SkBitmap::kA1_Config:
         case SkBitmap::kA8_Config:
             return 1;
         default:
@@ -166,48 +165,6 @@
     return stream;
 }
 
-static SkStream* extract_a1_alpha(const SkBitmap& bitmap,
-                                  const SkIRect& srcRect,
-                                  bool* isOpaque,
-                                  bool* isTransparent) {
-    const int alphaRowBytes = (srcRect.width() + 7) / 8;
-    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
-                                  (alphaRowBytes * srcRect.height()));
-    uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
-
-    int offset1 = srcRect.fLeft % 8;
-    int offset2 = 8 - offset1;
-
-    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-        uint8_t* src = bitmap.getAddr1(0, y);
-        // This may read up to one byte after src, but the
-        // potentially invalid bits are never used for computation.
-        for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8)  {
-            if (offset1) {
-                alphaDst[0] = src[x / 8] << offset1 |
-                    src[x / 8 + 1] >> offset2;
-            } else {
-                alphaDst[0] = src[x / 8];
-            }
-            if (x + 7 < srcRect.fRight) {
-                *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
-                *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
-            }
-            alphaDst++;
-        }
-        // Calculate the mask of bits we're interested in within the
-        // last byte of alphaDst.
-        // width mod 8  == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
-        uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
-        if (srcRect.width() % 8) {
-            *isOpaque &= (alphaDst[-1] & mask) == (SK_AlphaOPAQUE & mask);
-            *isTransparent &=
-                    (alphaDst[-1] & mask) == (SK_AlphaTRANSPARENT & mask);
-        }
-    }
-    return stream;
-}
-
 static SkStream* extract_a8_alpha(const SkBitmap& bitmap,
                                   const SkIRect& srcRect,
                                   bool* isOpaque,
@@ -283,14 +240,6 @@
             stream = extract_argb8888_data(bitmap, srcRect, extractAlpha,
                                            &isOpaque, &transparent);
             break;
-        case SkBitmap::kA1_Config:
-            if (!extractAlpha) {
-                stream = create_black_image();
-            } else {
-                stream = extract_a1_alpha(bitmap, srcRect,
-                                          &isOpaque, &transparent);
-            }
-            break;
         case SkBitmap::kA8_Config:
             if (!extractAlpha) {
                 stream = create_black_image();
@@ -574,8 +523,7 @@
     insertName("Type", "XObject");
     insertName("Subtype", "Image");
 
-    bool alphaOnly = (config == SkBitmap::kA1_Config ||
-                      config == SkBitmap::kA8_Config);
+    bool alphaOnly = (config == SkBitmap::kA8_Config);
 
     if (!isAlpha && alphaOnly) {
         // For alpha only images, we stretch a single pixel of black for
@@ -601,8 +549,6 @@
     int bitsPerComp = 8;
     if (config == SkBitmap::kARGB_4444_Config) {
         bitsPerComp = 4;
-    } else if (isAlpha && config == SkBitmap::kA1_Config) {
-        bitsPerComp = 1;
     }
     insertInt("BitsPerComponent", bitsPerComp);
 
diff --git a/utils/debugger/SkObjectParser.cpp b/utils/debugger/SkObjectParser.cpp
index 54ae077..ebbd400 100644
--- a/utils/debugger/SkObjectParser.cpp
+++ b/utils/debugger/SkObjectParser.cpp
@@ -26,9 +26,9 @@
     mBitmap->appendS32(bitmap.height());
 
     const char* gConfigStrings[] = {
-        "None", "A1", "A8", "Index8", "RGB565", "ARGB4444", "ARGB8888"
+        "None", "A8", "Index8", "RGB565", "ARGB4444", "ARGB8888"
     };
-    SkASSERT(SkBitmap::kConfigCount == 7);
+    SkASSERT(SkBitmap::kConfigCount == SK_ARRAY_COUNT(gConfigStrings));
 
     mBitmap->append(" Config: ");
     mBitmap->append(gConfigStrings[bitmap.config()]);