Merge Blink code to cache SkTextBlob

This CL merges the following CLs from Blink to implement SkTextBlobs:

https://codereview.chromium.org/554613004
https://codereview.chromium.org/645363003
https://codereview.chromium.org/667573008
https://codereview.chromium.org/672713002
https://codereview.chromium.org/778233002
https://codereview.chromium.org/820093005
https://codereview.chromium.org/856613002

R=esprehn@chromium.org

Review URL: https://codereview.chromium.org/859203002
diff --git a/sky/engine/core/rendering/InlineTextBox.cpp b/sky/engine/core/rendering/InlineTextBox.cpp
index cb54daf..a026235 100644
--- a/sky/engine/core/rendering/InlineTextBox.cpp
+++ b/sky/engine/core/rendering/InlineTextBox.cpp
@@ -56,7 +56,7 @@
 struct SameSizeAsInlineTextBox : public InlineBox {
     unsigned variables[1];
     unsigned short variables2[2];
-    void* pointers[2];
+    void* pointers[3];
 };
 
 COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small);
@@ -416,13 +416,16 @@
     const Font& font, const TextRun& textRun,
     const AtomicString& emphasisMark, int emphasisMarkOffset,
     int startOffset, int endOffset, int truncationPoint,
-    const FloatPoint& textOrigin, const FloatRect& boxRect)
+    const FloatPoint& textOrigin, const FloatRect& boxRect,
+    TextBlobPtr* cachedTextBlob = 0)
 {
     TextRunPaintInfo textRunPaintInfo(textRun);
     textRunPaintInfo.bounds = boxRect;
     if (startOffset <= endOffset) {
         textRunPaintInfo.from = startOffset;
         textRunPaintInfo.to = endOffset;
+        // FIXME: We should be able to use cachedTextBlob in more cases.
+        textRunPaintInfo.cachedTextBlob = cachedTextBlob;
         if (emphasisMark.isEmpty())
             context->drawText(font, textRunPaintInfo, textOrigin);
         else
@@ -460,11 +463,11 @@
 void paintTextWithEmphasisMark(
     GraphicsContext* context, const Font& font, const TextPaintingStyle& textStyle, const TextRun& textRun,
     const AtomicString& emphasisMark, int emphasisMarkOffset, int startOffset, int endOffset, int length,
-    const FloatPoint& textOrigin, const FloatRect& boxRect)
+    const FloatPoint& textOrigin, const FloatRect& boxRect, TextBlobPtr* cachedTextBlob = 0)
 {
     GraphicsContextStateSaver stateSaver(*context, false);
     updateGraphicsContext(context, textStyle, stateSaver);
-    paintText(context, font, textRun, nullAtom, 0, startOffset, endOffset, length, textOrigin, boxRect);
+    paintText(context, font, textRun, nullAtom, 0, startOffset, endOffset, length, textOrigin, boxRect, cachedTextBlob);
 
     if (!emphasisMark.isEmpty()) {
         if (textStyle.emphasisMarkColor != textStyle.fillColor)
@@ -603,12 +606,19 @@
             startOffset = ePos;
             endOffset = sPos;
         }
-        paintTextWithEmphasisMark(context, font, textStyle, textRun, emphasisMark, emphasisMarkOffset, startOffset, endOffset, length, textOrigin, boxRect);
+        // FIXME: This cache should probably ultimately be held somewhere else.
+        // A hashmap is convenient to avoid a memory hit when the
+        // RuntimeEnabledFeature is off.
+        bool textBlobIsCacheable = RuntimeEnabledFeatures::textBlobEnabled() && startOffset == 0 && endOffset == length;
+        TextBlobPtr* cachedTextBlob = textBlobIsCacheable ? &m_cachedTextBlob : nullptr;
+        paintTextWithEmphasisMark(context, font, textStyle, textRun, emphasisMark, emphasisMarkOffset, startOffset, endOffset, length, textOrigin, boxRect, cachedTextBlob);
     }
 
     if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) {
         // paint only the text that is selected
-        paintTextWithEmphasisMark(context, font, selectionStyle, textRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, textOrigin, boxRect);
+        bool textBlobIsCacheable = RuntimeEnabledFeatures::textBlobEnabled() && sPos == 0 && ePos == length;
+        TextBlobPtr* cachedTextBlob = textBlobIsCacheable ? &m_cachedTextBlob : nullptr;
+        paintTextWithEmphasisMark(context, font, selectionStyle, textRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, textOrigin, boxRect, cachedTextBlob);
     }
 
     // Paint decorations
diff --git a/sky/engine/core/rendering/InlineTextBox.h b/sky/engine/core/rendering/InlineTextBox.h
index ab67eee..76696f9 100644
--- a/sky/engine/core/rendering/InlineTextBox.h
+++ b/sky/engine/core/rendering/InlineTextBox.h
@@ -161,6 +161,7 @@
 private:
     InlineTextBox* m_prevTextBox; // The previous box that also uses our RenderObject
     InlineTextBox* m_nextTextBox; // The next box that also uses our RenderObject
+    TextBlobPtr m_cachedTextBlob;
 
     int m_start;
     unsigned short m_len;
diff --git a/sky/engine/platform/BUILD.gn b/sky/engine/platform/BUILD.gn
index 63bf93c..20e644b 100644
--- a/sky/engine/platform/BUILD.gn
+++ b/sky/engine/platform/BUILD.gn
@@ -699,7 +699,6 @@
     "fonts/FontCacheTest.cpp",
     "fonts/FontDescriptionTest.cpp",
     "fonts/FontTest.cpp",
-    "fonts/GlyphBufferTest.cpp",
     "fonts/GlyphPageTreeNodeTest.cpp",
     "fonts/android/FontCacheAndroidTest.cpp",
     "geometry/FloatBoxTest.cpp",
