merge in ics-mr1-release history after reset to ics-mr1
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
index 270abc2..3830b4a 100644
--- a/include/core/SkDrawLooper.h
+++ b/include/core/SkDrawLooper.h
@@ -52,6 +52,20 @@
      */
     virtual bool next(SkCanvas*, SkPaint* paint) = 0;
     
+    /**
+     * The fast bounds functions are used to enable the paint to be culled early
+     * in the drawing pipeline. If a subclass can support this feature it must
+     * return true for the canComputeFastBounds() function.  If that function
+     * returns false then computeFastBounds behavior is undefined otherwise it
+     * is expected to have the following behavior. Given the parent paint and
+     * the parent's bounding rect the subclass must fill in and return the
+     * storage rect, where the storage rect is with the union of the src rect
+     * and the looper's bounding rect.
+     */
+    virtual bool canComputeFastBounds(const SkPaint& paint);
+    virtual void computeFastBounds(const SkPaint& paint,
+                                   const SkRect& src, SkRect* dst);
+
 protected:
     SkDrawLooper() {}
     SkDrawLooper(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
index 641ad83..60dade9 100644
--- a/include/core/SkMaskFilter.h
+++ b/include/core/SkMaskFilter.h
@@ -72,6 +72,19 @@
 
     virtual void flatten(SkFlattenableWriteBuffer& ) {}
 
+    /**
+     * The fast bounds function is used to enable the paint to be culled early
+     * in the drawing pipeline. This function accepts the current bounds of the
+     * paint as its src param and the filter adjust those bounds using its
+     * current mask and returns the result using the dest param. Callers are
+     * allowed to provide the same struct for both src and dest so each
+     * implementation must accomodate that behavior.
+     *
+     *  The default impl calls filterMask with the src mask having no image,
+     *  but subclasses may override this if they can compute the rect faster.
+     */
+    virtual void computeFastBounds(const SkRect& src, SkRect* dest);
+
 protected:
     // empty for now, but lets get our subclass to remember to init us for the future
     SkMaskFilter(SkFlattenableReadBuffer&) {}
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index f336f5b..9b547a4 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -18,7 +18,7 @@
 #define SkPaint_DEFINED
 
 #include "SkColor.h"
-#include "SkMath.h"
+#include "SkDrawLooper.h"
 #include "SkXfermode.h"
 
 class SkAutoGlyphCache;
@@ -35,7 +35,6 @@
 class SkPathEffect;
 class SkRasterizer;
 class SkShader;
-class SkDrawLooper;
 class SkTypeface;
 
 typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**,
@@ -425,10 +424,11 @@
         the bounds computation expensive.
     */
     bool canComputeFastBounds() const {
+        if (this->getLooper()) {
+            return this->getLooper()->canComputeFastBounds(*this);
+        }
         // use bit-or since no need for early exit
-        return (reinterpret_cast<uintptr_t>(this->getMaskFilter()) |
-                reinterpret_cast<uintptr_t>(this->getLooper()) |
-                reinterpret_cast<uintptr_t>(this->getRasterizer()) |
+        return (reinterpret_cast<uintptr_t>(this->getRasterizer()) |
                 reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
     }
 
@@ -454,8 +454,12 @@
         }
     */
     const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const {
-        return this->getStyle() == kFill_Style ? orig :
-                    this->computeStrokeFastBounds(orig, storage);
+        if (this->getStyle() == kFill_Style &&
+                !this->getLooper() && !this->getMaskFilter()) {
+            return orig;
+        }
+
+        return this->doComputeFastBounds(orig, storage);
     }
 
     /** Get the paint's shader object.
@@ -864,8 +868,7 @@
                         void (*proc)(const SkDescriptor*, void*),
                         void* context, bool ignoreGamma = false) const;
 
-    const SkRect& computeStrokeFastBounds(const SkRect& orig,
-                                          SkRect* storage) const;
+    const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage) const;
 
     enum {
         kCanonicalTextSizeForPaths = 64
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index a278261..2008c93 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -57,4 +57,19 @@
     return true;
 }
 
+void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) {
+    SkMask  srcM, dstM;
+
+    srcM.fImage = NULL;
+    src.roundOut(&srcM.fBounds);
+    srcM.fRowBytes = 0;
+    srcM.fFormat = SkMask::kA8_Format;
+
+    SkIPoint margin;    // ignored
+    if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
+        dst->set(dstM.fBounds);
+    } else {
+        dst->set(srcM.fBounds);
+    }
+}
 
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index c29c4f0..21bd078 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -17,7 +17,6 @@
 
 #include "SkPaint.h"
 #include "SkColorFilter.h"
-#include "SkDrawLooper.h"
 #include "SkFontHost.h"
 #include "SkMaskFilter.h"
 #include "SkPathEffect.h"
@@ -1703,23 +1702,38 @@
     return width != 0;  // return true if we're filled, or false if we're hairline (width == 0)
 }
 
-const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
-                                               SkRect* storage) const {
+const SkRect& SkPaint::doComputeFastBounds(const SkRect& src,
+                                                 SkRect* storage) const {
     SkASSERT(storage);
-    SkASSERT(this->getStyle() != SkPaint::kFill_Style);
 
-    // since we're stroked, outset the rect by the radius (and join type)
-    SkScalar radius = SkScalarHalf(this->getStrokeWidth());
-    if (0 == radius) {  // hairline
-        radius = SK_Scalar1;
-    } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
-        SkScalar scale = this->getStrokeMiter();
-        if (scale > SK_Scalar1) {
-            radius = SkScalarMul(radius, scale);
-        }
+    if (this->getLooper()) {
+        SkASSERT(this->getLooper()->canComputeFastBounds(*this));
+        this->getLooper()->computeFastBounds(*this, src, storage);
+        return *storage;
     }
-    storage->set(src.fLeft - radius, src.fTop - radius,
-                 src.fRight + radius, src.fBottom + radius);
+
+    if (this->getStyle() != SkPaint::kFill_Style) {
+        // since we're stroked, outset the rect by the radius (and join type)
+        SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+        if (0 == radius) {  // hairline
+            radius = SK_Scalar1;
+        } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+            SkScalar scale = this->getStrokeMiter();
+            if (scale > SK_Scalar1) {
+                radius = SkScalarMul(radius, scale);
+            }
+        }
+        storage->set(src.fLeft - radius, src.fTop - radius,
+                     src.fRight + radius, src.fBottom + radius);
+    } else {
+        *storage = src;
+    }
+
+    // check the mask filter
+    if (this->getMaskFilter()) {
+        this->getMaskFilter()->computeFastBounds(*storage, storage);
+    }
+
     return *storage;
 }
 
@@ -1811,3 +1825,48 @@
     }
     return NULL;
 }
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
+    SkCanvas canvas;
+
+    this->init(&canvas);
+    for (;;) {
+        SkPaint p(paint);
+        if (this->next(&canvas, &p)) {
+            p.setLooper(NULL);
+            if (!p.canComputeFastBounds()) {
+                return false;
+            }
+        } else {
+            break;
+        }
+    }
+    return true;
+}
+
+void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src,
+                                     SkRect* dst) {
+    SkCanvas canvas;
+
+    this->init(&canvas);
+    for (bool firstTime = true;; firstTime = false) {
+        SkPaint p(paint);
+        if (this->next(&canvas, &p)) {
+            SkRect r(src);
+
+            p.setLooper(NULL);
+            p.computeFastBounds(r, &r);
+            canvas.getTotalMatrix().mapRect(&r);
+
+            if (firstTime) {
+                *dst = r;
+            } else {
+                dst->join(r);
+            }
+        } else {
+            break;
+        }
+    }
+}
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 697b399..4db61f7 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -25,6 +25,7 @@
     DRAW_PICTURE,
     DRAW_POINTS,
     DRAW_POS_TEXT,
+    DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
     DRAW_POS_TEXT_H,
     DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
     DRAW_RECT,
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 80189ab..5a856ef 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -625,6 +625,17 @@
                 const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
                 canvas.drawPosText(text.text(), text.length(), pos, paint);
             } break;
+            case DRAW_POS_TEXT_TOP_BOTTOM: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                size_t points = getInt();
+                const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
+                const SkScalar top = fReader.readScalar();
+                const SkScalar bottom = fReader.readScalar();
+                if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+                    canvas.drawPosText(text.text(), text.length(), pos, paint);
+                }
+            } break;
             case DRAW_POS_TEXT_H: {
                 const SkPaint& paint = *getPaint();
                 getText(&text);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 6b2b330..5810051 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -242,14 +242,14 @@
 }
 
 void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
-                                              SkScalar baselineY) {
+                                              SkScalar minY, SkScalar maxY) {
     SkPaint::FontMetrics metrics;
     paint.getFontMetrics(&metrics);
     SkRect bounds;
     // construct a rect so we can see any adjustments from the paint.
     // we use 0,1 for left,right, just so the rect isn't empty
-    bounds.set(0, metrics.fTop + baselineY,
-               SK_Scalar1, metrics.fBottom + baselineY);
+    bounds.set(0, metrics.fTop + minY,
+               SK_Scalar1, metrics.fBottom + maxY);
     (void)paint.computeFastBounds(bounds, &bounds);
     // now record the top and bottom
     addScalar(bounds.fTop);
@@ -266,7 +266,7 @@
     addScalar(x);
     addScalar(y);
     if (fast) {
-        addFontMetricsTopBottom(paint, y);
+        addFontMetricsTopBottom(paint, y, y);
     }
     validate();
 }
@@ -278,23 +278,34 @@
         return;
 
     bool canUseDrawH = true;
+    SkScalar minY = pos[0].fY;
+    SkScalar maxY = pos[0].fY;
     // check if the caller really should have used drawPosTextH()
     {
         const SkScalar firstY = pos[0].fY;
         for (size_t index = 1; index < points; index++) {
             if (pos[index].fY != firstY) {
                 canUseDrawH = false;
-                break;
+                if (pos[index].fY < minY) {
+                    minY = pos[index].fY;
+                } else if (pos[index].fY > maxY) {
+                    maxY = pos[index].fY;
+                }
             }
         }
     }
 
-    bool fast = canUseDrawH && paint.canComputeFastBounds();
+    bool fastBounds = paint.canComputeFastBounds();
+    bool fast = canUseDrawH && fastBounds;
 
     if (fast) {
         addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
+    } else if (canUseDrawH) {
+        addDraw(DRAW_POS_TEXT_H);
+    } else if (fastBounds) {
+        addDraw(DRAW_POS_TEXT_TOP_BOTTOM);
     } else {
-        addDraw(canUseDrawH ? DRAW_POS_TEXT_H : DRAW_POS_TEXT);
+        addDraw(DRAW_POS_TEXT);
     }
     addPaint(paint);
     addText(text, byteLength);
@@ -305,7 +316,7 @@
 #endif
     if (canUseDrawH) {
         if (fast) {
-            addFontMetricsTopBottom(paint, pos[0].fY);
+            addFontMetricsTopBottom(paint, pos[0].fY, pos[0].fY);
         }
         addScalar(pos[0].fY);
         SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
@@ -314,6 +325,9 @@
     }
     else {
         fWriter.writeMul4(pos, points * sizeof(SkPoint));
+        if (fastBounds) {
+            addFontMetricsTopBottom(paint, minY, maxY);
+        }
     }
 #ifdef SK_DEBUG_SIZE
     fPointBytes += fWriter.size() - start;
@@ -340,7 +354,7 @@
     size_t start = fWriter.size();
 #endif
     if (fast) {
-        addFontMetricsTopBottom(paint, constY);
+        addFontMetricsTopBottom(paint, constY, constY);
     }
     addScalar(constY);
     fWriter.writeMul4(xpos, points * sizeof(SkScalar));
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index e0d6a50..5a400cb 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -59,8 +59,8 @@
                               const SkPaint&);
     virtual void drawData(const void*, size_t);
 
-    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar baselineY);
-    
+    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar minY, SkScalar maxY);
+
     const SkTDArray<const SkFlatBitmap* >& getBitmaps() const {
         return fBitmaps;
     }
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index 41e04b8..ad00d17 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -27,6 +27,7 @@
     // overrides from SkMaskFilter
     virtual SkMask::Format getFormat();
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin);
+    virtual void computeFastBounds(const SkRect& src, SkRect* dst);
 
     // overrides from SkFlattenable
     // This method is not exported to java.
@@ -111,6 +112,11 @@
     return false;
 }
 
+void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) {
+    dst->set(src.fLeft - fRadius, src.fTop - fRadius,
+             src.fRight + fRadius, src.fBottom + fRadius);
+}
+
 SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkFlattenableReadBuffer& buffer)
 {
     return SkNEW_ARGS(SkBlurMaskFilterImpl, (buffer));
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index 5d0fdcf..74cc624 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -64,15 +64,18 @@
     }
 }
 
+// Even with kEntirePaint_Bits, we always ensure that the master paint's
+// text-encoding is respected, since that controls how we interpret the
+// text/length parameters of a draw[Pos]Text call.
 void SkLayerDrawLooper::ApplyInfo(SkPaint* dst, const SkPaint& src,
                                   const LayerInfo& info) {
 
     uint32_t mask = info.fFlagsMask;
     dst->setFlags((dst->getFlags() & ~mask) | (src.getFlags() & mask));
-
     dst->setColor(xferColor(src.getColor(), dst->getColor(), info.fColorMode));
 
     BitFlags bits = info.fPaintBits;
+    SkPaint::TextEncoding encoding = dst->getTextEncoding();
 
     if (0 == bits) {
         return;
@@ -84,6 +87,7 @@
         *dst = src;
         dst->setFlags(f);
         dst->setColor(c);
+        dst->setTextEncoding(encoding);
         return;
     }