DirectWrite font host for skia.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 16 Aug 2012 16:13:40 +0000 (16:13 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 16 Aug 2012 16:13:40 +0000 (16:13 +0000)
https://codereview.appspot.com/5417063/

git-svn-id: http://skia.googlecode.com/svn/trunk@5128 2bbb7eff-a529-9590-31e7-b0007b416f81

14 files changed:
gyp/common.gypi
gyp/common_variables.gypi
gyp/ports.gyp
gyp/utils.gyp
include/utils/win/SkHRESULT.h
include/utils/win/SkTScopedComPtr.h
samplecode/SampleTextBox.cpp
src/core/SkAdvancedTypefaceMetrics.cpp
src/ports/SkFontHost_win_dw.cpp [new file with mode: 0644]
src/utils/SkFloatUtils.h [new file with mode: 0644]
src/utils/win/SkDWriteFontFileStream.cpp [new file with mode: 0644]
src/utils/win/SkDWriteFontFileStream.h [new file with mode: 0644]
src/utils/win/SkDWriteGeometrySink.cpp [new file with mode: 0644]
src/utils/win/SkDWriteGeometrySink.h [new file with mode: 0644]

index 4b558f8..08c266e 100644 (file)
           ],
         }
       ],
+      [ 'skia_directwrite', {
+          'defines': [
+            'SK_FONTHOST_WIN_DW',
+          ],
+      }],
       [ 'skia_mesa', {
         'defines': [
           'SK_MESA',
index fd2c7e7..247fe74 100644 (file)
@@ -70,6 +70,7 @@
       # Do not turn on 'skia_angle' - it is currently experimental
       'skia_angle%': 0,
       'skia_arch_type%': 'x86',
+      'skia_directwrite%': 0,
       'android_make_apk%': 1,
       'skia_gpu%': 1,
       'skia_static_initializers%': 1,
@@ -85,6 +86,7 @@
     'skia_angle%': '<(skia_angle)',
     'skia_arch_type%': '<(skia_arch_type)',
     'skia_arch_width%': '<(skia_arch_width)',
+    'skia_directwrite%': '<(skia_directwrite)',
     'android_make_apk%': '<(android_make_apk)',
     'skia_gpu%': '<(skia_gpu)',
     'skia_static_initializers%': '<(skia_static_initializers)',
 # tab-width:2
 # indent-tabs-mode:nil
 # End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
+# vim: set expandtab tabstop=2 shiftwidth=2:
\ No newline at end of file
index 3272ac9..bb55030 100644 (file)
@@ -24,6 +24,7 @@
         '../src/ports/SkFontDescriptor.cpp',
         '../src/ports/SkFontHost_sandbox_none.cpp',
         '../src/ports/SkFontHost_win.cpp',
+        '../src/ports/SkFontHost_win_dw.cpp',
         '../src/ports/SkGlobalInitialization_default.cpp',
         '../src/ports/SkThread_win.cpp',
 
         [ 'skia_os == "win"', {
           'include_dirs': [
             'config/win',
+            '../src/utils/win',
+          ],
+          'conditions': [
+            [ 'skia_directwrite', {
+                'sources!': [
+                  '../src/ports/SkFontHost_win.cpp',
+                ],
+              }, { # else !skia_directwrite
+                'sources!': [
+                  '../src/ports/SkFontHost_win_dw.cpp',
+                ],
+              }],
           ],
           'sources!': [ # these are used everywhere but windows
             '../src/ports/SkDebug_stdio.cpp',
index 8b919a3..b4b8785 100644 (file)
@@ -2,10 +2,11 @@
   'targets': [
     {
       'target_name': 'utils',
+      'dependencies': [
+        'core.gyp:core',
+      ],
       'type': 'static_library',
       'include_dirs': [
-        '../include/config',
-        '../include/core',
         '../include/effects',
         '../include/pipe',
         '../include/utils',
@@ -13,6 +14,7 @@
         '../include/utils/unix',
         '../include/utils/win',
         '../include/xml',
+        '../src/utils',
       ],
       'sources': [
         '../include/utils/SkBoundaryPatch.h',
@@ -45,6 +47,7 @@
         '../src/utils/SkCullPoints.cpp',
         '../src/utils/SkDeferredCanvas.cpp',
         '../src/utils/SkDumpCanvas.cpp',
+        '../src/utils/SkFloatUtils.h',
         '../src/utils/SkInterpolator.cpp',
         '../src/utils/SkLayer.cpp',
         '../src/utils/SkMatrix44.cpp',
         '../include/utils/win/SkIStream.h',
         '../include/utils/win/SkTScopedComPtr.h',
         '../src/utils/win/SkAutoCoInitialize.cpp',
+        '../src/utils/win/SkDWriteFontFileStream.cpp',
+        '../src/utils/win/SkDWriteFontFileStream.h',
+        '../src/utils/win/SkDWriteGeometrySink.cpp',
+        '../src/utils/win/SkDWriteGeometrySink.h',
         '../src/utils/win/SkHRESULT.cpp',
         '../src/utils/win/SkIStream.cpp',
         '../src/utils/win/SkWGL_win.cpp',
             '../include/utils/win/SkIStream.h',
             '../include/utils/win/SkTScopedComPtr.h',
             '../src/utils/win/SkAutoCoInitialize.cpp',
+            '../src/utils/win/SkDWriteFontFileStream.cpp',
+            '../src/utils/win/SkDWriteFontFileStream.h',
+            '../src/utils/win/SkDWriteGeometrySink.cpp',
+            '../src/utils/win/SkDWriteGeometrySink.h',
             '../src/utils/win/SkHRESULT.cpp',
             '../src/utils/win/SkIStream.cpp',
           ],
index ff596c7..9738f74 100644 (file)
@@ -36,6 +36,7 @@ If the HRESULT FAILED then the macro will return from the current function.
 In variants ending with 'M' the given message will be traced when FAILED.
 The HR variants will return the HRESULT when FAILED.
 The HRB variants will return false when FAILED.
+The HRN variants will return NULL when FAILED.
 The HRV variants will simply return when FAILED.
 */
 #define HR(ex) HR_GENERAL(ex, NULL, _hr)
@@ -44,6 +45,9 @@ The HRV variants will simply return when FAILED.
 #define HRB(ex) HR_GENERAL(ex, NULL, false)
 #define HRBM(ex, msg) HR_GENERAL(ex, msg, false)
 
+#define HRN(ex) HR_GENERAL(ex, NULL, NULL)
+#define HRNM(ex, msg) HR_GENERAL(ex, msg, NULL)
+
 #define HRV(ex) HR_GENERAL(ex, NULL, )
 #define HRVM(ex, msg) HR_GENERAL(ex, msg, )
 //@}
index b9be037..14aace5 100644 (file)
@@ -29,7 +29,7 @@ public:
     ~SkTScopedComPtr() {
         this->reset();
     }
-    T &operator*() const { return *fPtr; }
+    T &operator*() const { SkASSERT(fPtr != NULL); return *fPtr; }
     SkBlockComRef<T> *operator->() const {
         return static_cast<SkBlockComRef<T>*>(fPtr);
     }
index 5f37380..6b4f3f3 100644 (file)
@@ -30,7 +30,7 @@
 
 extern void skia_set_text_gamma(float blackGamma, float whiteGamma);
 
-#ifdef SK_BUILD_FOR_WIN
+#if defined(SK_BUILD_FOR_WIN) && !defined(SK_FONTHOST_WIN_DW)
 extern SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
 #endif
 
@@ -45,7 +45,7 @@ static const char gText[] =
 class TextBoxView : public SampleView {
 public:
        TextBoxView() {
-#ifdef SK_BUILD_FOR_WIN
+#if defined(SK_BUILD_FOR_WIN) && !defined(SK_FONTHOST_WIN_DW)
                LOGFONT lf;
                sk_bzero(&lf, sizeof(lf));
                lf.lfHeight = 9;
index a8a6083..c3cb3d8 100644 (file)
 
 SK_DEFINE_INST_COUNT(SkAdvancedTypefaceMetrics)
 
+#if defined(SK_BUILD_FOR_WIN) && defined(SK_FONTHOST_WIN_DW)
+#include <DWrite.h>
+#endif
+
 #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -253,13 +257,20 @@ SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
 
 // Make AdvanceMetric template functions available for linking with typename
 // WidthRange and VerticalAdvanceRange.
-#if defined(SK_BUILD_FOR_WIN)
+#if defined(SK_BUILD_FOR_WIN) && !defined(SK_FONTHOST_WIN_DW)
 template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
         HDC hdc,
         int num_glyphs,
         const uint32_t* subsetGlyphIDs,
         uint32_t subsetGlyphIDsLength,
         bool (*getAdvance)(HDC hdc, int gId, int16_t* data));
+#elif defined(SK_BUILD_FOR_WIN) && defined(SK_FONTHOST_WIN_DW)
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        IDWriteFontFace* fontFace,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(IDWriteFontFace* fontFace, int gId, int16_t* data));
 #elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
 template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
         FT_Face face,
diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp
new file mode 100644 (file)
index 0000000..c3a13a0
--- /dev/null
@@ -0,0 +1,1519 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#undef GetGlyphIndices
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkColorFilter.h"
+#include "SkDWriteFontFileStream.h"
+#include "SkDWriteGeometrySink.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFontHost.h"
+#include "SkGlyph.h"
+#include "SkHRESULT.h"
+#include "SkMaskGamma.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_hhea.h"
+#include "SkOTTable_OS_2.h"
+#include "SkOTTable_post.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTScopedComPtr.h"
+#include "SkThread.h"
+#include "SkTypeface_win.h"
+#include "SkTypefaceCache.h"
+#include "SkUtils.h"
+
+#include <dwrite.h>
+
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
+
+static bool isLCD(const SkScalerContext::Rec& rec) {
+    return SkMask::kLCD16_Format == rec.fMaskFormat ||
+           SkMask::kLCD32_Format == rec.fMaskFormat;
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+  // Zero means that we don't have any fallback fonts for this fontID.
+  // This function is implemented on Android, but doesn't have much
+  // meaning here.
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class DWriteOffscreen {
+public:
+    DWriteOffscreen() : fWidth(0), fHeight(0) {
+    }
+
+    void init(IDWriteFontFace* fontFace, const DWRITE_MATRIX& xform, FLOAT fontSize) {
+        fFontFace = fontFace;
+        fFontSize = fontSize;
+        fXform = xform;
+    }
+
+    const void* draw(const SkGlyph&, bool isBW);
+
+private:
+    uint16_t fWidth;
+    uint16_t fHeight;
+    IDWriteFontFace* fFontFace;
+    FLOAT fFontSize;
+    DWRITE_MATRIX fXform;
+    SkTDArray<uint8_t> fBits;
+};
+
+typedef HRESULT (WINAPI *DWriteCreateFactoryProc)(
+    __in DWRITE_FACTORY_TYPE factoryType,
+    __in REFIID iid,
+    __out IUnknown **factory
+);
+
+static HRESULT get_dwrite_factory(IDWriteFactory** factory) {
+    static IDWriteFactory* gDWriteFactory = NULL;
+
+    if (gDWriteFactory != NULL) {
+        *factory = gDWriteFactory;
+        return S_OK;
+    }
+
+    DWriteCreateFactoryProc dWriteCreateFactoryProc =
+        reinterpret_cast<DWriteCreateFactoryProc>(
+            GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory")
+        )
+    ;
+    
+    if (!dWriteCreateFactoryProc) {
+        return E_UNEXPECTED;
+    }
+
+    HRM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED,
+                                __uuidof(IDWriteFactory),
+                                reinterpret_cast<IUnknown**>(&gDWriteFactory)),
+        "Could not create DirectWrite factory.");
+    
+    *factory = gDWriteFactory;
+    return S_OK;
+}
+
+const void* DWriteOffscreen::draw(const SkGlyph& glyph, bool isBW) {
+    IDWriteFactory* factory;
+    HRNM(get_dwrite_factory(&factory), "Could not get factory.");
+
+    if (fWidth < glyph.fWidth || fHeight < glyph.fHeight) {
+        fWidth = SkMax32(fWidth, glyph.fWidth);
+        fHeight = SkMax32(fHeight, glyph.fHeight);
+
+        if (isBW) {
+            fBits.setCount(fWidth * fHeight);
+        } else {
+            fBits.setCount(fWidth * fHeight * 3);
+        }
+    }
+
+    // erase
+    memset(fBits.begin(), 0, fBits.count());
+
+    fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
+    fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
+
+    FLOAT advance = 0.0f;
+
+    UINT16 index = glyph.getGlyphID();
+
+    DWRITE_GLYPH_OFFSET offset;
+    offset.advanceOffset = 0.0f;
+    offset.ascenderOffset = 0.0f;
+
+    DWRITE_GLYPH_RUN run;
+    run.glyphCount = 1;
+    run.glyphAdvances = &advance;
+    run.fontFace = fFontFace;
+    run.fontEmSize = fFontSize;
+    run.bidiLevel = 0;
+    run.glyphIndices = &index;
+    run.isSideways = FALSE;
+    run.glyphOffsets = &offset;
+
+    DWRITE_RENDERING_MODE renderingMode;
+    DWRITE_TEXTURE_TYPE textureType;
+    if (isBW) {
+        renderingMode = DWRITE_RENDERING_MODE_ALIASED;
+        textureType = DWRITE_TEXTURE_ALIASED_1x1;
+    } else {
+        renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        textureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+    }
+    SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
+    HRNM(factory->CreateGlyphRunAnalysis(&run,
+                                         1.0f, // pixelsPerDip,
+                                         &fXform,
+                                         renderingMode,
+                                         DWRITE_MEASURING_MODE_NATURAL,
+                                         0.0f, // baselineOriginX,
+                                         0.0f, // baselineOriginY,
+                                         &glyphRunAnalysis),
+         "Could not create glyph run analysis.");
+    
+    //NOTE: this assumes that the glyph has already been measured
+    //with an exact same glyph run analysis.
+    RECT bbox;
+    bbox.left = glyph.fLeft;
+    bbox.top = glyph.fTop;
+    bbox.right = glyph.fLeft + glyph.fWidth;
+    bbox.bottom = glyph.fTop + glyph.fHeight;
+    HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
+                                              &bbox,
+                                              fBits.begin(),
+                                              fBits.count()),
+         "Could not draw mask.");
+    return fBits.begin();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkTypeface::Style get_style(IDWriteFont* font) {
+    int style = SkTypeface::kNormal;
+    DWRITE_FONT_WEIGHT weight = font->GetWeight();
+    if (DWRITE_FONT_WEIGHT_DEMI_BOLD <= weight) {
+        style |= SkTypeface::kBold;
+    }
+    DWRITE_FONT_STYLE angle = font->GetStyle();
+    if (DWRITE_FONT_STYLE_OBLIQUE == angle || DWRITE_FONT_STYLE_ITALIC == angle) {
+        style |= SkTypeface::kItalic;
+    }
+    return static_cast<SkTypeface::Style>(style);
+}
+
+class DWriteFontTypeface : public SkTypeface {
+private:
+    DWriteFontTypeface(SkTypeface::Style style, SkFontID fontID,
+                       IDWriteFontFace* fontFace,
+                       IDWriteFont* font,
+                       IDWriteFontFamily* fontFamily,
+                       IDWriteFontFileLoader* fontFileLoader = NULL,
+                       IDWriteFontCollectionLoader* fontCollectionLoader = NULL)
+        : SkTypeface(style, fontID, false)
+        , fDWriteFontCollectionLoader(fontCollectionLoader)
+        , fDWriteFontFileLoader(fontFileLoader)
+        , fDWriteFontFamily(fontFamily)
+        , fDWriteFont(font)
+        , fDWriteFontFace(fontFace) {
+
+        if (fontCollectionLoader != NULL) {
+            fontCollectionLoader->AddRef();
+        }
+        if (fontFileLoader != NULL) {
+            fontFileLoader->AddRef();
+        }
+        fontFamily->AddRef();
+        font->AddRef();
+        fontFace->AddRef();
+    }
+
+public:
+    SkTScopedComPtr<IDWriteFontCollectionLoader> fDWriteFontCollectionLoader;
+    SkTScopedComPtr<IDWriteFontFileLoader> fDWriteFontFileLoader;
+    SkTScopedComPtr<IDWriteFontFamily> fDWriteFontFamily;
+    SkTScopedComPtr<IDWriteFont> fDWriteFont;
+    SkTScopedComPtr<IDWriteFontFace> fDWriteFontFace;
+
+    static DWriteFontTypeface* Create(IDWriteFontFace* fontFace,
+                                      IDWriteFont* font,
+                                      IDWriteFontFamily* fontFamily,
+                                      IDWriteFontFileLoader* fontFileLoader = NULL,
+                                      IDWriteFontCollectionLoader* fontCollectionLoader = NULL) {
+        SkTypeface::Style style = get_style(font);
+        SkFontID fontID = SkTypefaceCache::NewFontID();
+        return SkNEW_ARGS(DWriteFontTypeface, (style, fontID, fontFace, font, fontFamily,
+                                               fontFileLoader, fontCollectionLoader));
+    }
+
+    ~DWriteFontTypeface() {
+        if (fDWriteFontCollectionLoader.get() == NULL) return;
+
+        IDWriteFactory* factory;
+        HRVM(get_dwrite_factory(&factory), "Could not get factory.");
+        HRV(factory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get()));
+        HRV(factory->UnregisterFontFileLoader(fDWriteFontFileLoader.get()));
+    }
+};
+
+class SkScalerContext_Windows : public SkScalerContext {
+public:
+    SkScalerContext_Windows(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Windows();
+
+protected:
+    virtual unsigned generateGlyphCount() SK_OVERRIDE;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+    virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateImage(const SkGlyph& glyph,
+                               SkMaskGamma::PreBlend* maskPreBlend) SK_OVERRIDE;
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
+                                     SkPaint::FontMetrics* mY) SK_OVERRIDE;
+
+private:
+    DWriteOffscreen fOffscreen;
+    DWRITE_MATRIX fXform;
+    SkAutoTUnref<DWriteFontTypeface> fTypeface;
+    int fGlyphCount;
+};
+
+#define SK_DWRITE_DEFAULT_FONT_NAMED 1
+#define SK_DWRITE_DEFAULT_FONT_MESSAGE 2
+#define SK_DWRITE_DEFAULT_FONT_THEME 3
+#define SK_DWRITE_DEFAULT_FONT_SHELLDLG 4
+#define SK_DWRITE_DEFAULT_FONT_GDI 5
+#define SK_DWRITE_DEFAULT_FONT_STRATEGY SK_DWRITE_DEFAULT_FONT_MESSAGE
+
+static HRESULT get_default_font(IDWriteFont** font) {
+    IDWriteFactory* factory;
+    HRM(get_dwrite_factory(&factory), "Could not get factory.");
+
+#if SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_NAMED
+    SkTScopedComPtr<IDWriteFontCollection> sysFonts;
+    HRM(factory->GetSystemFontCollection(&sysFonts, false),
+        "Could not get system font collection.");
+
+    UINT32 index;
+    BOOL exists;
+    //hr = sysFonts->FindFamilyName(L"Georgia", &index, &exists);
+    HRM(sysFonts->FindFamilyName(L"Microsoft Sans Serif", &index, &exists),
+        "Could not access family names.");
+
+    if (!exists) {
+        SkDEBUGF(("The hard coded font family does not exist."));
+        return E_UNEXPECTED;
+    }
+    
+    SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+    HRM(sysFonts->GetFontFamily(index, &fontFamily),
+        "Could not load the requested font family.");
+
+    HRM(fontFamily->GetFont(0, font), "Could not get first font from family.");
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_MESSAGE
+    SkTScopedComPtr<IDWriteGdiInterop> gdi;
+    HRM(factory->GetGdiInterop(&gdi), "Could not get GDI interop.");
+
+    NONCLIENTMETRICSW metrics;
+    metrics.cbSize = sizeof(metrics);
+    if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
+                                   sizeof(metrics),
+                                   &metrics,
+                                   0)) {
+        return E_UNEXPECTED;
+    }
+    HRM(gdi->CreateFontFromLOGFONT(&metrics.lfMessageFont, font),
+        "Could not create DWrite font from LOGFONT.");
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_THEME
+    //Theme body font?
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_SHELLDLG
+    //"MS Shell Dlg" or "MS Shell Dlg 2"?
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_GDI
+    //Never works.
+    SkTScopedComPtr<IDWriteGdiInterop> gdi;
+    HRM(factory->GetGdiInterop(&gdi), "Could not get GDI interop.");
+
+    static LOGFONTW gDefaultFont = {};
+    gDefaultFont.lfFaceName
+    HRM(gdi->CreateFontFromLOGFONT(&gDefaultFont, font),
+        "Could not create DWrite font from LOGFONT.";
+#endif
+    return S_OK;
+}
+
+static bool are_same(IUnknown* a, IUnknown* b) {
+    SkTScopedComPtr<IUnknown> iunkA;
+    if (FAILED(a->QueryInterface(&iunkA))) {
+        return false;
+    }
+
+    SkTScopedComPtr<IUnknown> iunkB;
+    if (FAILED(b->QueryInterface(&iunkB))) {
+        return false;
+    }
+
+    return iunkA.get() == iunkB.get();
+}
+static bool FindByDWriteFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
+    //Check to see if the two fonts are identical.
+    DWriteFontTypeface* dwFace = reinterpret_cast<DWriteFontTypeface*>(face);
+    IDWriteFont* dwFont = reinterpret_cast<IDWriteFont*>(ctx);
+    if (are_same(dwFace->fDWriteFont.get(), dwFont)) {
+        return true;
+    }
+
+    //Check if the two fonts share the same loader and have the same key.
+    SkTScopedComPtr<IDWriteFontFace> dwFaceFontFace;
+    SkTScopedComPtr<IDWriteFontFace> dwFontFace;
+    HRB(dwFace->fDWriteFont->CreateFontFace(&dwFaceFontFace));
+    HRB(dwFont->CreateFontFace(&dwFontFace));
+    if (are_same(dwFaceFontFace.get(), dwFontFace.get())) {
+        return true;
+    }
+
+    UINT32 dwFaceNumFiles;
+    UINT32 dwNumFiles;
+    HRB(dwFaceFontFace->GetFiles(&dwFaceNumFiles, NULL));
+    HRB(dwFontFace->GetFiles(&dwNumFiles, NULL));
+    if (dwFaceNumFiles != dwNumFiles) {
+        return false;
+    }
+
+    SkTScopedComPtr<IDWriteFontFile> dwFaceFontFile;
+    SkTScopedComPtr<IDWriteFontFile> dwFontFile;
+    HRB(dwFaceFontFace->GetFiles(&dwFaceNumFiles, &dwFaceFontFile));
+    HRB(dwFontFace->GetFiles(&dwNumFiles, &dwFontFile));
+
+    //for (each file) { //we currently only admit fonts from one file.
+    SkTScopedComPtr<IDWriteFontFileLoader> dwFaceFontFileLoader;
+    SkTScopedComPtr<IDWriteFontFileLoader> dwFontFileLoader;
+    HRB(dwFaceFontFile->GetLoader(&dwFaceFontFileLoader));
+    HRB(dwFontFile->GetLoader(&dwFontFileLoader));
+    if (!are_same(dwFaceFontFileLoader.get(), dwFontFileLoader.get())) {
+        return false;
+    }
+    //}
+
+    const void* dwFaceFontRefKey;
+    UINT32 dwFaceFontRefKeySize;
+    const void* dwFontRefKey;
+    UINT32 dwFontRefKeySize;
+    HRB(dwFaceFontFile->GetReferenceKey(&dwFaceFontRefKey, &dwFaceFontRefKeySize));
+    HRB(dwFontFile->GetReferenceKey(&dwFontRefKey, &dwFontRefKeySize));
+    if (dwFaceFontRefKeySize != dwFontRefKeySize) {
+        return false;
+    }
+    if (0 != memcmp(dwFaceFontRefKey, dwFontRefKey, dwFontRefKeySize)) {
+        return false;
+    }
+
+    //TODO: better means than comparing name strings?
+    //NOTE: .tfc and fake bold/italic will end up here.
+    SkTScopedComPtr<IDWriteFontFamily> dwFaceFontFamily;
+    SkTScopedComPtr<IDWriteFontFamily> dwFontFamily;
+    HRB(dwFace->fDWriteFont->GetFontFamily(&dwFaceFontFamily));
+    HRB(dwFont->GetFontFamily(&dwFontFamily));
+
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFaceFontFamilyNames;
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFaceFontNames;
+    HRB(dwFaceFontFamily->GetFamilyNames(&dwFaceFontFamilyNames));
+    HRB(dwFace->fDWriteFont->GetFaceNames(&dwFaceFontNames));
+
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFontFamilyNames;
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFontNames;
+    HRB(dwFontFamily->GetFamilyNames(&dwFontFamilyNames));
+    HRB(dwFont->GetFaceNames(&dwFontNames));
+
+    UINT32 dwFaceFontFamilyNameLength;
+    UINT32 dwFaceFontNameLength;
+    HRB(dwFaceFontFamilyNames->GetStringLength(0, &dwFaceFontFamilyNameLength));
+    HRB(dwFaceFontNames->GetStringLength(0, &dwFaceFontNameLength));
+
+    UINT32 dwFontFamilyNameLength;
+    UINT32 dwFontNameLength;
+    HRB(dwFontFamilyNames->GetStringLength(0, &dwFontFamilyNameLength));
+    HRB(dwFontNames->GetStringLength(0, &dwFontNameLength));
+
+    if (dwFaceFontFamilyNameLength != dwFontFamilyNameLength ||
+        dwFaceFontNameLength != dwFontNameLength)
+    {
+        return false;
+    }
+
+    SkTDArray<wchar_t> dwFaceFontFamilyNameChar(new wchar_t[dwFaceFontFamilyNameLength+1], dwFaceFontFamilyNameLength+1);
+    SkTDArray<wchar_t> dwFaceFontNameChar(new wchar_t[dwFaceFontNameLength+1], dwFaceFontNameLength+1);
+    HRB(dwFaceFontFamilyNames->GetString(0, dwFaceFontFamilyNameChar.begin(), dwFaceFontFamilyNameChar.count()));
+    HRB(dwFaceFontNames->GetString(0, dwFaceFontNameChar.begin(), dwFaceFontNameChar.count()));
+
+    SkTDArray<wchar_t> dwFontFamilyNameChar(new wchar_t[dwFontFamilyNameLength+1], dwFontFamilyNameLength+1);
+    SkTDArray<wchar_t> dwFontNameChar(new wchar_t[dwFontNameLength+1], dwFontNameLength+1);
+    HRB(dwFontFamilyNames->GetString(0, dwFontFamilyNameChar.begin(), dwFontFamilyNameChar.count()));
+    HRB(dwFontNames->GetString(0, dwFontNameChar.begin(), dwFontNameChar.count()));
+
+    return wcscmp(dwFaceFontFamilyNameChar.begin(), dwFontFamilyNameChar.begin()) == 0 &&
+           wcscmp(dwFaceFontNameChar.begin(), dwFontNameChar.begin()) == 0;
+}
+
+SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFontFace* fontFace,
+                                           IDWriteFont* font,
+                                           IDWriteFontFamily* fontFamily,
+                                           IDWriteFontFileLoader* fontFileLoader = NULL,
+                                           IDWriteFontCollectionLoader* fontCollectionLoader = NULL) {
+    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByDWriteFont, font);
+    if (face) {
+        face->ref();
+    } else {
+        face = DWriteFontTypeface::Create(fontFace, font, fontFamily,
+                                          fontFileLoader, fontCollectionLoader);
+        SkTypefaceCache::Add(face, get_style(font), fontCollectionLoader != NULL);
+    }
+    return face;
+}
+
+void SkDWriteFontFromTypeface(const SkTypeface* face, IDWriteFont** font) {
+    if (NULL == face) {
+        HRVM(get_default_font(font), "Could not get default font.");
+    } else {
+        *font = static_cast<const DWriteFontTypeface*>(face)->fDWriteFont.get();
+        (*font)->AddRef();
+    }
+}
+static DWriteFontTypeface* GetDWriteFontByID(SkFontID fontID) {
+    return static_cast<DWriteFontTypeface*>(SkTypefaceCache::FindByID(fontID));
+}
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
+        : SkScalerContext(desc)
+        , fGlyphCount(-1) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    fXform.m11 = SkScalarToFloat(fRec.fPost2x2[0][0]);
+    fXform.m12 = SkScalarToFloat(fRec.fPost2x2[1][0]);
+    fXform.m21 = SkScalarToFloat(fRec.fPost2x2[0][1]);
+    fXform.m22 = SkScalarToFloat(fRec.fPost2x2[1][1]);
+    fXform.dx = 0;
+    fXform.dy = 0;
+
+    fTypeface.reset(GetDWriteFontByID(fRec.fFontID));
+    fTypeface.get()->ref();
+
+    fOffscreen.init(fTypeface->fDWriteFontFace.get(), fXform, SkScalarToFloat(fRec.fTextSize));
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() {
+    if (fGlyphCount < 0) {
+        fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount();
+    }
+    return fGlyphCount;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+    uint16_t index = 0;
+    fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast<UINT32*>(&uni), 1, &index);
+    return index;
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+    //Delta is the difference between the right/left side bearing metric
+    //and where the right/left side bearing ends up after hinting.
+    //DirectWrite does not provide this information.
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+    
+    glyph->fAdvanceX = 0;
+    glyph->fAdvanceY = 0;
+
+    uint16_t glyphId = glyph->getGlyphID();
+    DWRITE_GLYPH_METRICS gm;
+    HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
+         "Could not get design metrics.");
+
+    DWRITE_FONT_METRICS dwfm;
+    fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
+
+    SkScalar advanceX = SkScalarMulDiv(fRec.fTextSize,
+                                       SkIntToScalar(gm.advanceWidth),
+                                       SkIntToScalar(dwfm.designUnitsPerEm));
+    
+    if (!(fRec.fFlags & kSubpixelPositioning_Flag)) {
+        advanceX = SkScalarRoundToScalar(advanceX);
+    }
+
+    SkVector vecs[1] = { { advanceX, 0 } };
+    SkMatrix mat;
+    mat.setAll(fRec.fPost2x2[0][0], fRec.fPost2x2[0][1], 0,
+               fRec.fPost2x2[1][0], fRec.fPost2x2[1][1], 0,
+               0, 0, SkScalarToPersp(SK_Scalar1));
+    mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
+
+    glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX);
+    glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+    glyph->fWidth = 0;
+
+    this->generateAdvance(glyph);
+
+    //Measure raster size.
+    fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
+    fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
+
+    FLOAT advance = 0;
+
+    UINT16 glyphId = glyph->getGlyphID();
+
+    DWRITE_GLYPH_OFFSET offset;
+    offset.advanceOffset = 0.0f;
+    offset.ascenderOffset = 0.0f;
+
+    DWRITE_GLYPH_RUN run;
+    run.glyphCount = 1;
+    run.glyphAdvances = &advance;
+    run.fontFace = fTypeface->fDWriteFontFace.get();
+    run.fontEmSize = SkScalarToFloat(fRec.fTextSize);
+    run.bidiLevel = 0;
+    run.glyphIndices = &glyphId;
+    run.isSideways = FALSE;
+    run.glyphOffsets = &offset;
+
+    IDWriteFactory* factory;
+    HRVM(get_dwrite_factory(&factory), "Could not get factory.");
+
+    const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+    DWRITE_RENDERING_MODE renderingMode;
+    DWRITE_TEXTURE_TYPE textureType;
+    if (isBW) {
+        renderingMode = DWRITE_RENDERING_MODE_ALIASED;
+        textureType = DWRITE_TEXTURE_ALIASED_1x1;
+    } else {
+        renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        textureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+    }
+
+    SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
+    HRVM(factory->CreateGlyphRunAnalysis(&run,
+                                         1.0f, // pixelsPerDip,
+                                         &fXform,
+                                         renderingMode,
+                                         DWRITE_MEASURING_MODE_NATURAL,
+                                         0.0f, // baselineOriginX,
+                                         0.0f, // baselineOriginY,
+                                         &glyphRunAnalysis),
+         "Could not create glyph run analysis.");
+    
+    RECT bbox;
+    HRVM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, &bbox),
+         "Could not get texture bounds.");
+
+    glyph->fWidth = SkToU16(bbox.right - bbox.left);
+    glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
+    glyph->fLeft = SkToS16(bbox.left);
+    glyph->fTop = SkToS16(bbox.top);
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx,
+                                                  SkPaint::FontMetrics* my) {
+    if (!(mx || my))
+      return;
+
+    DWRITE_FONT_METRICS dwfm;
+    fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
+
+    if (mx) {
+        mx->fTop = SkScalarMulDiv(-fRec.fTextSize,
+                                  SkIntToScalar(dwfm.ascent),
+                                  SkIntToScalar(dwfm.designUnitsPerEm));
+        mx->fAscent = mx->fTop;
+        mx->fDescent = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.descent),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+        mx->fBottom = mx->fDescent;
+        //TODO, can be less than zero
+        mx->fLeading = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.lineGap),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+    }
+
+    if (my) {
+        my->fTop = SkScalarMulDiv(-fRec.fTextSize,
+                                  SkIntToScalar(dwfm.ascent),
+                                  SkIntToScalar(dwfm.designUnitsPerEm));
+        my->fAscent = my->fTop;
+        my->fDescent = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.descent),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+        my->fBottom = my->fDescent;
+        //TODO, can be less than zero
+        my->fLeading = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.lineGap),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+
+static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) {
+    const int width = glyph.fWidth;
+    const size_t dstRB = (width + 7) >> 3;
+    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
+
+    int byteCount = width >> 3;
+    int bitCount = width & 7;
+
+    for (int y = 0; y < glyph.fHeight; ++y) {
+        if (byteCount > 0) {
+            for (int i = 0; i < byteCount; ++i) {
+                unsigned byte = 0;
+                byte |= src[0] & (1 << 7);
+                byte |= src[1] & (1 << 6);
+                byte |= src[2] & (1 << 5);
+                byte |= src[3] & (1 << 4);
+                byte |= src[4] & (1 << 3);
+                byte |= src[5] & (1 << 2);
+                byte |= src[6] & (1 << 1);
+                byte |= src[7] & (1 << 0);
+                dst[i] = byte;
+                src += 8;
+            }
+        }
+        if (bitCount > 0) {
+            unsigned byte = 0;
+            unsigned mask = 0x80;
+            for (int i = 0; i < bitCount; i++) {
+                byte |= (src[i]) & mask;
+                mask >>= 1;
+            }
+            dst[byteCount] = byte;
+        }
+        src += bitCount;
+        dst += dstRB;
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
+    const size_t dstRB = glyph.rowBytes();
+    const U16CPU width = glyph.fWidth;
+    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
+
+    for (U16CPU y = 0; y < glyph.fHeight; y++) {
+        for (U16CPU i = 0; i < width; i++) {
+            U8CPU r = *(src++);
+            U8CPU g = *(src++);
+            U8CPU b = *(src++);
+            dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
+        }
+        dst = (uint8_t*)((char*)dst + dstRB);
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+    const size_t dstRB = glyph.rowBytes();
+    const U16CPU width = glyph.fWidth;
+    uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage);
+
+    for (U16CPU y = 0; y < glyph.fHeight; y++) {
+        for (U16CPU i = 0; i < width; i++) {
+            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
+            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
+            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
+            dst[i] = SkPack888ToRGB16(r, g, b);
+        }
+        dst = (uint16_t*)((char*)dst + dstRB);
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+    const size_t dstRB = glyph.rowBytes();
+    const U16CPU width = glyph.fWidth;
+    SkPMColor* SK_RESTRICT dst = static_cast<SkPMColor*>(glyph.fImage);
+
+    for (U16CPU y = 0; y < glyph.fHeight; y++) {
+        for (U16CPU i = 0; i < width; i++) {
+            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
+            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
+            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
+            dst[i] = SkPackARGB32(0xFF, r, g, b);
+        }
+        dst = (SkPMColor*)((char*)dst + dstRB);
+    }
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph,
+                                            SkMaskGamma::PreBlend* maskPreBlend) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    //Must be careful not to use these if maskPreBlend == NULL
+    const uint8_t* tableR = NULL;
+    const uint8_t* tableG = NULL;
+    const uint8_t* tableB = NULL;
+    if (maskPreBlend) {
+        tableR = maskPreBlend->fR;
+        tableG = maskPreBlend->fG;
+        tableB = maskPreBlend->fB;
+    }
+
+    const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+    const bool isAA = !isLCD(fRec);
+
+    //Create the mask.
+    const void* bits = fOffscreen.draw(glyph, isBW);
+    if (!bits) {
+        sk_bzero(glyph.fImage, glyph.computeImageSize());
+        return;
+    }
+
+    //Copy the mask into the glyph.
+    int width = glyph.fWidth;
+    size_t dstRB = glyph.rowBytes();
+    const uint8_t* src = (const uint8_t*)bits;
+    if (isBW) {
+        bilevel_to_bw(src, glyph);
+    } else if (isAA) {
+        if (maskPreBlend) {
+            rgb_to_a8<true>(src, glyph, tableG);
+        } else {
+            rgb_to_a8<false>(src, glyph, tableG);
+        }
+    } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+        if (maskPreBlend) {
+            rgb_to_lcd16<true>(src, glyph, tableR, tableG, tableB);
+        } else {
+            rgb_to_lcd16<false>(src, glyph, tableR, tableG, tableB);
+        }
+    } else {
+        SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+        if (maskPreBlend) {
+            rgb_to_lcd32<true>(src, glyph, tableR, tableG, tableB);
+        } else {
+            rgb_to_lcd32<true>(src, glyph, tableR, tableG, tableB);
+        }
+    }
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    SkASSERT(&glyph && path);
+
+    path->reset();
+
+    SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
+    HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath),
+         "Could not create geometry to path converter.");
+    uint16_t glyphId = glyph.getGlyphID();
+    //TODO: convert to<->from DIUs? This would make a difference if hinting.
+    //It may not be needed, it appears that DirectWrite only hints at em size.
+    HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fRec.fTextSize),
+                                       &glyphId,
+                                       NULL, //advances
+                                       NULL, //offsets
+                                       1, //num glyphs
+                                       FALSE, //sideways
+                                       FALSE, //rtl
+                                       geometryToPath.get()),
+         "Could not create glyph outline.");
+}
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkASSERT(!"SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+class StreamFontFileLoader : public IDWriteFontFileLoader {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontFileLoader methods
+    virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(
+        void const* fontFileReferenceKey,
+        UINT32 fontFileReferenceKeySize,
+        IDWriteFontFileStream** fontFileStream);
+
+    static HRESULT Create(SkStream* stream, StreamFontFileLoader** streamFontFileLoader) {
+        *streamFontFileLoader = new StreamFontFileLoader(stream);
+        if (NULL == streamFontFileLoader) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontFileLoader(SkStream* stream) : fRefCount(1), fStream(stream) {
+        stream->ref();
+    }
+
+    ULONG fRefCount;
+    SkAutoTUnref<SkStream> fStream;
+};
+
+HRESULT StreamFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG StreamFontFileLoader::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG StreamFontFileLoader::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT StreamFontFileLoader::CreateStreamFromKey(
+    void const* fontFileReferenceKey,
+    UINT32 fontFileReferenceKeySize,
+    IDWriteFontFileStream** fontFileStream)
+{
+    SkTScopedComPtr<SkDWriteFontFileStreamWrapper> stream;
+    HR(SkDWriteFontFileStreamWrapper::Create(fStream, &stream));
+    *fontFileStream = stream.release();
+    return S_OK;
+}
+
+class StreamFontFileEnumerator : public IDWriteFontFileEnumerator {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontFileEnumerator methods
+    virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasCurrentFile);
+    virtual HRESULT STDMETHODCALLTYPE GetCurrentFontFile(IDWriteFontFile** fontFile);
+
+    static HRESULT Create(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader,
+                          StreamFontFileEnumerator** streamFontFileEnumerator) {
+        *streamFontFileEnumerator = new StreamFontFileEnumerator(factory, fontFileLoader);
+        if (NULL == streamFontFileEnumerator) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontFileEnumerator(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader);
+    ULONG fRefCount;
+
+    SkTScopedComPtr<IDWriteFactory> fFactory;
+    SkTScopedComPtr<IDWriteFontFile> fCurrentFile;
+    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
+    bool fHasNext;
+};
+
+StreamFontFileEnumerator::StreamFontFileEnumerator(IDWriteFactory* factory,
+                                                   IDWriteFontFileLoader* fontFileLoader)
+    : fRefCount(1)
+    , fFactory(factory)
+    , fCurrentFile()
+    , fFontFileLoader(fontFileLoader)
+    , fHasNext(true)
+{
+    factory->AddRef();
+    fontFileLoader->AddRef();
+}
+
+HRESULT StreamFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG StreamFontFileEnumerator::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG StreamFontFileEnumerator::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT StreamFontFileEnumerator::MoveNext(BOOL* hasCurrentFile) {
+    *hasCurrentFile = FALSE;
+
+    if (!fHasNext) {
+        return S_OK;
+    }
+    fHasNext = false;
+
+    UINT32 dummy = 0;
+    HR(fFactory->CreateCustomFontFileReference(
+            &dummy, //cannot be NULL
+            sizeof(dummy), //even if this is 0
+            fFontFileLoader.get(),
+            &fCurrentFile));
+
+    *hasCurrentFile = TRUE;
+    return S_OK;
+}
+
+HRESULT StreamFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** fontFile) {
+    if (fCurrentFile.get() == NULL) {
+        *fontFile = NULL;
+        return E_FAIL;
+    }
+
+    fCurrentFile.get()->AddRef();
+    *fontFile = fCurrentFile.get();
+    return  S_OK;
+}
+
+class StreamFontCollectionLoader : public IDWriteFontCollectionLoader {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontCollectionLoader methods
+    virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey(
+        IDWriteFactory* factory,
+        void const* collectionKey,
+        UINT32 collectionKeySize,
+        IDWriteFontFileEnumerator** fontFileEnumerator);
+
+    static HRESULT Create(IDWriteFontFileLoader* fontFileLoader,
+                          StreamFontCollectionLoader** streamFontCollectionLoader) {
+        *streamFontCollectionLoader = new StreamFontCollectionLoader(fontFileLoader);
+        if (NULL == streamFontCollectionLoader) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontCollectionLoader(IDWriteFontFileLoader* fontFileLoader)
+        : fRefCount(1)
+        , fFontFileLoader(fontFileLoader)
+    {
+        fontFileLoader->AddRef();
+    }
+
+    ULONG fRefCount;
+    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
+};
+
+HRESULT StreamFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG StreamFontCollectionLoader::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG StreamFontCollectionLoader::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT StreamFontCollectionLoader::CreateEnumeratorFromKey(
+    IDWriteFactory* factory,
+    void const* collectionKey,
+    UINT32 collectionKeySize,
+    IDWriteFontFileEnumerator** fontFileEnumerator)
+{
+    SkTScopedComPtr<StreamFontFileEnumerator> enumerator;
+    HR(StreamFontFileEnumerator::Create(factory, fFontFileLoader.get(), &enumerator));
+    *fontFileEnumerator = enumerator.release();
+    return S_OK;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    IDWriteFactory* factory;
+    HRN(get_dwrite_factory(&factory));
+
+    SkTScopedComPtr<StreamFontFileLoader> fontFileLoader;
+    HRN(StreamFontFileLoader::Create(stream, &fontFileLoader));
+    HRN(factory->RegisterFontFileLoader(fontFileLoader.get()));
+
+    SkTScopedComPtr<StreamFontCollectionLoader> streamFontCollectionLoader;
+    HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &streamFontCollectionLoader));
+    HRN(factory->RegisterFontCollectionLoader(streamFontCollectionLoader.get()));
+
+    SkTScopedComPtr<IDWriteFontCollection> streamFontCollection;
+    HRN(factory->CreateCustomFontCollection(streamFontCollectionLoader.get(), NULL, 0,
+                                            &streamFontCollection));
+
+    SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+    HRN(streamFontCollection->GetFontFamily(0, &fontFamily));
+
+    SkTScopedComPtr<IDWriteFont> font;
+    HRN(fontFamily->GetFont(0, &font));
+
+    SkTScopedComPtr<IDWriteFontFace> fontFace;
+    HRN(font->CreateFontFace(&fontFace));
+
+    return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get(),
+                                          fontFileLoader.get(), streamFontCollectionLoader.get());
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+    DWriteFontTypeface* typeface = GetDWriteFontByID(uniqueID);
+    if (NULL == typeface) {
+        return NULL;
+    }
+
+    UINT32 numFiles;
+    HRNM(typeface->fDWriteFontFace->GetFiles(&numFiles, NULL),
+         "Could not get number of font files.");
+    if (numFiles != 1) {
+        return NULL;
+    }
+
+    SkTScopedComPtr<IDWriteFontFile> fontFile;
+    HRNM(typeface->fDWriteFontFace->GetFiles(&numFiles, &fontFile), "Could not get font files.");
+
+    const void* fontFileKey;
+    UINT32 fontFileKeySize;
+    HRNM(fontFile->GetReferenceKey(&fontFileKey, &fontFileKeySize),
+         "Could not get font file reference key.");
+
+    SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
+    HRNM(fontFile->GetLoader(&fontFileLoader), "Could not get font file loader.");
+
+    SkTScopedComPtr<IDWriteFontFileStream> fontFileStream;
+    HRNM(fontFileLoader->CreateStreamFromKey(fontFileKey, fontFileKeySize,
+                                             &fontFileStream),
+         "Could not create font file stream.");
+
+    return SkNEW_ARGS(SkDWriteFontFileStream, (fontFileStream.get()));
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+static HRESULT get_by_family_name(const char familyName[], IDWriteFontFamily** fontFamily) {
+    IDWriteFactory* factory;
+    HR(get_dwrite_factory(&factory));
+
+    SkTScopedComPtr<IDWriteFontCollection> sysFontCollection;
+    HR(factory->GetSystemFontCollection(&sysFontCollection, FALSE));
+
+    // Get the buffer size needed first.
+    int wlen = ::MultiByteToWideChar(CP_UTF8, 0, familyName,-1, NULL, 0);
+    if (0 == wlen) {
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+    // Allocate a buffer
+    SkTDArray<wchar_t> wideFamilyName(new wchar_t[wlen], wlen);
+    // Now actually convert the string.
+    wlen = ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
+                                    wideFamilyName.begin(), wlen);
+    if (0 == wlen) {
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    UINT32 index;
+    BOOL exists;
+    HR(sysFontCollection->FindFamilyName(wideFamilyName.begin(), &index, &exists));
+
+    if (exists) {
+        HR(sysFontCollection->GetFontFamily(index, fontFamily));
+        return S_OK;
+    }
+    return S_FALSE;
+}
+
+/** Return the closest matching typeface given either an existing family
+ (specified by a typeface in that family) or by a familyName, and a
+ requested style.
+ 1) If familyFace is null, use familyName.
+ 2) If familyName is null, use familyFace.
+ 3) If both are null, return the default font that best matches style
+ This MUST not return NULL.
+ */
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                       const char familyName[],
+                                       SkTypeface::Style style) {
+    HRESULT hr;
+    SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+    SkTScopedComPtr<IDWriteFontCollectionLoader> fontCollectionLoader;
+    SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
+    if (familyFace) {
+        const DWriteFontTypeface* face = static_cast<const DWriteFontTypeface*>(familyFace);
+        face->fDWriteFontFamily.get()->AddRef();
+        *(&fontFamily) = face->fDWriteFontFamily.get();
+
+        if (face->fDWriteFontCollectionLoader.get() != NULL) {
+            face->fDWriteFontCollectionLoader.get()->AddRef();
+            *(&fontCollectionLoader) = face->fDWriteFontCollectionLoader.get();
+
+            face->fDWriteFontFileLoader.get()->AddRef();
+            *(&fontFileLoader) = face->fDWriteFontFileLoader.get();
+        }
+
+    } else if (familyName) {
+        hr = get_by_family_name(familyName, &fontFamily);
+    }
+
+    if (NULL == fontFamily.get()) {
+        //No good family found, go with default.
+        SkTScopedComPtr<IDWriteFont> font;
+        hr = get_default_font(&font);
+        hr = font->GetFontFamily(&fontFamily);
+    }
+
+    SkTScopedComPtr<IDWriteFont> font;
+    DWRITE_FONT_WEIGHT weight = (style & SkTypeface::kBold)
+                                 ? DWRITE_FONT_WEIGHT_BOLD
+                                 : DWRITE_FONT_WEIGHT_NORMAL;
+    DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_UNDEFINED;
+    DWRITE_FONT_STYLE italic = (style & SkTypeface::kItalic)
+                                ? DWRITE_FONT_STYLE_ITALIC
+                                : DWRITE_FONT_STYLE_NORMAL;
+    hr = fontFamily->GetFirstMatchingFont(weight, stretch, italic, &font);
+
+    SkTScopedComPtr<IDWriteFontFace> fontFace;
+    hr = font->CreateFontFace(&fontFace);
+
+    return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get(),
+                                          fontFileLoader.get(), fontCollectionLoader.get());
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    printf("SkFontHost::CreateTypefaceFromFile unimplemented");
+    return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+    unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
+                                  SkScalerContext::kAutohinting_Flag |
+                                  SkScalerContext::kEmbeddedBitmapText_Flag |
+                                  SkScalerContext::kEmbolden_Flag |
+                                  SkScalerContext::kLCD_BGROrder_Flag |
+                                  SkScalerContext::kLCD_Vertical_Flag;
+    rec->fFlags &= ~flagsWeDontSupport;
+
+    SkPaint::Hinting h = rec->getHinting();
+    // DirectWrite does not provide for hinting hints.
+    h = SkPaint::kSlight_Hinting;
+    rec->setHinting(h);
+
+#if SK_FONT_HOST_USE_SYSTEM_SETTINGS
+    IDWriteFactory* factory;
+    if (SUCCEEDED(get_dwrite_factory(&factory))) {
+        SkTScopedComPtr<IDWriteRenderingParams> defaultRenderingParams;
+        if (SUCCEEDED(factory->CreateRenderingParams(&defaultRenderingParams))) {
+            float gamma = defaultRenderingParams->GetGamma();
+            rec->setDeviceGamma(SkFloatToScalar(gamma));
+            rec->setPaintGamma(SkFloatToScalar(gamma));
+            
+            rec->setContrast(SkFloatToScalar(defaultRenderingParams->GetEnhancedContrast()));
+        }
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//PDF Support
+
+using namespace skia_advanced_typeface_metrics_utils;
+
+// Construct Glyph to Unicode table.
+// Unicode code points that require conjugate pairs in utf16 are not
+// supported.
+// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
+// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
+// of calling GetFontUnicodeRange().
+// TODO(bungeman): This never does what anyone wants.
+// What is really wanted is the text to glyphs mapping
+static void populate_glyph_to_unicode(IDWriteFontFace* fontFace,
+                                      const unsigned glyphCount,
+                                      SkTDArray<SkUnichar>* glyphToUnicode) {
+    HRESULT hr = S_OK;
+
+    //Do this like free type instead
+    UINT32 count = 0;
+    for (UINT32 c = 0; c < 0x10FFFF; ++c) {
+        UINT16 glyph;
+        hr = fontFace->GetGlyphIndices(&c, 1, &glyph);
+        if (glyph > 0) {
+            ++count;
+        }
+    }
+
+    SkAutoTArray<UINT32> chars(count);
+    count = 0;
+    for (UINT32 c = 0; c < 0x10FFFF; ++c) {
+        UINT16 glyph;
+        hr = fontFace->GetGlyphIndices(&c, 1, &glyph);
+        if (glyph > 0) {
+            chars[count] = c;
+            ++count;
+        }
+    }
+
+    SkAutoTArray<UINT16> glyph(count);
+    fontFace->GetGlyphIndices(chars.get(), count, glyph.get());
+
+    USHORT maxGlyph = 0;
+    for (USHORT j = 0; j < count; ++j) {
+        if (glyph[j] > maxGlyph) maxGlyph = glyph[j];
+    }
+
+    glyphToUnicode->setCount(maxGlyph+1);
+    for (size_t j = 0; j < maxGlyph+1u; ++j) {
+        (*glyphToUnicode)[j] = 0;
+    }
+
+    //'invert'
+    for (USHORT j = 0; j < count; ++j) {
+        if (glyph[j] < glyphCount && (*glyphToUnicode)[glyph[j]] == 0) {
+            (*glyphToUnicode)[glyph[j]] = chars[j];
+        }
+    }
+}
+
+static bool getWidthAdvance(IDWriteFontFace* fontFace, int gId, int16_t* advance) {
+    SkASSERT(advance);
+
+    UINT16 glyphId = gId;
+    DWRITE_GLYPH_METRICS gm;
+    HRESULT hr = fontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm);
+
+    if (FAILED(hr)) {
+        *advance = 0;
+        return false;
+    }
+
+    *advance = gm.advanceWidth;
+    return true;
+}
+
+template<typename T>
+class AutoDWriteTable {
+public:
+    AutoDWriteTable(IDWriteFontFace* fontFace)
+        : fFontFace(fontFace)
+        , fExists(FALSE) {
+
+        //fontFace->AddRef();
+        const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0,
+                                                    T::TAG1,
+                                                    T::TAG2,
+                                                    T::TAG3);
+        HRESULT hr = fontFace->TryGetFontTable(tag,
+            reinterpret_cast<const void **>(&fData), &fSize, &fLock, &fExists);
+    }
+    ~AutoDWriteTable() {
+        if (fExists) {
+            fFontFace->ReleaseFontTable(fLock);
+        }
+    }
+    const T* operator->() const { return fData; }
+
+    const T* fData;
+    UINT32 fSize;
+    BOOL fExists;
+private:
+    //SkTScopedComPtr<IDWriteFontFace> fFontFace;
+    IDWriteFontFace* fFontFace;
+    void* fLock;
+};
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+
+    SkAdvancedTypefaceMetrics* info = NULL;
+
+    HRESULT hr = S_OK;
+
+    DWriteFontTypeface* typeface = GetDWriteFontByID(fontID);
+
+    const unsigned glyphCount = typeface->fDWriteFontFace->GetGlyphCount();
+
+    DWRITE_FONT_METRICS dwfm;
+    typeface->fDWriteFontFace->GetMetrics(&dwfm);
+
+    info = new SkAdvancedTypefaceMetrics;
+    info->fEmSize = dwfm.designUnitsPerEm;
+    info->fMultiMaster = false;
+    info->fLastGlyphID = SkToU16(glyphCount - 1);
+    info->fStyle = 0;
+
+
+    SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
+    SkTScopedComPtr<IDWriteLocalizedStrings> faceNames;
+    hr = typeface->fDWriteFontFamily->GetFamilyNames(&familyNames);
+    hr = typeface->fDWriteFont->GetFaceNames(&faceNames);
+
+    UINT32 familyNameLength;
+    hr = familyNames->GetStringLength(0, &familyNameLength);
+
+    UINT32 faceNameLength;
+    hr = faceNames->GetStringLength(0, &faceNameLength);
+
+    size_t size = familyNameLength+1+faceNameLength+1;
+    SkTDArray<wchar_t> wFamilyName(new wchar_t[size], size);
+    hr = familyNames->GetString(0, wFamilyName.begin(), size);
+    wFamilyName[familyNameLength] = L' ';
+    hr = faceNames->GetString(0, &wFamilyName[familyNameLength+1], size - faceNameLength + 1);
+
+    size_t str_len = WideCharToMultiByte(CP_UTF8, 0, wFamilyName.begin(), -1, NULL, 0, NULL, NULL);
+    if (0 == str_len) {
+        //TODO: error
+    }
+    SkTDArray<char> familyName(new char[str_len], str_len);
+    str_len = WideCharToMultiByte(CP_UTF8, 0, wFamilyName.begin(), -1, familyName.begin(), str_len, NULL, NULL);
+    if (0 == str_len) {
+        //TODO: error
+    }
+    info->fFontName.set(familyName.begin(), str_len);
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+        populate_glyph_to_unicode(typeface->fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
+    }
+
+    DWRITE_FONT_FACE_TYPE fontType = typeface->fDWriteFontFace->GetType();
+    if (fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE ||
+        fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) {
+        info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+    } else {
+        info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+        info->fItalicAngle = 0;
+        info->fAscent = dwfm.ascent;;
+        info->fDescent = dwfm.descent;
+        info->fStemV = 0;
+        info->fCapHeight = dwfm.capHeight;
+        info->fBBox = SkIRect::MakeEmpty();
+        return info;
+    }
+
+    AutoDWriteTable<SkOTTableHead> headTable(typeface->fDWriteFontFace.get());
+    AutoDWriteTable<SkOTTablePostScript> postTable(typeface->fDWriteFontFace.get());
+    AutoDWriteTable<SkOTTableHorizontalHeader> hheaTable(typeface->fDWriteFontFace.get());
+    AutoDWriteTable<SkOTTableOS2> os2Table(typeface->fDWriteFontFace.get());
+    if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) {
+        info->fItalicAngle = 0;
+        info->fAscent = dwfm.ascent;;
+        info->fDescent = dwfm.descent;
+        info->fStemV = 0;
+        info->fCapHeight = dwfm.capHeight;
+        info->fBBox = SkIRect::MakeEmpty();
+        return info;
+    }
+
+    //There exist CJK fonts which set the IsFixedPitch and Monospace bits,
+    //but have full width, latin half-width, and half-width kana.
+    bool fixedWidth = (postTable->isFixedPitch &&
+                      (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics)));
+    //Monospace
+    if (fixedWidth) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    }
+    //Italic
+    if (os2Table->version.v0.fsSelection.field.Italic) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    }
+    //Symbolic (uses more than base latin).
+    info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+    //Script
+    if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType.value) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+    //Serif
+    } else if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType.value &&
+               SkPanose::Data::TextAndDisplay::SerifStyle::Triangle <= os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value &&
+               SkPanose::Data::TextAndDisplay::SerifStyle::NoFit != os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+    }
+
+    info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16;
+
+    info->fAscent = SkToS16(dwfm.ascent);
+    info->fDescent = SkToS16(dwfm.descent);
+    info->fCapHeight = SkToS16(dwfm.capHeight);
+
+    info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin),
+                                    (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax),
+                                    (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax),
+                                    (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin));
+
+    //TODO: is this even desired? It seems PDF only wants this value for Type1
+    //fonts, and we only get here for TrueType fonts.
+    info->fStemV = 0;
+    /*
+    // Figure out a good guess for StemV - Min width of i, I, !, 1.
+    // This probably isn't very good with an italic font.
+    int16_t min_width = SHRT_MAX;
+    info->fStemV = 0;
+    char stem_chars[] = {'i', 'I', '!', '1'};
+    for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
+        ABC abcWidths;
+        if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
+            int16_t width = abcWidths.abcB;
+            if (width > 0 && width < min_width) {
+                min_width = width;
+                info->fStemV = min_width;
+            }
+        }
+    }
+    */
+    
+    // If Restricted, the font may not be embedded in a document.
+    // If not Restricted, the font can be embedded.
+    // If PreviewPrint, the embedding is read-only.
+    if (os2Table->version.v0.fsType.field.Restricted) {
+        info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+    } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+        if (fixedWidth) {
+            appendRange(&info->fGlyphWidths, 0);
+            int16_t advance;
+            getWidthAdvance(typeface->fDWriteFontFace.get(), 1, &advance);
+            info->fGlyphWidths->fAdvance.append(1, &advance);
+            finishRange(info->fGlyphWidths.get(), 0,
+                        SkAdvancedTypefaceMetrics::WidthRange::kDefault);
+        } else {
+            info->fGlyphWidths.reset(
+                getAdvanceData(typeface->fDWriteFontFace.get(),
+                               glyphCount,
+                               glyphIDs,
+                               glyphIDsCount,
+                               getWidthAdvance));
+        }
+    }
+
+    return info;
+}
diff --git a/src/utils/SkFloatUtils.h b/src/utils/SkFloatUtils.h
new file mode 100644 (file)
index 0000000..101aac7
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFloatUtils_DEFINED
+#define SkFloatUtils_DEFINED
+
+#include "SkTypes.h"
+#include <limits.h>
+#include <float.h>
+
+template <size_t size>
+class SkTypeWithSize {
+public:
+    // Prevents using SkTypeWithSize<N> with non-specialized N.
+    typedef void UInt;
+};
+
+template <>
+class SkTypeWithSize<32> {
+public:
+    typedef uint32_t UInt;
+};
+
+template <>
+class SkTypeWithSize<64> {
+public:
+    typedef uint64_t UInt;
+};
+
+template <typename RawType>
+struct SkNumericLimits {
+    static const int digits = 0;
+};
+
+template <>
+struct SkNumericLimits<double> {
+    static const int digits = DBL_MANT_DIG;
+};
+
+template <>
+struct SkNumericLimits<float> {
+    static const int digits = FLT_MANT_DIG;
+};
+
+//See
+//http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison/3423299#3423299
+//http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h
+//http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
+
+template <typename RawType, unsigned int ULPs>
+class SkFloatingPoint {
+public:
+    /** Bits is a unsigned integer the same size as the floating point number. */
+    typedef typename SkTypeWithSize<sizeof(RawType) * CHAR_BIT>::UInt Bits;
+
+    /** # of bits in a number. */
+    static const size_t kBitCount = CHAR_BIT * sizeof(RawType);
+
+    /** # of fraction bits in a number. */
+    static const size_t kFractionBitCount = SkNumericLimits<RawType>::digits - 1;
+
+    /** # of exponent bits in a number. */
+    static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+    /** The mask for the sign bit. */
+    static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+    /** The mask for the fraction bits. */
+    static const Bits kFractionBitMask =
+        ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+    /** The mask for the exponent bits. */
+    static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+    /** How many ULP's (Units in the Last Place) to tolerate when comparing. */
+    static const size_t kMaxUlps = ULPs;
+
+    /**
+     *  Constructs a FloatingPoint from a raw floating-point number.
+     *
+     *  On an Intel CPU, passing a non-normalized NAN (Not a Number)
+     *  around may change its bits, although the new value is guaranteed
+     *  to be also a NAN.  Therefore, don't expect this constructor to
+     *  preserve the bits in x when x is a NAN.
+     */
+    explicit SkFloatingPoint(const RawType& x) { fU.value = x; }
+
+    /** Returns the exponent bits of this number. */
+    Bits exponent_bits() const { return kExponentBitMask & fU.bits; }
+
+    /** Returns the fraction bits of this number. */
+    Bits fraction_bits() const { return kFractionBitMask & fU.bits; }
+
+    /** Returns true iff this is NAN (not a number). */
+    bool is_nan() const {
+        // It's a NAN if both of the folloowing are true:
+        // * the exponent bits are all ones
+        // * the fraction bits are not all zero.
+        return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+    }
+
+    /**
+     *  Returns true iff this number is at most kMaxUlps ULP's away from ths.
+     *  In particular, this function:
+     *   - returns false if either number is (or both are) NAN.
+     *   - treats really large numbers as almost equal to infinity.
+     *   - thinks +0.0 and -0.0 are 0 DLP's apart.
+     */
+    bool AlmostEquals(const SkFloatingPoint& rhs) const {
+        // Any comparison operation involving a NAN must return false.
+        if (is_nan() || rhs.is_nan()) return false;
+
+        const Bits dist = DistanceBetweenSignAndMagnitudeNumbers(fU.bits,
+                                                                 rhs.fU.bits);
+        //SkDEBUGF(("(%f, %f, %d) ", u_.value_, rhs.u_.value_, dist));
+        return dist <= kMaxUlps;
+    }
+
+private:
+    /** The data type used to store the actual floating-point number. */
+    union FloatingPointUnion {
+        /** The raw floating-point number. */
+        RawType value;
+        /** The bits that represent the number. */
+        Bits bits;
+    };
+
+    /**
+     *  Converts an integer from the sign-and-magnitude representation to
+     *  the biased representation. More precisely, let N be 2 to the
+     *  power of (kBitCount - 1), an integer x is represented by the
+     *  unsigned number x + N.
+     *
+     *  For instance,
+     *
+     *    -N + 1 (the most negative number representable using
+     *           sign-and-magnitude) is represented by 1;
+     *    0      is represented by N; and
+     *    N - 1  (the biggest number representable using
+     *           sign-and-magnitude) is represented by 2N - 1.
+     *
+     *  Read http://en.wikipedia.org/wiki/Signed_number_representations
+     *  for more details on signed number representations.
+     */
+    static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+        if (kSignBitMask & sam) {
+            // sam represents a negative number.
+            return ~sam + 1;
+        } else {
+            // sam represents a positive number.
+            return kSignBitMask | sam;
+        }
+    }
+
+    /**
+     *  Given two numbers in the sign-and-magnitude representation,
+     *  returns the distance between them as an unsigned number.
+     */
+    static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+                                                       const Bits &sam2) {
+        const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+        const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+        return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+    }
+
+    FloatingPointUnion fU;
+};
+
+#endif
diff --git a/src/utils/win/SkDWriteFontFileStream.cpp b/src/utils/win/SkDWriteFontFileStream.cpp
new file mode 100644 (file)
index 0000000..1569158
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "SkDWriteFontFileStream.h"
+#include "SkHRESULT.h"
+
+#include <dwrite.h>
+#include <limits>
+
+///////////////////////////////////////////////////////////////////////////////
+//  SkIDWriteFontFileStream
+
+SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream)
+    : fFontFileStream(fontFileStream)
+    , fPos(0)
+    , fLockedMemory(NULL)
+    , fFragmentLock(NULL) {
+    fontFileStream->AddRef();
+}
+
+SkDWriteFontFileStream::~SkDWriteFontFileStream() {
+    if (fFragmentLock) {
+        fFontFileStream->ReleaseFileFragment(fFragmentLock);
+    }
+}
+
+const void* SkDWriteFontFileStream::getMemoryBase() {
+    if (fLockedMemory) {
+        return fLockedMemory;
+    }
+
+    UINT64 fileSize;
+    HRNM(fFontFileStream->GetFileSize(&fileSize), "Could not get file size");
+    HRNM(fFontFileStream->ReadFileFragment(&fLockedMemory, 0, fileSize, &fFragmentLock),
+         "Could not lock file fragment.");
+    return fLockedMemory;
+}
+
+bool SkDWriteFontFileStream::rewind() {
+    fPos = 0;
+    return true;
+}
+
+size_t SkDWriteFontFileStream::read(void* buffer, size_t size) {
+    HRESULT hr = S_OK;
+
+    if (NULL == buffer) {
+        UINT64 realFileSize = 0;
+        hr = fFontFileStream->GetFileSize(&realFileSize);
+        if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+            return 0;
+        }
+        size_t fileSize = static_cast<size_t>(realFileSize);
+        if (size == 0) {
+            return fileSize;
+        } else {
+            if (fPos + size > fileSize) {
+                size_t skipped = fileSize - fPos;
+                fPos = fileSize;
+                return skipped;
+            } else {
+                fPos += size;
+                return size;
+            }
+        }
+    }
+
+    const void* start;
+    void* fragmentLock;
+    hr = fFontFileStream->ReadFileFragment(&start, fPos, size, &fragmentLock);
+    if (SUCCEEDED(hr)) {
+        memcpy(buffer, start, size);
+        fFontFileStream->ReleaseFileFragment(fragmentLock);
+        fPos += size;
+        return size;
+    }
+
+    //The read may have failed because we asked for too much data.
+    UINT64 realFileSize = 0;
+    hr = fFontFileStream->GetFileSize(&realFileSize);
+    if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+        return 0;
+    }
+    size_t fileSize = static_cast<size_t>(realFileSize);
+    if (fPos + size > fileSize) {
+        size_t read = fileSize - fPos;
+        hr = fFontFileStream->ReadFileFragment(&start, fPos, read, &fragmentLock);
+        if (SUCCEEDED(hr)) {
+            memcpy(buffer, start, read);
+            fFontFileStream->ReleaseFileFragment(fragmentLock);
+            fPos = fileSize;
+            return read;
+        }
+        return 0;
+    } else {
+        //This means we were within bounds, but failed for some other reason.
+        return 0;
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//  SkIDWriteFontFileStreamWrapper
+
+HRESULT SkDWriteFontFileStreamWrapper::Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream) {
+    *streamFontFileStream = new SkDWriteFontFileStreamWrapper(stream);
+    if (NULL == streamFontFileStream) {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStream* stream)
+    : fRefCount(1), fStream(stream) {
+    stream->ref();
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReadFileFragment(
+    void const** fragmentStart,
+    UINT64 fileOffset,
+    UINT64 fragmentSize,
+    void** fragmentContext)
+{
+    // The loader is responsible for doing a bounds check.
+    UINT64 fileSize;
+    this->GetFileSize(&fileSize);
+    if (fileOffset > fileSize || fragmentSize > fileSize - fileOffset) {
+        *fragmentStart = NULL;
+        *fragmentContext = NULL;
+        return E_FAIL;
+    }
+
+    if (fileOffset + fragmentSize > (std::numeric_limits<size_t>::max)()) {
+        return E_FAIL;
+    }
+
+    const void* data = fStream->getMemoryBase();
+    if (NULL != data) {
+        *fragmentStart = static_cast<BYTE const*>(data) + static_cast<size_t>(fileOffset);
+        *fragmentContext = NULL;
+
+    } else {
+        //May be called from multiple threads.
+        SkAutoMutexAcquire ama(fStreamMutex);
+
+        *fragmentStart = NULL;
+        *fragmentContext = NULL;
+
+        if (!fStream->rewind()) {
+            return E_FAIL;
+        }
+        if (fStream->skip(static_cast<size_t>(fileOffset)) != fileOffset) {
+            return E_FAIL;
+        }
+        SkAutoTDeleteArray<uint8_t> streamData(new uint8_t[static_cast<size_t>(fragmentSize)]);
+        if (fStream->read(streamData.get(), static_cast<size_t>(fragmentSize)) != fragmentSize) {
+            return E_FAIL;
+        }
+
+        *fragmentStart = streamData.get();
+        *fragmentContext = streamData.detach();
+    }
+    return S_OK;
+}
+
+void STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) {
+    if (NULL == fragmentContext) {
+        return;
+    }
+    delete [] fragmentContext;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) {
+    *fileSize = fStream->getLength();
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetLastWriteTime(UINT64* lastWriteTime) {
+    // The concept of last write time does not apply to this loader.
+    *lastWriteTime = 0;
+    return E_NOTIMPL;
+}
diff --git a/src/utils/win/SkDWriteFontFileStream.h b/src/utils/win/SkDWriteFontFileStream.h
new file mode 100644 (file)
index 0000000..cd5787e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDWriteFontFileStream_DEFINED
+#define SkDWriteFontFileStream_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkStream.h"
+#include "SkTScopedComPtr.h"
+
+#include <dwrite.h>
+
+/**
+ *  An SkStream backed by an IDWriteFontFileStream.
+ *  This allows Skia code to read an IDWriteFontFileStream.
+ */
+class SkDWriteFontFileStream : public SkStream {
+public:
+    explicit SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream);
+    virtual ~SkDWriteFontFileStream();
+    
+    virtual bool rewind() SK_OVERRIDE;
+    virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+    virtual const void* getMemoryBase() SK_OVERRIDE;
+
+private:
+    SkTScopedComPtr<IDWriteFontFileStream> fFontFileStream;
+    size_t fPos;
+    const void* fLockedMemory;
+    void* fFragmentLock;
+};
+
+/**
+ *  An IDWriteFontFileStream backed by an SkStream.
+ *  This allows DirectWrite to read an SkStream.
+ */
+class SkDWriteFontFileStreamWrapper : public IDWriteFontFileStream {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontFileStream methods
+    virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(
+        void const** fragmentStart,
+        UINT64 fileOffset,
+        UINT64 fragmentSize,
+        void** fragmentContext);
+
+    virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
+    virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* fileSize);
+    virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* lastWriteTime);
+
+    static HRESULT Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream);
+
+private:
+    explicit SkDWriteFontFileStreamWrapper(SkStream* stream);
+
+    ULONG fRefCount;
+    SkAutoTUnref<SkStream> fStream;
+    SkMutex fStreamMutex;
+};
+#endif
diff --git a/src/utils/win/SkDWriteGeometrySink.cpp b/src/utils/win/SkDWriteGeometrySink.cpp
new file mode 100644 (file)
index 0000000..4aec3bb
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "SkDWriteGeometrySink.h"
+#include "SkFloatUtils.h"
+#include "SkPath.h"
+
+#include <dwrite.h>
+#include <d2d1.h>
+
+SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path) : fRefCount(1), fPath(path) { }
+
+SkDWriteGeometrySink::~SkDWriteGeometrySink() { }
+
+HRESULT STDMETHODCALLTYPE SkDWriteGeometrySink::QueryInterface(REFIID iid, void **object) {
+    if (NULL == object) {
+        return E_INVALIDARG;
+    }
+    if (iid == __uuidof(IUnknown) || iid == __uuidof(IDWriteGeometrySink)) {
+        *object = static_cast<IDWriteGeometrySink*>(this);
+        this->AddRef();
+        return S_OK;
+    } else {
+        *object = NULL;
+        return E_NOINTERFACE; 
+    }
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::AddRef(void) {
+    return static_cast<ULONG>(InterlockedIncrement(&fRefCount));
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::Release(void) {
+    ULONG res = static_cast<ULONG>(InterlockedDecrement(&fRefCount));
+    if (0 == res) {
+        delete this;
+    }
+    return res;
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) {
+    switch (fillMode) {
+    case D2D1_FILL_MODE_ALTERNATE:
+        fPath->setFillType(SkPath::kEvenOdd_FillType);
+        break;
+    case D2D1_FILL_MODE_WINDING:
+        fPath->setFillType(SkPath::kWinding_FillType);
+        break;
+    default:
+        SkASSERT(!"Unknown D2D1_FILL_MODE.");
+        break;
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) {
+    if (vertexFlags == D2D1_PATH_SEGMENT_NONE || vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN) {
+        SkASSERT(!"Invalid D2D1_PATH_SEGMENT value.");
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) {
+    fPath->moveTo(SkFloatToScalar(startPoint.x), SkFloatToScalar(startPoint.y));
+    if (figureBegin == D2D1_FIGURE_BEGIN_HOLLOW) {
+        SkASSERT(!"Invalid D2D1_FIGURE_BEGIN value.");
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) {
+    for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) {
+        fPath->lineTo(SkFloatToScalar(points->x), SkFloatToScalar(points->y));
+    }
+}
+
+static bool approximately_equal(float a, float b) {
+    const SkFloatingPoint<float, 10> lhs(a), rhs(b);
+    return lhs.AlmostEquals(rhs);
+}
+
+typedef struct {
+    float x;
+    float y;
+} Cubic[4], Quadratic[3];
+
+static bool check_quadratic(const Cubic& cubic, Quadratic& reduction) {
+    float dx10 = cubic[1].x - cubic[0].x;
+    float dx23 = cubic[2].x - cubic[3].x;
+    float midX = cubic[0].x + dx10 * 3 / 2;
+    //NOTE: !approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)
+    //does not work as subnormals get in between the left side and 0.
+    if (!approximately_equal(midX, (dx23 * 3 / 2) + cubic[3].x)) {
+        return false;
+    }
+    float dy10 = cubic[1].y - cubic[0].y;
+    float dy23 = cubic[2].y - cubic[3].y;
+    float midY = cubic[0].y + dy10 * 3 / 2;
+    if (!approximately_equal(midY, (dy23 * 3 / 2) + cubic[3].y)) {
+        return false;
+    }
+    reduction[0] = cubic[0];
+    reduction[1].x = midX;
+    reduction[1].y = midY;
+    reduction[2] = cubic[3];
+    return true;
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) {
+    SkPoint lastPt;
+    fPath->getLastPt(&lastPt);
+    D2D1_POINT_2F prevPt = { SkScalarToFloat(lastPt.fX), SkScalarToFloat(lastPt.fY) };
+
+    for (const D2D1_BEZIER_SEGMENT *end = &beziers[beziersCount]; beziers < end; ++beziers) {
+        Cubic cubic = { { prevPt.x, prevPt.y },
+                        { beziers->point1.x, beziers->point1.y },
+                        { beziers->point2.x, beziers->point2.y },
+                        { beziers->point3.x, beziers->point3.y }, };
+        Quadratic quadratic;
+        if (check_quadratic(cubic, quadratic)) {
+            fPath->quadTo(SkFloatToScalar(quadratic[1].x), SkFloatToScalar(quadratic[1].y),
+                          SkFloatToScalar(quadratic[2].x), SkFloatToScalar(quadratic[2].y));
+        } else {
+            fPath->cubicTo(SkFloatToScalar(beziers->point1.x), SkFloatToScalar(beziers->point1.y),
+                           SkFloatToScalar(beziers->point2.x), SkFloatToScalar(beziers->point2.y),
+                           SkFloatToScalar(beziers->point3.x), SkFloatToScalar(beziers->point3.y));
+        }
+        prevPt = beziers->point3;
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) {
+    fPath->close();
+}
+
+HRESULT SkDWriteGeometrySink::Close() {
+    return S_OK;
+}
+
+HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) {
+    *geometryToPath = new SkDWriteGeometrySink(path);
+    return S_OK;
+}
diff --git a/src/utils/win/SkDWriteGeometrySink.h b/src/utils/win/SkDWriteGeometrySink.h
new file mode 100644 (file)
index 0000000..99a3262
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDWriteToPath_DEFINED
+#define SkDWriteToPath_DEFINED
+
+#include "SkTypes.h"
+
+class SkPath;
+
+#include <dwrite.h>
+#include <d2d1.h>
+
+class SkDWriteGeometrySink : public IDWriteGeometrySink {
+private:
+    LONG fRefCount;
+    SkPath* fPath;
+
+    SkDWriteGeometrySink(const SkDWriteGeometrySink&);
+    SkDWriteGeometrySink& operator=(const SkDWriteGeometrySink&);
+
+protected:
+    explicit SkDWriteGeometrySink(SkPath* path);
+    virtual ~SkDWriteGeometrySink();
+
+public:
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) SK_OVERRIDE;
+    virtual ULONG STDMETHODCALLTYPE AddRef(void) SK_OVERRIDE;
+    virtual ULONG STDMETHODCALLTYPE Release(void) SK_OVERRIDE;
+
+    virtual void STDMETHODCALLTYPE SetFillMode(D2D1_FILL_MODE fillMode) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE AddLines(const D2D1_POINT_2F *points, UINT pointsCount) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE EndFigure(D2D1_FIGURE_END figureEnd) SK_OVERRIDE;
+    virtual HRESULT STDMETHODCALLTYPE Close() SK_OVERRIDE;
+
+    static HRESULT Create(SkPath* path, IDWriteGeometrySink** geometryToPath);
+};
+
+#endif