diff --git a/sky/engine/platform/fonts/Font.cpp b/sky/engine/platform/fonts/Font.cpp
index 3167834..b0c2975 100644
--- a/sky/engine/platform/fonts/Font.cpp
+++ b/sky/engine/platform/fonts/Font.cpp
@@ -34,6 +34,7 @@
 #include "sky/engine/platform/fonts/GlyphPageTreeNode.h"
 #include "sky/engine/platform/fonts/SimpleFontData.h"
 #include "sky/engine/platform/fonts/WidthIterator.h"
+#include "sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h"
 #include "sky/engine/platform/geometry/FloatRect.h"
 #include "sky/engine/platform/graphics/GraphicsContext.h"
 #include "sky/engine/platform/text/TextRun.h"
@@ -103,24 +104,95 @@
     m_fontFallbackList->invalidate(fontSelector);
 }
 
-void Font::drawText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const FloatPoint& point, CustomFontNotReadyAction customFontNotReadyAction) const
+float Font::buildGlyphBuffer(const TextRunPaintInfo& runInfo, GlyphBuffer& glyphBuffer,
+    ForTextEmphasisOrNot forTextEmphasis) const
+{
+    if (codePath(runInfo.run) == ComplexPath) {
+        HarfBuzzShaper shaper(this, runInfo.run, (forTextEmphasis == ForTextEmphasis)
+            ? HarfBuzzShaper::ForTextEmphasis : HarfBuzzShaper::NotForTextEmphasis);
+        shaper.setDrawRange(runInfo.from, runInfo.to);
+        shaper.shape(&glyphBuffer);
+
+        return 0;
+    }
+
+    WidthIterator it(this, runInfo.run, nullptr, false, forTextEmphasis);
+    it.advance(runInfo.from);
+    float beforeWidth = it.runWidthSoFar();
+    it.advance(runInfo.to, &glyphBuffer);
+
+    if (runInfo.run.ltr())
+        return beforeWidth;
+
+    // RTL
+    float afterWidth = it.runWidthSoFar();
+    it.advance(runInfo.run.length());
+    glyphBuffer.reverse();
+
+    return it.runWidthSoFar() - afterWidth;
+}
+
+void Font::drawText(GraphicsContext* context, const TextRunPaintInfo& runInfo,
+    const FloatPoint& point) const
+{
+    // Don't draw anything while we are using custom fonts that are in the process of loading.
+    if (shouldSkipDrawing())
+        return;
+
+    TextDrawingModeFlags textMode = context->textDrawingMode();
+    if (!(textMode & TextModeFill) && !((textMode & TextModeStroke) && context->hasStroke()))
+        return;
+
+    if (runInfo.cachedTextBlob && runInfo.cachedTextBlob->get()) {
+        ASSERT(RuntimeEnabledFeatures::textBlobEnabled());
+        // we have a pre-cached blob -- happy joy!
+        drawTextBlob(context, runInfo.cachedTextBlob->get(), point.data());
+        return;
+    }
+
+    GlyphBuffer glyphBuffer;
+    float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer);
+
+    if (glyphBuffer.isEmpty())
+        return;
+
+    if (RuntimeEnabledFeatures::textBlobEnabled()) {
+        // Enabling text-blobs forces the blob rendering path even for uncacheable blobs.
+        TextBlobPtr uncacheableTextBlob;
+        TextBlobPtr& textBlob = runInfo.cachedTextBlob ? *runInfo.cachedTextBlob : uncacheableTextBlob;
+        FloatRect blobBounds = runInfo.bounds;
+        blobBounds.moveBy(-point);
+
+        textBlob = buildTextBlob(glyphBuffer, initialAdvance, blobBounds);
+        if (textBlob) {
+            drawTextBlob(context, textBlob.get(), point.data());
+            return;
+        }
+    }
+
+    drawGlyphBuffer(context, runInfo, glyphBuffer, FloatPoint(point.x() + initialAdvance, point.y()));
+}
+
+float Font::drawUncachedText(GraphicsContext* context, const TextRunPaintInfo& runInfo,
+    const FloatPoint& point, CustomFontNotReadyAction customFontNotReadyAction) const
 {
     // Don't draw anything while we are using custom fonts that are in the process of loading,
     // except if the 'force' argument is set to true (in which case it will use a fallback
     // font).
     if (shouldSkipDrawing() && customFontNotReadyAction == DoNotPaintIfFontNotReady)
-        return;
+        return 0;
 
-    CodePath codePathToUse = codePath(runInfo.run);
-    // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
-    if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length()))
-        codePathToUse = ComplexPath;
+    TextDrawingModeFlags textMode = context->textDrawingMode();
+    if (!(textMode & TextModeFill) && !((textMode & TextModeStroke) && context->hasStroke()))
+        return 0;
 
-    if (codePathToUse != ComplexPath) {
-        drawSimpleText(context, runInfo, point);
-    } else {
-        drawComplexText(context, runInfo, point);
-    }
+    GlyphBuffer glyphBuffer;
+    float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer);
+
+    if (glyphBuffer.isEmpty())
+        return 0;
+
+    return drawGlyphBuffer(context, runInfo, glyphBuffer, FloatPoint(point.x() + initialAdvance, point.y()));
 }
 
 void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const
@@ -128,15 +200,13 @@
     if (shouldSkipDrawing())
         return;
 
-    CodePath codePathToUse = codePath(runInfo.run);
-    // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
-    if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length()))
-        codePathToUse = ComplexPath;
+    GlyphBuffer glyphBuffer;
+    float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer, ForTextEmphasis);
 
-    if (codePathToUse != ComplexPath)
-        drawEmphasisMarksForSimpleText(context, runInfo, mark, point);
-    else
-        drawEmphasisMarksForComplexText(context, runInfo, mark, point);
+    if (glyphBuffer.isEmpty())
+        return;
+
+    drawEmphasisMarks(context, runInfo, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
 }
 
 static inline void updateGlyphOverflowFromBounds(const IntRectExtent& glyphBounds,
@@ -609,97 +679,32 @@
     return markFontData->fontMetrics().height();
 }
 
-float Font::getGlyphsAndAdvancesForSimpleText(const TextRunPaintInfo& runInfo, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
-{
-    float initialAdvance;
-
-    WidthIterator it(this, runInfo.run, 0, false, forTextEmphasis);
-    it.advance(runInfo.from);
-    float beforeWidth = it.m_runWidthSoFar;
-    it.advance(runInfo.to, &glyphBuffer);
-
-    if (glyphBuffer.isEmpty())
-        return 0;
-
-    float afterWidth = it.m_runWidthSoFar;
-
-    if (runInfo.run.rtl()) {
-        it.advance(runInfo.run.length());
-        initialAdvance = it.m_runWidthSoFar - afterWidth;
-        glyphBuffer.reverse();
-    } else {
-        initialAdvance = beforeWidth;
-    }
-
-    return initialAdvance;
-}
-
-void Font::drawSimpleText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const FloatPoint& point) const
-{
-    // This glyph buffer holds our glyphs+advances+font data for each glyph.
-    GlyphBuffer glyphBuffer;
-    float initialAdvance = getGlyphsAndAdvancesForSimpleText(runInfo, glyphBuffer);
-    ASSERT(!glyphBuffer.hasVerticalAdvances());
-
-    if (glyphBuffer.isEmpty())
-        return;
-
-    TextBlobPtr textBlob;
-    if (RuntimeEnabledFeatures::textBlobEnabled()) {
-        // Using text blob causes a small difference in how gradients and
-        // patterns are rendered.
-        // FIXME: Fix this, most likely in Skia.
-        if (!context->strokeGradient() && !context->strokePattern() && !context->fillGradient() && !context->fillPattern()) {
-            FloatRect blobBounds = runInfo.bounds;
-            blobBounds.moveBy(-point);
-            textBlob = buildTextBlob(glyphBuffer, initialAdvance, blobBounds);
-        }
-    }
-
-    if (textBlob) {
-        drawTextBlob(context, textBlob.get(), point.data());
-    } else {
-        FloatPoint startPoint(point.x() + initialAdvance, point.y());
-        drawGlyphBuffer(context, runInfo, glyphBuffer, startPoint);
-    }
-}
-
-void Font::drawEmphasisMarksForSimpleText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const
-{
-    GlyphBuffer glyphBuffer;
-    float initialAdvance = getGlyphsAndAdvancesForSimpleText(runInfo, glyphBuffer, ForTextEmphasis);
-
-    if (glyphBuffer.isEmpty())
-        return;
-
-    drawEmphasisMarks(context, runInfo, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
-}
-
-void Font::drawGlyphBuffer(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const FloatPoint& point) const
+float Font::drawGlyphBuffer(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const FloatPoint& point) const
 {
     // Draw each contiguous run of glyphs that use the same font data.
     const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
     FloatPoint startPoint(point);
-    FloatPoint nextPoint = startPoint + glyphBuffer.advanceAt(0);
+    float advanceSoFar = 0;
     unsigned lastFrom = 0;
-    unsigned nextGlyph = 1;
+    unsigned nextGlyph = 0;
     while (nextGlyph < glyphBuffer.size()) {
         const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
-
         if (nextFontData != fontData) {
             drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint, runInfo.bounds);
-
             lastFrom = nextGlyph;
             fontData = nextFontData;
-            startPoint = nextPoint;
+            startPoint += FloatSize(advanceSoFar, 0);
+            advanceSoFar = 0;
         }
-        nextPoint += glyphBuffer.advanceAt(nextGlyph);
+        advanceSoFar += glyphBuffer.advanceAt(nextGlyph);
         nextGlyph++;
     }
-
     drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint, runInfo.bounds);
+    startPoint += FloatSize(advanceSoFar, 0);
+    return startPoint.x() - point.x();
 }
 
+
 inline static float offsetToMiddleOfGlyph(const SimpleFontData* fontData, Glyph glyph)
 {
     if (fontData->platformData().orientation() == Horizontal) {
@@ -712,7 +717,7 @@
 
 inline static float offsetToMiddleOfAdvanceAtIndex(const GlyphBuffer& glyphBuffer, size_t i)
 {
-    return glyphBuffer.advanceAt(i).width() / 2;
+    return glyphBuffer.advanceAt(i) / 2;
 }
 
 void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const
@@ -737,7 +742,7 @@
     GlyphBuffer markBuffer;
     for (unsigned i = 0; i + 1 < glyphBuffer.size(); ++i) {
         float middleOfNextGlyph = offsetToMiddleOfAdvanceAtIndex(glyphBuffer, i + 1);
-        float advance = glyphBuffer.advanceAt(i).width() - middleOfLastGlyph + middleOfNextGlyph;
+        float advance = glyphBuffer.advanceAt(i) - middleOfLastGlyph + middleOfNextGlyph;
         markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance);
         middleOfLastGlyph = middleOfNextGlyph;
     }
diff --git a/sky/engine/platform/fonts/Font.h b/sky/engine/platform/fonts/Font.h
index 213915f..f57afd0 100644
--- a/sky/engine/platform/fonts/Font.h
+++ b/sky/engine/platform/fonts/Font.h
@@ -100,7 +100,8 @@
     void update(PassRefPtr<FontSelector>) const;
 
     enum CustomFontNotReadyAction { DoNotPaintIfFontNotReady, UseFallbackIfFontNotReady };
-    void drawText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&, CustomFontNotReadyAction = DoNotPaintIfFontNotReady) const;
+    void drawText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&) const;
+    float drawUncachedText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&, CustomFontNotReadyAction) const;
     void drawEmphasisMarks(GraphicsContext*, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&) const;
 
     float width(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
@@ -136,12 +137,11 @@
     enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis };
 
     // Returns the initial in-stream advance.
-    float getGlyphsAndAdvancesForSimpleText(const TextRunPaintInfo&, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
-    void drawSimpleText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&) const;
-    void drawEmphasisMarksForSimpleText(GraphicsContext*, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&) const;
+    float buildGlyphBuffer(const TextRunPaintInfo&, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
+    PassTextBlobPtr buildTextBlob(const GlyphBuffer&, float initialAdvance, const FloatRect& bounds) const;
     void drawGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint&, const FloatRect& textRect) const;
-    void drawGlyphBuffer(GraphicsContext*, const TextRunPaintInfo&, const GlyphBuffer&, const FloatPoint&) const;
     void drawTextBlob(GraphicsContext*, const SkTextBlob*, const SkPoint& origin) const;
+    float drawGlyphBuffer(GraphicsContext*, const TextRunPaintInfo&, const GlyphBuffer&, const FloatPoint&) const;
     void drawEmphasisMarks(GraphicsContext*, const TextRunPaintInfo&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const;
     float floatWidthForSimpleText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts = 0, IntRectExtent* glyphBounds = 0) const;
     int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const;
@@ -149,10 +149,6 @@
 
     bool getEmphasisMarkGlyphData(const AtomicString&, GlyphData&) const;
 
-    // Returns the initial in-stream advance.
-    float getGlyphsAndAdvancesForComplexText(const TextRunPaintInfo&, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
-    void drawComplexText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&) const;
-    void drawEmphasisMarksForComplexText(GraphicsContext*, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&) const;
     float floatWidthForComplexText(const TextRun&, HashSet<const SimpleFontData*>* fallbackFonts, IntRectExtent* glyphBounds) const;
     int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const;
     FloatRect selectionRectForComplexText(const TextRun&, const FloatPoint&, int h, int from, int to) const;
@@ -183,8 +179,6 @@
         return m_fontFallbackList && m_fontFallbackList->shouldSkipDrawing();
     }
 
-    PassTextBlobPtr buildTextBlob(const GlyphBuffer&, float initialAdvance, const FloatRect& bounds) const;
-
     FontDescription m_fontDescription;
     mutable RefPtr<FontFallbackList> m_fontFallbackList;
 };
diff --git a/sky/engine/platform/fonts/GlyphBuffer.h b/sky/engine/platform/fonts/GlyphBuffer.h
index 7249213..c9e8a42 100644
--- a/sky/engine/platform/fonts/GlyphBuffer.h
+++ b/sky/engine/platform/fonts/GlyphBuffer.h
@@ -40,22 +40,23 @@
 
 class GlyphBuffer {
 public:
-    GlyphBuffer() : m_hasVerticalAdvances(false) { }
+    GlyphBuffer() { }
 
     bool isEmpty() const { return m_fontData.isEmpty(); }
+    bool hasOffsets() const { return !m_offsets.isEmpty(); }
     unsigned size() const { return m_fontData.size(); }
-    bool hasVerticalAdvances() const { return m_hasVerticalAdvances; }
 
     void clear()
     {
         m_fontData.clear();
         m_glyphs.clear();
         m_advances.clear();
-        m_hasVerticalAdvances = false;
+        m_offsets.clear();
     }
 
     const Glyph* glyphs(unsigned from) const { return m_glyphs.data() + from; }
-    const FloatSize* advances(unsigned from) const { return m_advances.data() + from; }
+    const float* advances(unsigned from) const { return m_advances.data() + from; }
+    const FloatSize* offsets(unsigned from) const { return m_offsets.data() + from; }
 
     const SimpleFontData* fontDataAt(unsigned index) const { return m_fontData[index]; }
 
@@ -64,25 +65,30 @@
         return m_glyphs[index];
     }
 
-    FloatSize advanceAt(unsigned index) const
+    float advanceAt(unsigned index) const
     {
         return m_advances[index];
     }
 
     void add(Glyph glyph, const SimpleFontData* font, float width)
     {
+        // should not mix offset/advance-only glyphs
+        ASSERT(!hasOffsets());
+
         m_fontData.append(font);
         m_glyphs.append(glyph);
-        m_advances.append(FloatSize(width, 0));
+        m_advances.append(width);
     }
 
-    void add(Glyph glyph, const SimpleFontData* font, const FloatSize& advance)
+    void add(Glyph glyph, const SimpleFontData* font, const FloatSize& offset, float advance)
     {
+        // should not mix offset/advance-only glyphs
+        ASSERT(size() == m_offsets.size());
+
         m_fontData.append(font);
         m_glyphs.append(glyph);
+        m_offsets.append(offset);
         m_advances.append(advance);
-        if (advance.height())
-            m_hasVerticalAdvances = true;
     }
 
     void reverse()
@@ -92,23 +98,18 @@
         m_advances.reverse();
     }
 
-    void setAdvanceWidth(unsigned index, float newWidth)
-    {
-        m_advances[index].setWidth(newWidth);
-    }
-
     void expandLastAdvance(float width)
     {
         ASSERT(!isEmpty());
-        FloatSize& lastAdvance = m_advances.last();
-        lastAdvance.setWidth(lastAdvance.width() + width);
+        float& lastAdvance = m_advances.last();
+        lastAdvance += width;
     }
 
 private:
     Vector<const SimpleFontData*, 2048> m_fontData;
     Vector<Glyph, 2048> m_glyphs;
-    Vector<FloatSize, 2048> m_advances;
-    bool m_hasVerticalAdvances;
+    Vector<float, 2048> m_advances;
+    Vector<FloatSize, 1024> m_offsets;
 };
 
 } // namespace blink
diff --git a/sky/engine/platform/fonts/GlyphBufferTest.cpp b/sky/engine/platform/fonts/GlyphBufferTest.cpp
deleted file mode 100644
index 37d3d45..0000000
--- a/sky/engine/platform/fonts/GlyphBufferTest.cpp
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "sky/engine/config.h"
-#include "sky/engine/platform/fonts/GlyphBuffer.h"
-
-#include <gtest/gtest.h>
-#include "sky/engine/platform/fonts/SimpleFontData.h"
-#include "sky/engine/wtf/PassRefPtr.h"
-#include "sky/engine/wtf/RefPtr.h"
-
-using namespace blink;
-
-namespace {
-
-// Minimal TestSimpleFontData implementation.
-// Font has no glyphs, but that's okay.
-class TestSimpleFontData : public SimpleFontData {
-public:
-    static PassRefPtr<TestSimpleFontData> create()
-    {
-        return adoptRef(new TestSimpleFontData);
-    }
-
-private:
-    TestSimpleFontData() : SimpleFontData(nullptr, 10, false, false) { }
-
-    bool fillGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength) const override
-    {
-        return false;
-    }
-};
-
-TEST(GlyphBufferTest, StartsEmpty)
-{
-    GlyphBuffer glyphBuffer;
-    EXPECT_TRUE(glyphBuffer.isEmpty());
-    EXPECT_EQ(0u, glyphBuffer.size());
-}
-
-TEST(GlyphBufferTest, StoresGlyphs)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-
-    EXPECT_EQ(42, glyphBuffer.glyphAt(0));
-    EXPECT_EQ(43, glyphBuffer.glyphAt(1));
-    EXPECT_EQ(44, glyphBuffer.glyphAt(2));
-
-    const Glyph* glyphs = glyphBuffer.glyphs(0);
-    EXPECT_EQ(42, glyphs[0]);
-    EXPECT_EQ(43, glyphs[1]);
-    EXPECT_EQ(44, glyphs[2]);
-}
-
-TEST(GlyphBufferTest, StoresAdvances)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-
-    EXPECT_EQ(FloatSize(10, 0), glyphBuffer.advanceAt(0));
-    EXPECT_EQ(FloatSize(15, 0), glyphBuffer.advanceAt(1));
-    EXPECT_EQ(FloatSize(12, 2), glyphBuffer.advanceAt(2));
-
-    const FloatSize* advances = glyphBuffer.advances(0);
-    EXPECT_EQ(FloatSize(10, 0), advances[0]);
-    EXPECT_EQ(FloatSize(15, 0), advances[1]);
-    EXPECT_EQ(FloatSize(12, 2), advances[2]);
-}
-
-TEST(GlyphBufferTest, StoresSimpleFontData)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-
-    EXPECT_EQ(font1.get(), glyphBuffer.fontDataAt(0));
-    EXPECT_EQ(font1.get(), glyphBuffer.fontDataAt(1));
-    EXPECT_EQ(font2.get(), glyphBuffer.fontDataAt(2));
-}
-
-TEST(GlyphBufferTest, GlyphArrayWithOffset)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-
-    const Glyph* glyphs = glyphBuffer.glyphs(1);
-    EXPECT_EQ(43, glyphs[0]);
-    EXPECT_EQ(44, glyphs[1]);
-}
-
-TEST(GlyphBufferTest, AdvanceArrayWithOffset)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-
-    const FloatSize* advances = glyphBuffer.advances(1);
-    EXPECT_EQ(FloatSize(15, 0), advances[0]);
-    EXPECT_EQ(FloatSize(12, 2), advances[1]);
-}
-
-TEST(GlyphBufferTest, Clear)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-
-    glyphBuffer.clear();
-
-    EXPECT_TRUE(glyphBuffer.isEmpty());
-    EXPECT_EQ(0u, glyphBuffer.size());
-}
-
-TEST(GlyphBufferTest, TracksVerticalAdvances)
-{
-    RefPtr<SimpleFontData> font = TestSimpleFontData::create();
-    GlyphBuffer glyphBuffer;
-    EXPECT_FALSE(glyphBuffer.hasVerticalAdvances());
-    glyphBuffer.add(42, font.get(), 10);
-    EXPECT_FALSE(glyphBuffer.hasVerticalAdvances());
-    glyphBuffer.add(43, font.get(), FloatSize(15, 0));
-    EXPECT_FALSE(glyphBuffer.hasVerticalAdvances());
-    glyphBuffer.add(44, font.get(), FloatSize(10, 5));
-    EXPECT_TRUE(glyphBuffer.hasVerticalAdvances());
-    glyphBuffer.clear();
-    EXPECT_FALSE(glyphBuffer.hasVerticalAdvances());
-}
-
-TEST(GlyphBufferTest, Reverse)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-    EXPECT_TRUE(glyphBuffer.hasVerticalAdvances());
-
-    glyphBuffer.reverse();
-
-    EXPECT_FALSE(glyphBuffer.isEmpty());
-    EXPECT_EQ(3u, glyphBuffer.size());
-    EXPECT_TRUE(glyphBuffer.hasVerticalAdvances());
-    EXPECT_EQ(44, glyphBuffer.glyphAt(0));
-    EXPECT_EQ(43, glyphBuffer.glyphAt(1));
-    EXPECT_EQ(42, glyphBuffer.glyphAt(2));
-    EXPECT_EQ(FloatSize(12, 2), glyphBuffer.advanceAt(0));
-    EXPECT_EQ(FloatSize(15, 0), glyphBuffer.advanceAt(1));
-    EXPECT_EQ(FloatSize(10, 0), glyphBuffer.advanceAt(2));
-    EXPECT_EQ(font2.get(), glyphBuffer.fontDataAt(0));
-    EXPECT_EQ(font1.get(), glyphBuffer.fontDataAt(1));
-    EXPECT_EQ(font1.get(), glyphBuffer.fontDataAt(2));
-}
-
-TEST(GlyphBufferTest, SetAdvanceWidth)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    glyphBuffer.setAdvanceWidth(1, 20);
-    EXPECT_EQ(FloatSize(20, 0), glyphBuffer.advanceAt(1));
-
-    glyphBuffer.setAdvanceWidth(2, 10);
-    EXPECT_EQ(FloatSize(10, 2), glyphBuffer.advanceAt(2));
-}
-
-TEST(GlyphBufferTest, ExpandLastAdvance)
-{
-    RefPtr<SimpleFontData> font1 = TestSimpleFontData::create();
-    RefPtr<SimpleFontData> font2 = TestSimpleFontData::create();
-
-    GlyphBuffer glyphBuffer;
-    glyphBuffer.add(42, font1.get(), 10);
-    glyphBuffer.add(43, font1.get(), 15);
-    glyphBuffer.add(44, font2.get(), FloatSize(12, 2));
-
-    glyphBuffer.expandLastAdvance(20);
-    EXPECT_EQ(FloatSize(32, 2), glyphBuffer.advanceAt(2));
-}
-
-} // namespace
diff --git a/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp b/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp
index 998b38b..af34cba 100644
--- a/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp
+++ b/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp
@@ -31,7 +31,7 @@
 #include "sky/engine/config.h"
 #include "sky/engine/platform/fonts/Font.h"
 
-#include "sky/engine/platform/NotImplemented.h"
+#include "gen/sky/platform/RuntimeEnabledFeatures.h"
 #include "sky/engine/platform/fonts/FontPlatformFeatures.h"
 #include "sky/engine/platform/fonts/GlyphBuffer.h"
 #include "sky/engine/platform/fonts/SimpleFontData.h"
@@ -157,7 +157,7 @@
                 pos[i].set(
                     x + SkIntToScalar(lroundf(translations[i].x())),
                     y + -SkIntToScalar(-lroundf(currentWidth - translations[i].y())));
-                currentWidth += glyphBuffer.advanceAt(from + glyphIndex).width();
+                currentWidth += glyphBuffer.advanceAt(from + glyphIndex);
             }
             horizontalOffset += currentWidth;
             paintGlyphs(gc, font, glyphs, chunkLength, pos, textRect);
@@ -167,13 +167,13 @@
         return;
     }
 
-    if (!glyphBuffer.hasVerticalAdvances()) {
+    if (!glyphBuffer.hasOffsets()) {
         SkAutoSTMalloc<64, SkScalar> storage(numGlyphs);
         SkScalar* xpos = storage.get();
-        const FloatSize* adv = glyphBuffer.advances(from);
+        const float* adv = glyphBuffer.advances(from);
         for (unsigned i = 0; i < numGlyphs; i++) {
             xpos[i] = x;
-            x += SkFloatToScalar(adv[i].width());
+            x += SkFloatToScalar(adv[i]);
         }
         const Glyph* glyphs = glyphBuffer.glyphs(from);
         paintGlyphsHorizontal(gc, font, glyphs, numGlyphs, xpos, SkFloatToScalar(y), textRect);
@@ -188,11 +188,14 @@
     // here.
     SkAutoSTMalloc<32, SkPoint> storage(numGlyphs);
     SkPoint* pos = storage.get();
-    const FloatSize* adv = glyphBuffer.advances(from);
+    const FloatSize* offsets = glyphBuffer.offsets(from);
+    const float* advances = glyphBuffer.advances(from);
+    SkScalar advanceSoFar = SkFloatToScalar(0);
     for (unsigned i = 0; i < numGlyphs; i++) {
-        pos[i].set(x, y);
-        x += SkFloatToScalar(adv[i].width());
-        y += SkFloatToScalar(adv[i].height());
+        pos[i].set(
+            x + SkFloatToScalar(offsets[i].width()) + advanceSoFar,
+            y + SkFloatToScalar(offsets[i].height()));
+        advanceSoFar += SkFloatToScalar(advances[i]);
     }
 
     const Glyph* glyphs = glyphBuffer.glyphs(from);
@@ -201,69 +204,24 @@
 
 void Font::drawTextBlob(GraphicsContext* gc, const SkTextBlob* blob, const SkPoint& origin) const
 {
+    ASSERT(RuntimeEnabledFeatures::textBlobEnabled());
+
     // FIXME: It would be good to move this to Font.cpp, if we're sure that none
     // of the things in FontMac's setupPaint need to apply here.
     // See also paintGlyphs.
     TextDrawingModeFlags textMode = gc->textDrawingMode();
 
-    if (textMode & TextModeFill) {
-        SkPaint paint = gc->fillPaint();
-        gc->adjustTextRenderMode(&paint);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        gc->drawTextBlob(blob, origin, paint);
-    }
+    if (textMode & TextModeFill)
+        gc->drawTextBlob(blob, origin, gc->fillPaint());
 
     if ((textMode & TextModeStroke) && gc->hasStroke()) {
         SkPaint paint = gc->strokePaint();
-        gc->adjustTextRenderMode(&paint);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         if (textMode & TextModeFill)
             paint.setLooper(0);
         gc->drawTextBlob(blob, origin, paint);
     }
 }
 
-void Font::drawComplexText(GraphicsContext* gc, const TextRunPaintInfo& runInfo, const FloatPoint& point) const
-{
-    if (!runInfo.run.length())
-        return;
-
-    TextDrawingModeFlags textMode = gc->textDrawingMode();
-    bool fill = textMode & TextModeFill;
-    bool stroke = (textMode & TextModeStroke) && gc->hasStroke();
-
-    if (!fill && !stroke)
-        return;
-
-    GlyphBuffer glyphBuffer;
-    HarfBuzzShaper shaper(this, runInfo.run);
-    shaper.setDrawRange(runInfo.from, runInfo.to);
-    if (!shaper.shape(&glyphBuffer) || glyphBuffer.isEmpty())
-        return;
-    FloatPoint adjustedPoint = shaper.adjustStartPoint(point);
-    drawGlyphBuffer(gc, runInfo, glyphBuffer, adjustedPoint);
-}
-
-void Font::drawEmphasisMarksForComplexText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const
-{
-    GlyphBuffer glyphBuffer;
-
-    float initialAdvance = getGlyphsAndAdvancesForComplexText(runInfo, glyphBuffer, ForTextEmphasis);
-
-    if (glyphBuffer.isEmpty())
-        return;
-
-    drawEmphasisMarks(context, runInfo, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
-}
-
-float Font::getGlyphsAndAdvancesForComplexText(const TextRunPaintInfo& runInfo, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
-{
-    HarfBuzzShaper shaper(this, runInfo.run, HarfBuzzShaper::ForTextEmphasis);
-    shaper.setDrawRange(runInfo.from, runInfo.to);
-    shaper.shape(&glyphBuffer);
-    return 0;
-}
-
 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, IntRectExtent* glyphBounds) const
 {
     HarfBuzzShaper shaper(this, run, HarfBuzzShaper::NotForTextEmphasis, fallbackFonts);
@@ -299,27 +257,23 @@
     return shaper.selectionRect(point, height, from, to);
 }
 
-PassTextBlobPtr Font::buildTextBlob(const GlyphBuffer& glyphBuffer, float initialAdvance, const FloatRect& bounds) const
+namespace {
+
+template <bool hasOffsets>
+bool buildTextBlobInternal(const GlyphBuffer& glyphBuffer, SkScalar initialAdvance, SkTextBlobBuilder& builder)
 {
-    // FIXME: Except for setupPaint, this is not specific to FontHarfBuzz.
-    // FIXME: Also implement the more general full-positioning path.
-    ASSERT(!glyphBuffer.hasVerticalAdvances());
-
-    SkTextBlobBuilder builder;
-    SkScalar x = SkFloatToScalar(initialAdvance);
-    SkRect skBounds = bounds;
-
+    SkScalar x = initialAdvance;
     unsigned i = 0;
     while (i < glyphBuffer.size()) {
         const SimpleFontData* fontData = glyphBuffer.fontDataAt(i);
 
         // FIXME: Handle vertical text.
         if (fontData->platformData().orientation() == Vertical)
-            return nullptr;
+            return false;
 
         // FIXME: Handle SVG fonts.
         if (fontData->isSVGFont())
-            return nullptr;
+            return false;
 
         // FIXME: FontPlatformData makes some decisions on the device scale
         // factor, which is found via the GraphicsContext. This should be fixed
@@ -333,19 +287,43 @@
             i++;
         unsigned count = i - start;
 
-        const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPosH(paint, count, 0, &skBounds);
+        const SkTextBlobBuilder::RunBuffer& buffer = hasOffsets ?
+            builder.allocRunPos(paint, count) :
+            builder.allocRunPosH(paint, count, 0);
 
         const uint16_t* glyphs = glyphBuffer.glyphs(start);
         std::copy(glyphs, glyphs + count, buffer.glyphs);
 
-        const FloatSize* advances = glyphBuffer.advances(start);
+        const float* advances = glyphBuffer.advances(start);
+        const FloatSize* offsets = glyphBuffer.offsets(start);
         for (unsigned j = 0; j < count; j++) {
-            buffer.pos[j] = x;
-            x += SkFloatToScalar(advances[j].width());
+            if (hasOffsets) {
+                const FloatSize& offset = offsets[j];
+                buffer.pos[2 * j] = x + offset.width();
+                buffer.pos[2 * j + 1] = offset.height();
+            } else {
+                buffer.pos[j] = x;
+            }
+            x += SkFloatToScalar(advances[j]);
         }
     }
-
-    return adoptRef(builder.build());
+    return true;
 }
 
+} // namespace
+
+PassTextBlobPtr Font::buildTextBlob(const GlyphBuffer& glyphBuffer, float initialAdvance, const FloatRect& bounds) const
+{
+    ASSERT(RuntimeEnabledFeatures::textBlobEnabled());
+
+    SkTextBlobBuilder builder;
+    SkScalar advance = SkFloatToScalar(initialAdvance);
+
+    bool success = glyphBuffer.hasOffsets() ?
+        buildTextBlobInternal<true>(glyphBuffer, advance, builder) :
+        buildTextBlobInternal<false>(glyphBuffer, advance, builder);
+    return success ? adoptRef(builder.build()) : nullptr;
+}
+
+
 } // namespace blink
diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp
index 34fc737..1a3da20 100644
--- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp
+++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp
@@ -278,7 +278,7 @@
 {
     m_glyphs[index] = glyphId;
     m_advances[index] = advance;
-    m_offsets[index] = FloatPoint(offsetX, offsetY);
+    m_offsets[index] = FloatSize(offsetX, offsetY);
 }
 
 int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX)
@@ -571,11 +571,6 @@
     return true;
 }
 
-FloatPoint HarfBuzzShaper::adjustStartPoint(const FloatPoint& point)
-{
-    return point + m_startOffset;
-}
-
 static inline int handleMultipleUChar(
     UChar32 character,
     unsigned clusterLength,
@@ -950,29 +945,35 @@
     m_totalWidth += currentRun->width();
 }
 
-void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, FloatPoint& firstOffsetOfNextRun)
+void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, float& carryAdvance)
 {
-    FloatPoint* offsets = currentRun->offsets();
+    FloatSize* offsets = currentRun->offsets();
     uint16_t* glyphs = currentRun->glyphs();
     float* advances = currentRun->advances();
     unsigned numGlyphs = currentRun->numGlyphs();
     uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes();
-    for (unsigned i = 0; i < numGlyphs; ++i) {
-        uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i];
-        FloatPoint& currentOffset = offsets[i];
-        FloatPoint& nextOffset = (i == numGlyphs - 1) ? firstOffsetOfNextRun : offsets[i + 1];
-        float glyphAdvanceX = advances[i] + nextOffset.x() - currentOffset.x();
-        float glyphAdvanceY = nextOffset.y() - currentOffset.y();
-        if (m_run.rtl()) {
-            if (currentCharacterIndex >= m_toIndex)
-                m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
-            else if (currentCharacterIndex >= m_fromIndex)
-                glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY));
-        } else {
-            if (currentCharacterIndex < m_fromIndex)
-                m_startOffset.move(glyphAdvanceX, glyphAdvanceY);
-            else if (currentCharacterIndex < m_toIndex)
-                glyphBuffer->add(glyphs[i], currentRun->fontData(), FloatSize(glyphAdvanceX, glyphAdvanceY));
+    FloatSize runStartOffset = FloatSize();
+    if (m_run.rtl()) {
+        for (unsigned i = 0; i < numGlyphs; ++i) {
+            uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i];
+            if (currentCharacterIndex >= m_toIndex) {
+                carryAdvance += advances[i];
+            } else if (currentCharacterIndex >= m_fromIndex) {
+                runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) ? FloatSize(carryAdvance, 0) : FloatSize(0, carryAdvance);
+                glyphBuffer->add(glyphs[i], currentRun->fontData(), runStartOffset + offsets[i], carryAdvance + advances[i]);
+                carryAdvance = 0;
+            }
+        }
+    } else {
+        for (unsigned i = 0; i < numGlyphs; ++i) {
+            uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i];
+            if (currentCharacterIndex < m_fromIndex) {
+                carryAdvance += advances[i];
+            } else if (currentCharacterIndex < m_toIndex) {
+                runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) ? FloatSize(carryAdvance, 0) : FloatSize(0, carryAdvance);
+                glyphBuffer->add(glyphs[i], currentRun->fontData(), runStartOffset + offsets[i], carryAdvance + advances[i]);
+                carryAdvance = 0;
+            }
         }
     }
 }
@@ -1034,33 +1035,30 @@
 bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer)
 {
     unsigned numRuns = m_harfBuzzRuns.size();
+    float carryAdvance = 0;
     if (m_run.rtl()) {
-        m_startOffset = m_harfBuzzRuns.last()->offsets()[0];
         for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) {
             HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
             if (!currentRun->hasGlyphToCharacterIndexes()) {
                 // FIXME: bug 337886, 359664
                 continue;
             }
-            FloatPoint firstOffsetOfNextRun = !runIndex ? FloatPoint() : m_harfBuzzRuns[runIndex - 1]->offsets()[0];
             if (m_forTextEmphasis == ForTextEmphasis)
                 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
             else
-                fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
+                fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, carryAdvance);
         }
     } else {
-        m_startOffset = m_harfBuzzRuns.first()->offsets()[0];
         for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) {
             HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get();
             if (!currentRun->hasGlyphToCharacterIndexes()) {
                 // FIXME: bug 337886, 359664
                 continue;
             }
-            FloatPoint firstOffsetOfNextRun = runIndex == numRuns - 1 ? FloatPoint() : m_harfBuzzRuns[runIndex + 1]->offsets()[0];
             if (m_forTextEmphasis == ForTextEmphasis)
                 fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun);
             else
-                fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, firstOffsetOfNextRun);
+                fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, carryAdvance);
         }
     }
     return glyphBuffer->size();
diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h
index 8e968a6..581737c 100644
--- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h
+++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h
@@ -60,7 +60,6 @@
 
     void setDrawRange(int from, int to);
     bool shape(GlyphBuffer* = 0);
-    FloatPoint adjustStartPoint(const FloatPoint&);
     float totalWidth() { return m_totalWidth; }
     int offsetForPosition(float targetX);
     FloatRect selectionRect(const FloatPoint&, int height, int from, int to);
@@ -90,7 +89,7 @@
         unsigned numGlyphs() const { return m_numGlyphs; }
         uint16_t* glyphs() { return &m_glyphs[0]; }
         float* advances() { return &m_advances[0]; }
-        FloatPoint* offsets() { return &m_offsets[0]; }
+        FloatSize* offsets() { return &m_offsets[0]; }
         bool hasGlyphToCharacterIndexes() const
         {
             return m_glyphToCharacterIndexes.size() > 0;
@@ -116,7 +115,7 @@
         Vector<uint16_t, 256> m_glyphs;
         Vector<float, 256> m_advances;
         Vector<uint16_t, 256> m_glyphToCharacterIndexes;
-        Vector<FloatPoint, 256> m_offsets;
+        Vector<FloatSize, 256> m_offsets;
         float m_width;
     };
 
@@ -130,7 +129,7 @@
     bool createHarfBuzzRuns();
     bool shapeHarfBuzzRuns();
     bool fillGlyphBuffer(GlyphBuffer*);
-    void fillGlyphBufferFromHarfBuzzRun(GlyphBuffer*, HarfBuzzRun*, FloatPoint& firstOffsetOfNextRun);
+    void fillGlyphBufferFromHarfBuzzRun(GlyphBuffer*, HarfBuzzRun*, float& carryAdvance);
     void fillGlyphBufferForTextEmphasis(GlyphBuffer*, HarfBuzzRun* currentRun);
     void setGlyphPositionsForHarfBuzzRun(HarfBuzzRun*, hb_buffer_t*);
     void addHarfBuzzRun(unsigned startCharacter, unsigned endCharacter, const SimpleFontData*, UScriptCode);
@@ -149,8 +148,6 @@
     Vector<hb_feature_t, 4> m_features;
     Vector<OwnPtr<HarfBuzzRun>, 16> m_harfBuzzRuns;
 
-    FloatPoint m_startOffset;
-
     int m_fromIndex;
     int m_toIndex;
 
diff --git a/sky/engine/platform/graphics/GraphicsContext.cpp b/sky/engine/platform/graphics/GraphicsContext.cpp
index a0a8870..8323246 100644
--- a/sky/engine/platform/graphics/GraphicsContext.cpp
+++ b/sky/engine/platform/graphics/GraphicsContext.cpp
@@ -918,12 +918,10 @@
 
         TextRunPaintInfo subrunInfo(subrun);
         subrunInfo.bounds = runInfo.bounds;
-        font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction);
+        float runWidth = font.drawUncachedText(this, subrunInfo, currPoint, customFontNotReadyAction);
 
         bidiRun = bidiRun->next();
-        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
-        if (bidiRun)
-            currPoint.move(font.width(subrun), 0);
+        currPoint.move(runWidth, 0);
     }
 
     bidiRuns.deleteRuns();
diff --git a/sky/engine/platform/text/TextRun.h b/sky/engine/platform/text/TextRun.h
index f6329ad..285ba5b 100644
--- a/sky/engine/platform/text/TextRun.h
+++ b/sky/engine/platform/text/TextRun.h
@@ -32,6 +32,8 @@
 #include "sky/engine/wtf/RefCounted.h"
 #include "sky/engine/wtf/text/WTFString.h"
 
+class SkTextBlob;
+
 namespace blink {
 
 class FloatPoint;
@@ -245,6 +247,7 @@
         : run(r)
         , from(0)
         , to(r.length())
+        , cachedTextBlob(nullptr)
     {
     }
 
@@ -252,6 +255,7 @@
     int from;
     int to;
     FloatRect bounds;
+    RefPtr<const SkTextBlob>* cachedTextBlob;
 };
 
 }