A remotable font management interface and DirectWrite implementation.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 21 Mar 2014 22:48:32 +0000 (22:48 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 21 Mar 2014 22:48:32 +0000 (22:48 +0000)
The introduced SkRemotableFontMgr is a font management interface designed for simple and fast proxy support. SkFontMgr_Indirect bridges a SkRemotableFontMgr and a local SkFontMgr to present a SkFontMgr interface.

This change is to be followed by https://codereview.chromium.org/132113015/ and  https://codereview.chromium.org/206693003 .

R=reed@google.com

Review URL: https://codereview.chromium.org/206683002

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

14 files changed:
gyp/ports.gyp
gyp/utils.gyp
include/ports/SkFontMgr.h
include/ports/SkFontMgr_indirect.h [new file with mode: 0644]
include/ports/SkRemotableFontMgr.h [new file with mode: 0644]
include/ports/SkTypeface_win.h
src/core/SkFontHost.cpp
src/fonts/SkFontMgr_indirect.cpp [new file with mode: 0644]
src/fonts/SkRemotableFontMgr.cpp [new file with mode: 0644]
src/ports/SkFontHost_win.cpp
src/ports/SkFontHost_win_dw.cpp
src/ports/SkRemotableFontMgr_win_dw.cpp [new file with mode: 0644]
src/utils/win/SkDWrite.cpp [new file with mode: 0644]
src/utils/win/SkDWrite.h [new file with mode: 0644]

index 7ecda5f35e6e68c919d483bd51be041c302e709e..ee325706c085780967c325941a01ab8ceaf14044 100644 (file)
         '../src/ports/SkAtomics_none.h',
         '../src/ports/SkAtomics_sync.h',
         '../src/ports/SkAtomics_win.h',
+        '../src/ports/SkMutex_none.h',
+        '../src/ports/SkMutex_pthread.h',
+        '../src/ports/SkMutex_win.h',
         '../src/ports/SkDebug_nacl.cpp',
         '../src/ports/SkDebug_stdio.cpp',
         '../src/ports/SkDebug_win.cpp',
 
+        '../src/fonts/SkFontMgr_indirect.cpp',
+        '../src/fonts/SkRemotableFontMgr.cpp',
         '../src/ports/SkFontHost_win.cpp',
         '../src/ports/SkFontHost_win_dw.cpp',
         '../src/ports/SkFontMgr_default_gdi.cpp',
         '../src/ports/SkFontMgr_default_dw.cpp',
+        '../src/ports/SkRemotableFontMgr_win_dw.cpp',
 
         '../src/ports/SkGlobalInitialization_default.cpp',
         '../src/ports/SkMemory_malloc.cpp',
         '../src/ports/SkTLS_pthread.cpp',
         '../src/ports/SkTLS_win.cpp',
         '../src/ports/SkXMLParser_empty.cpp',
+
+        '../include/ports/SkFontConfigInterface.h',
+        '../include/ports/SkFontMgr.h',
+        '../include/ports/SkFontMgr_indirect.h',
+        '../include/ports/SkFontStyle.h',
+        '../include/ports/SkRemotableFontMgr.h',
       ],
       'conditions': [
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos", "nacl", "android"]', {
index d94f4a2d515f6ef9552c9088f4c68e0decf95145..070d18a07e1a3f119c97785362dd447641488355 100644 (file)
         '../include/utils/win/SkIStream.h',
         '../include/utils/win/SkTScopedComPtr.h',
         '../src/utils/win/SkAutoCoInitialize.cpp',
+        '../src/utils/win/SkDWrite.h',
+        '../src/utils/win/SkDWrite.cpp',
         '../src/utils/win/SkDWriteFontFileStream.cpp',
         '../src/utils/win/SkDWriteFontFileStream.h',
         '../src/utils/win/SkDWriteGeometrySink.cpp',
             '../include/utils/win/SkIStream.h',
             '../include/utils/win/SkTScopedComPtr.h',
             '../src/utils/win/SkAutoCoInitialize.cpp',
+            '../src/utils/win/SkDWrite.h',
+            '../src/utils/win/SkDWrite.cpp',
             '../src/utils/win/SkDWriteFontFileStream.cpp',
             '../src/utils/win/SkDWriteFontFileStream.h',
             '../src/utils/win/SkDWriteGeometrySink.cpp',
index df57aa762216cc28edf56ae314485a95a4e2d370..a2fad9aba6e3cba4fa935e87e62a70848bfec2ed 100644 (file)
@@ -44,6 +44,9 @@ public:
     /**
      *  The caller must call unref() on the returned object.
      *  Never returns NULL; will return an empty set if the name is not found.
+     *
+     *  It is possible that this will return a style set not accessible from
+     *  createStyleSet(int) due to hidden or auto-activated fonts.
      */
     SkFontStyleSet* matchFamily(const char familyName[]) const;
 
@@ -52,9 +55,24 @@ public:
      *  and return a ref to it. The caller must call unref() on the returned
      *  object. Will never return NULL, as it will return the default font if
      *  no matching font is found.
+     *
+     *  It is possible that this will return a style set not accessible from
+     *  createStyleSet(int) or matchFamily(const char[]) due to hidden or
+     *  auto-activated fonts.
      */
     SkTypeface* matchFamilyStyle(const char familyName[], const SkFontStyle&) const;
 
+    /**
+     *  Use the system fallback to find a typeface for the given character.
+     *  Note that bpc47 is a combination of ISO 639, 15924, and 3166-1 codes,
+     *  so it is fine to just pass a ISO 639 here.
+     *
+     *  Will return NULL if no family can be found for the character
+     *  in the system fallback.
+     */
+    SkTypeface* matchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
+                                          const char bpc47[], uint32_t character) const;
+
     SkTypeface* matchFaceStyle(const SkTypeface*, const SkFontStyle&) const;
 
     /**
@@ -98,6 +116,10 @@ protected:
 
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
                                            const SkFontStyle&) const = 0;
+    // TODO: pure virtual, implement on all impls.
+    virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
+                                                    const char bpc47[], uint32_t character) const
+    { return NULL; }
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
                                          const SkFontStyle&) const = 0;
 
diff --git a/include/ports/SkFontMgr_indirect.h b/include/ports/SkFontMgr_indirect.h
new file mode 100644 (file)
index 0000000..52cb450
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFontMgr_indirect_DEFINED
+#define SkFontMgr_indirect_DEFINED
+
+#include "SkDataTable.h"
+#include "SkFontMgr.h"
+#include "SkFontStyle.h"
+#include "SkOnce.h"
+#include "SkRemotableFontMgr.h"
+#include "SkTArray.h"
+#include "SkTypeface.h"
+
+class SkData;
+class SkStream;
+class SkString;
+class SkTypeface;
+
+class SK_API SkFontMgr_Indirect : public SkFontMgr {
+public:
+    // TODO: The SkFontMgr is only used for createFromStream/File/Data.
+    // In the future these calls should be broken out into their own interface
+    // with a name like SkFontRenderer.
+    SkFontMgr_Indirect(SkFontMgr* impl, SkRemotableFontMgr* proxy)
+        : fImpl(SkRef(impl)), fProxy(SkRef(proxy))
+    {
+        fOnce = SK_ONCE_INIT;
+    }
+
+protected:
+    virtual int onCountFamilies() const SK_OVERRIDE;
+    virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE;
+    virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE;
+
+    virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE;
+
+    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
+                                           const SkFontStyle& fontStyle) const SK_OVERRIDE;
+
+    virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
+                                                    const SkFontStyle&,
+                                                    const char bpc47[],
+                                                    uint32_t character) const SK_OVERRIDE;
+
+    virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
+                                         const SkFontStyle& fontStyle) const SK_OVERRIDE;
+
+    virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE;
+    virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE;
+    virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE;
+
+    virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
+                                               unsigned styleBits) const SK_OVERRIDE;
+
+private:
+    SkTypeface* createTypefaceFromFontId(const SkFontIdentity& fontId) const;
+
+    SkAutoTUnref<SkFontMgr> fImpl;
+    SkAutoTUnref<SkRemotableFontMgr> fProxy;
+
+    struct DataEntry {
+        int fDataId;  // key1
+        int fTtcIndex;  // key2
+        SkTypeface* fTypeface;  // value: weak ref to typeface
+
+        DataEntry() { }
+
+        // This is a move!!!
+        DataEntry(DataEntry& that)
+            : fDataId(that.fDataId)
+            , fTtcIndex(that.fTtcIndex)
+            , fTypeface(that.fTypeface)
+        {
+            SkDEBUGCODE(that.fDataId = -1;)
+            SkDEBUGCODE(that.fTtcIndex = -1;)
+            that.fTypeface = NULL;
+        }
+
+        ~DataEntry() {
+            if (fTypeface) {
+                fTypeface->weak_unref();
+            }
+        }
+    };
+    /**
+     *  This cache is essentially { dataId: { ttcIndex: typeface } }
+     *  For data caching we want a mapping from data id to weak references to
+     *  typefaces with that data id. By storing the index next to the typeface,
+     *  this data cache also acts as a typeface cache.
+     */
+    mutable SkTArray<DataEntry> fDataCache;
+    mutable SkMutex fDataCacheMutex;
+
+    mutable SkAutoTUnref<SkDataTable> fFamilyNames;
+    mutable SkOnceFlag fOnce;
+    static void set_up_family_names(const SkFontMgr_Indirect* self);
+
+    friend class SkStyleSet_Indirect;
+};
+
+#endif
+
diff --git a/include/ports/SkRemotableFontMgr.h b/include/ports/SkRemotableFontMgr.h
new file mode 100644 (file)
index 0000000..25a725b
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRemotableFontMgr_DEFINED
+#define SkRemotableFontMgr_DEFINED
+
+#include "SkFontStyle.h"
+#include "SkRefCnt.h"
+#include "SkTemplates.h"
+
+class SkDataTable;
+class SkStreamAsset;
+class SkString;
+
+struct SK_API SkFontIdentity {
+    static const uint32_t kInvalidDataId = 0xFFFFFFFF;
+
+    // Note that fDataId is a data identifier, not a font identifier.
+    // (fDataID, fTtcIndex) can be seen as a font identifier.
+    uint32_t fDataId;
+    uint32_t fTtcIndex;
+
+    // On Linux/FontConfig there is also the ability to specify preferences for rendering
+    // antialias, embedded bitmaps, autohint, hinting, hintstyle, lcd rendering
+    // may all be set or set to no-preference
+    // (No-preference is resolved against globals set by the platform)
+    // Since they may be selected against, these are really 'extensions' to SkFontStyle.
+    // SkFontStyle should pick these up.
+    SkFontStyle fFontStyle;
+};
+
+class SK_API SkRemotableFontIdentitySet : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkRemotableFontIdentitySet)
+
+    SkRemotableFontIdentitySet(int count, SkFontIdentity** data);
+
+    int count() const { return fCount; }
+    const SkFontIdentity& at(int index) const { return fData[index]; }
+
+    static SkRemotableFontIdentitySet* NewEmpty();
+
+private:
+    SkRemotableFontIdentitySet() : fCount(0), fData() { }
+    static void NewEmptyImpl(int);
+
+    int fCount;
+    SkAutoTMalloc<SkFontIdentity> fData;
+
+    typedef SkRefCnt INHERITED;
+};
+
+class SK_API SkRemotableFontMgr : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkRemotableFontMgr)
+
+    /**
+     *  Returns the names of the known fonts on the system.
+     *  Will not return NULL, will return an empty table if no families exist.
+     *
+     *  The indexes may be used with getIndex(int) and
+     *  matchIndexStyle(int, SkFontStyle).
+     *
+     *  The caller must unref() the returned object.
+     */
+    virtual SkDataTable* getFamilyNames() const = 0;
+
+    /**
+     *  Returns all of the fonts with the given familyIndex.
+     *  Returns NULL if the index is out of bounds.
+     *  Returns empty if there are no fonts at the given index.
+     *
+     *  The caller must unref() the returned object.
+     */
+    virtual SkRemotableFontIdentitySet* getIndex(int familyIndex) const = 0;
+
+    /**
+     *  Returns the closest match to the given style in the given index.
+     *  If there are no available fonts at the given index, the return value's
+     *  data id will be kInvalidDataId.
+     */
+    virtual SkFontIdentity matchIndexStyle(int familyIndex, const SkFontStyle&) const = 0;
+
+    /**
+     *  Returns all the fonts on the system with the given name.
+     *  If the given name is NULL, will return the default font family.
+     *  Never returns NULL; will return an empty set if the name is not found.
+     *
+     *  It is possible that this will return fonts not accessible from
+     *  getIndex(int) or matchIndexStyle(int, SkFontStyle) due to
+     *  hidden or auto-activated fonts.
+     *
+     *  The matching may be done in a system dependent way. The name may be
+     *  matched case-insensitive, there may be system aliases which resolve,
+     *  and names outside the current locale may be considered. However, this
+     *  should only return fonts which are somehow associated with the requested
+     *  name.
+     *
+     *  The caller must unref() the returned object.
+     */
+    virtual SkRemotableFontIdentitySet* matchName(const char familyName[]) const = 0;
+
+    /**
+     *  Returns the closest matching font to the specified name and style.
+     *  If there are no available fonts which match the name, the return value's
+     *  data id will be kInvalidDataId.
+     *  If the given name is NULL, the match will be against any default fonts.
+     *
+     *  It is possible that this will return a font identity not accessible from
+     *  methods returning sets due to hidden or auto-activated fonts.
+     *
+     *  The matching may be done in a system dependent way. The name may be
+     *  matched case-insensitive, there may be system aliases which resolve,
+     *  and names outside the current locale may be considered. However, this
+     *  should only return a font which is somehow associated with the requested
+     *  name.
+     *
+     *  The caller must unref() the returned object.
+     */
+    virtual SkFontIdentity matchNameStyle(const char familyName[], const SkFontStyle&) const = 0;
+
+    /**
+     *  Use the system fall-back to find a font for the given character.
+     *  If no font can be found for the character, the return value's data id
+     *  will be kInvalidDataId.
+     *  If the name is NULL, the match will start against any default fonts.
+     *  If the bpc47 is NULL, a default locale will be assumed.
+     *
+     *  Note that bpc47 is a combination of ISO 639, 15924, and 3166-1 codes,
+     *  so it is fine to just pass a ISO 639 here.
+     */
+    virtual SkFontIdentity matchNameStyleCharacter(const char familyName[], const SkFontStyle&,
+                                                   const char bpc47[], SkUnichar character) const=0;
+
+    /**
+     *  Returns the data for the given data id.
+     *  Will return NULL if the data id is invalid.
+     *  Note that this is a data id, not a font id.
+     *
+     *  The caller must unref() the returned object.
+     */
+    virtual SkStreamAsset* getData(int dataId) const = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
+
index e7dd4ab50ae77490598cac4a6833fd28c0ac23dc..77173e2cf7ca7853c49682b70ff9dbda58de7c32 100644 (file)
@@ -36,7 +36,25 @@ SK_API void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*)(const LOGFONT&));
 // Experimental!
 //
 class SkFontMgr;
+class SkRemotableFontMgr;
+
 SK_API SkFontMgr* SkFontMgr_New_GDI();
 SK_API SkFontMgr* SkFontMgr_New_DirectWrite();
 
+/**
+ *  Creates an SkFontMgr which renders using DirectWrite and obtains its data
+ *  from the SkRemotableFontMgr.
+ *
+ *  If DirectWrite could not be initialized, will return NULL.
+ */
+SK_API SkFontMgr* SkFontMgr_New_DirectWriteRenderer(SkRemotableFontMgr*);
+
+/**
+ *  Creates an SkRemotableFontMgr backed by DirectWrite using the default
+ *  system font collection in the current locale.
+ *
+ *  If DirectWrite could not be initialized, will return NULL.
+ */
+SK_API SkRemotableFontMgr* SkRemotableFontMgr_New_DirectWrite();
+
 #endif
index fa20b47e10e773223c76e32dec112e9c02ff11e7..9e7eeb182d1790ba9f362fd9c2bcbcdaf91f150a 100644 (file)
@@ -110,6 +110,12 @@ protected:
                                            const SkFontStyle&) const SK_OVERRIDE {
         return NULL;
     }
+    virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
+                                                    const SkFontStyle& style,
+                                                    const char bpc47[],
+                                                    uint32_t character) const SK_OVERRIDE {
+        return NULL;
+    }
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
                                          const SkFontStyle&) const SK_OVERRIDE {
         return NULL;
@@ -156,6 +162,11 @@ SkTypeface* SkFontMgr::matchFamilyStyle(const char familyName[],
     return this->onMatchFamilyStyle(familyName, fs);
 }
 
+SkTypeface* SkFontMgr::matchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style,
+                                                 const char bpc47[], uint32_t character) const {
+    return this->onMatchFamilyStyleCharacter(familyName, style, bpc47, character);
+}
+
 SkTypeface* SkFontMgr::matchFaceStyle(const SkTypeface* face,
                                       const SkFontStyle& fs) const {
     return this->onMatchFaceStyle(face, fs);
diff --git a/src/fonts/SkFontMgr_indirect.cpp b/src/fonts/SkFontMgr_indirect.cpp
new file mode 100644 (file)
index 0000000..bc8dce1
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFontMgr_indirect.h"
+
+#include "SkDataTable.h"
+#include "SkFontStyle.h"
+#include "SkOnce.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTypeface.h"
+
+class SkData;
+class SkString;
+
+class SkStyleSet_Indirect : public SkFontStyleSet {
+public:
+    /** Takes ownership of the SkRemotableFontIdentitySet. */
+    SkStyleSet_Indirect(const SkFontMgr_Indirect* owner, int familyIndex,
+                        SkRemotableFontIdentitySet* data)
+        : fOwner(SkRef(owner)), fFamilyIndex(familyIndex), fData(data)
+    { }
+
+    virtual int count() SK_OVERRIDE { return fData->count(); }
+
+    virtual void getStyle(int index, SkFontStyle* fs, SkString* style) SK_OVERRIDE {
+        if (fs) {
+            *fs = fData->at(index).fFontStyle;
+        }
+        if (style) {
+            // TODO: is this useful? Current locale?
+            style->reset();
+        }
+    }
+
+    virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
+        return fOwner->createTypefaceFromFontId(fData->at(index));
+    }
+
+    virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
+        if (fFamilyIndex >= 0) {
+            SkFontIdentity id = fOwner->fProxy->matchIndexStyle(fFamilyIndex, pattern);
+            return fOwner->createTypefaceFromFontId(id);
+        }
+
+        // If this SkStyleSet was created via onMatchFamily we would need a call like
+        // fOwner->fProxy->matchNameStyle(fFamilyName, pattern);
+        // but would not activate fonts (only consider fonts which would come back from matchName).
+
+        // CSS policy sounds good.
+        struct Score {
+            int score;
+            int index;
+        };
+
+        // Width has the greatest priority.
+        // If the value of pattern.width is 5 (normal) or less,
+        //    narrower width values are checked first, then wider values.
+        // If the value of pattern.width is greater than 5 (normal),
+        //    wider values are checked first, followed by narrower values.
+
+        // Italic/Oblique has the next highest priority.
+        // If italic requested and there is some italic font, use it.
+        // If oblique requested and there is some oblique font, use it.
+        // If italic requested and there is some oblique font, use it.
+        // If oblique requested and there is some italic font, use it.
+
+        // Exact match.
+        // If pattern.weight < 400, weights below pattern.weight are checked
+        //   in descending order followed by weights above pattern.weight
+        //   in ascending order until a match is found.
+        // If pattern.weight > 500, weights above pattern.weight are checked
+        //   in ascending order followed by weights below pattern.weight
+        //   in descending order until a match is found.
+        // If pattern.weight is 400, 500 is checked first
+        //   and then the rule for pattern.weight < 400 is used.
+        // If pattern.weight is 500, 400 is checked first
+        //   and then the rule for pattern.weight < 400 is used
+
+        Score maxScore = { 0, 0 };
+        for (int i = 0; i < fData->count(); ++i) {
+            const SkFontStyle& current = fData->at(i).fFontStyle;
+            Score currentScore = { 0, i };
+
+            // CSS stretch. (This is the width.)
+            // This has the highest priority.
+            if (pattern.width() <= SkFontStyle::kNormal_Width) {
+                if (current.width() <= pattern.width()) {
+                    currentScore.score += 10 - pattern.width() + current.width();
+                } else {
+                    currentScore.score += 10 - current.width();
+                }
+            } else {
+                if (current.width() > pattern.width()) {
+                    currentScore.score += 10 + pattern.width() - current.width();
+                } else {
+                    currentScore.score += current.width();
+                }
+            }
+            currentScore.score *= 1002;
+
+            // CSS style (italic/oblique)
+            // Being italic trumps all valid weights which are not italic.
+            // Note that newer specs differentiate between italic and oblique.
+            if (pattern.isItalic() && current.isItalic()) {
+                currentScore.score += 1001;
+            }
+
+            // Synthetics (weight/style) [no stretch synthetic?]
+
+            // The 'closer' to the target weight, the higher the score.
+            // 1000 is the 'heaviest' recognized weight
+            if (pattern.weight() == current.weight()) {
+                currentScore.score += 1000;
+            } else if (pattern.weight() <= 500) {
+                if (pattern.weight() >= 400 && pattern.weight() < 450) {
+                    if (current.weight() >= 450 && current.weight() <= 500) {
+                        // Artificially boost the 500 weight.
+                        // TODO: determine correct number to use.
+                        currentScore.score += 500;
+                    }
+                }
+                if (current.weight() <= pattern.weight()) {
+                    currentScore.score += 1000 - pattern.weight() + current.weight();
+                } else {
+                    currentScore.score += 1000 - current.weight();
+                }
+            } else if (pattern.weight() > 500) {
+                if (current.weight() > pattern.weight()) {
+                    currentScore.score += 1000 + pattern.weight() - current.weight();
+                } else {
+                    currentScore.score += current.weight();
+                }
+            }
+
+            if (currentScore.score > maxScore.score) {
+                maxScore = currentScore;
+            }
+        }
+
+        return this->createTypeface(maxScore.index);
+    }
+private:
+    SkAutoTUnref<const SkFontMgr_Indirect> fOwner;
+    int fFamilyIndex;
+    SkAutoTUnref<SkRemotableFontIdentitySet> fData;
+};
+
+void SkFontMgr_Indirect::set_up_family_names(const SkFontMgr_Indirect* self) {
+    self->fFamilyNames.reset(self->fProxy->getFamilyNames());
+}
+
+int SkFontMgr_Indirect::onCountFamilies() const {
+    SkOnce(&fOnce, SkFontMgr_Indirect::set_up_family_names, this);
+    return fFamilyNames->count();
+}
+
+void SkFontMgr_Indirect::onGetFamilyName(int index, SkString* familyName) const {
+    SkOnce(&fOnce, SkFontMgr_Indirect::set_up_family_names, this);
+    if (index >= fFamilyNames->count()) {
+        familyName->reset();
+        return;
+    }
+    familyName->set(fFamilyNames->atStr(index));
+}
+
+SkFontStyleSet* SkFontMgr_Indirect::onCreateStyleSet(int index) const {
+    SkRemotableFontIdentitySet* set = fProxy->getIndex(index);
+    if (NULL == set) {
+        return NULL;
+    }
+    return SkNEW_ARGS(SkStyleSet_Indirect, (this, index, set));
+}
+
+SkFontStyleSet* SkFontMgr_Indirect::onMatchFamily(const char familyName[]) const {
+    return SkNEW_ARGS(SkStyleSet_Indirect, (this, -1, fProxy->matchName(familyName)));
+}
+
+SkTypeface* SkFontMgr_Indirect::createTypefaceFromFontId(const SkFontIdentity& id) const {
+    if (id.fDataId == SkFontIdentity::kInvalidDataId) {
+        return NULL;
+    }
+
+    SkAutoMutexAcquire ama(fDataCacheMutex);
+
+    SkAutoTUnref<SkTypeface> dataTypeface;
+    int dataTypefaceIndex = 0;
+    for (int i = 0; i < fDataCache.count(); ++i) {
+        const DataEntry& entry = fDataCache[i];
+        if (entry.fDataId == id.fDataId) {
+            if (entry.fTtcIndex == id.fTtcIndex &&
+                !entry.fTypeface->weak_expired() && entry.fTypeface->try_ref())
+            {
+                return entry.fTypeface;
+            }
+            if (dataTypeface.get() == NULL &&
+                !entry.fTypeface->weak_expired() && entry.fTypeface->try_ref())
+            {
+                dataTypeface.reset(entry.fTypeface);
+                dataTypefaceIndex = entry.fTtcIndex;
+            }
+        }
+
+        if (entry.fTypeface->weak_expired()) {
+            fDataCache.removeShuffle(i);
+            --i;
+        }
+    }
+
+    // No exact match, but did find a data match.
+    if (dataTypeface.get() != NULL) {
+        SkAutoTUnref<SkStream> stream(dataTypeface->openStream(NULL));
+        if (stream.get() != NULL) {
+            return fImpl->createFromStream(stream.get(), dataTypefaceIndex);
+        }
+    }
+
+    // No data match, request data and add entry.
+    SkAutoTUnref<SkStreamAsset> stream(fProxy->getData(id.fDataId));
+    if (stream.get() == NULL) {
+        return NULL;
+    }
+
+    SkAutoTUnref<SkTypeface> typeface(fImpl->createFromStream(stream, id.fTtcIndex));
+    if (typeface.get() == NULL) {
+        return NULL;
+    }
+
+    DataEntry& newEntry = fDataCache.push_back();
+    typeface->weak_ref();
+    newEntry.fDataId = id.fDataId;
+    newEntry.fTtcIndex = id.fTtcIndex;
+    newEntry.fTypeface = typeface.get();  // weak reference passed to new entry.
+
+    return typeface.detach();
+}
+
+SkTypeface* SkFontMgr_Indirect::onMatchFamilyStyle(const char familyName[],
+                                                   const SkFontStyle& fontStyle) const {
+    SkFontIdentity id = fProxy->matchNameStyle(familyName, fontStyle);
+    return this->createTypefaceFromFontId(id);
+}
+
+SkTypeface* SkFontMgr_Indirect::onMatchFamilyStyleCharacter(const char familyName[],
+                                                            const SkFontStyle& style,
+                                                            const char bpc47[],
+                                                            uint32_t character) const {
+    SkFontIdentity id = fProxy->matchNameStyleCharacter(familyName, style, bpc47, character);
+    return this->createTypefaceFromFontId(id);
+}
+
+SkTypeface* SkFontMgr_Indirect::onMatchFaceStyle(const SkTypeface* familyMember,
+                                                 const SkFontStyle& fontStyle) const {
+    SkString familyName;
+    familyMember->getFamilyName(&familyName);
+    return this->matchFamilyStyle(familyName.c_str(), fontStyle);
+}
+
+SkTypeface* SkFontMgr_Indirect::onCreateFromStream(SkStream* stream, int ttcIndex) const {
+    return fImpl->createFromStream(stream, ttcIndex);
+}
+
+SkTypeface* SkFontMgr_Indirect::onCreateFromFile(const char path[], int ttcIndex) const {
+    return fImpl->createFromFile(path, ttcIndex);
+}
+
+SkTypeface* SkFontMgr_Indirect::onCreateFromData(SkData* data, int ttcIndex) const {
+    return fImpl->createFromData(data, ttcIndex);
+}
+
+SkTypeface* SkFontMgr_Indirect::onLegacyCreateTypeface(const char familyName[],
+                                                       unsigned styleBits) const {
+    bool bold = SkToBool(styleBits & SkTypeface::kBold);
+    bool italic = SkToBool(styleBits & SkTypeface::kItalic);
+    SkFontStyle style = SkFontStyle(bold ? SkFontStyle::kBold_Weight
+                                         : SkFontStyle::kNormal_Weight,
+                                    SkFontStyle::kNormal_Width,
+                                    italic ? SkFontStyle::kItalic_Slant
+                                           : SkFontStyle::kUpright_Slant);
+
+    SkAutoTUnref<SkTypeface> face(this->matchFamilyStyle(familyName, style));
+
+    if (NULL == face.get()) {
+        face.reset(this->matchFamilyStyle(NULL, style));
+    }
+
+    if (NULL == face.get()) {
+        SkFontIdentity fontId = this->fProxy->matchIndexStyle(0, style);
+        face.reset(this->createTypefaceFromFontId(fontId));
+    }
+
+    return face.detach();
+}
diff --git a/src/fonts/SkRemotableFontMgr.cpp b/src/fonts/SkRemotableFontMgr.cpp
new file mode 100644 (file)
index 0000000..1139972
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRemotableFontMgr.h"
+
+#include "SkOnce.h"
+
+SkRemotableFontIdentitySet::SkRemotableFontIdentitySet(int count, SkFontIdentity** data)
+      : fCount(count), fData(count)
+{
+    SkASSERT(data);
+    *data = fData;
+}
+
+static SkRemotableFontIdentitySet* gEmptyRemotableFontIdentitySet = NULL;
+static void cleanup_gEmptyRemotableFontIdentitySet() { gEmptyRemotableFontIdentitySet->unref(); }
+
+void SkRemotableFontIdentitySet::NewEmptyImpl(int) {
+    gEmptyRemotableFontIdentitySet = new SkRemotableFontIdentitySet();
+}
+
+SkRemotableFontIdentitySet* SkRemotableFontIdentitySet::NewEmpty() {
+    SK_DECLARE_STATIC_ONCE(once);
+    SkOnce(&once, SkRemotableFontIdentitySet::NewEmptyImpl, 0,
+           cleanup_gEmptyRemotableFontIdentitySet);
+    gEmptyRemotableFontIdentitySet->ref();
+    return gEmptyRemotableFontIdentitySet;
+}
index 9dc720a643b52109226045c9d3559ed87295fc04..8ed3b666aa5718f28067d64ba0a3c9e9ff437817 100755 (executable)
@@ -283,15 +283,6 @@ protected:
 
 class FontMemResourceTypeface : public LogFontTypeface {
 public:
-    /**
-     *  Takes ownership of fontMemResource.
-     */
-    FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
-        LogFontTypeface(style, fontID, lf, true), fFontMemResource(fontMemResource) {
-    }
-
-    HANDLE fFontMemResource;
-
     /**
      *  The created FontMemResourceTypeface takes ownership of fontMemResource.
      */
@@ -309,6 +300,15 @@ protected:
     }
 
 private:
+    /**
+     *  Takes ownership of fontMemResource.
+     */
+    FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
+        LogFontTypeface(style, fontID, lf, true), fFontMemResource(fontMemResource) {
+    }
+
+    HANDLE fFontMemResource;
+
     typedef LogFontTypeface INHERITED;
 };
 
index 39b83e9d1aef20b928d25cb7067b5f3c372af10a..462ca1d3a6da03c680aeed6324105e62b0bdc39f 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "SkAdvancedTypefaceMetrics.h"
 #include "SkColorFilter.h"
+#include "SkDWrite.h"
 #include "SkDWriteFontFileStream.h"
 #include "SkDWriteGeometrySink.h"
 #include "SkDescriptor.h"
@@ -42,77 +43,6 @@ static bool isLCD(const SkScalerContext::Rec& rec) {
            SkMask::kLCD32_Format == rec.fMaskFormat;
 }
 
-/** Prefer to use this type to prevent template proliferation. */
-typedef SkAutoSTMalloc<16, WCHAR> SkSMallocWCHAR;
-
-/** Converts a utf8 string to a WCHAR string. */
-static HRESULT cstring_to_wchar(const char* skname, SkSMallocWCHAR* name) {
-    int wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, NULL, 0);
-    if (0 == wlen) {
-        HRM(HRESULT_FROM_WIN32(GetLastError()),
-            "Could not get length for wchar to utf-8 conversion.");
-    }
-    name->reset(wlen);
-    wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, name->get(), wlen);
-    if (0 == wlen) {
-        HRM(HRESULT_FROM_WIN32(GetLastError()), "Could not convert wchar to utf-8.");
-    }
-    return S_OK;
-}
-
-/** Converts a WCHAR string to a utf8 string. */
-static HRESULT wchar_to_skstring(WCHAR* name, SkString* skname) {
-    int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
-    if (0 == len) {
-        HRM(HRESULT_FROM_WIN32(GetLastError()),
-            "Could not get length for utf-8 to wchar conversion.");
-    }
-    skname->resize(len - 1);
-
-    // TODO: remove after https://code.google.com/p/skia/issues/detail?id=1989 is fixed.
-    // If we resize to 0 then the skname points to gEmptyRec (the unique empty SkString::Rec).
-    // gEmptyRec is static const and on Windows this means the value is in a read only page.
-    // Writing to it in the following call to WideCharToMultiByte will cause an access violation.
-    if (1 == len) {
-        return S_OK;
-    }
-
-    len = WideCharToMultiByte(CP_UTF8, 0, name, -1, skname->writable_str(), len, NULL, NULL);
-    if (0 == len) {
-        HRM(HRESULT_FROM_WIN32(GetLastError()), "Could not convert utf-8 to wchar.");
-    }
-    return S_OK;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void create_dwrite_factory(IDWriteFactory** factory) {
-    typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
-    DWriteCreateFactoryProc dWriteCreateFactoryProc = reinterpret_cast<DWriteCreateFactoryProc>(
-        GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"));
-
-    if (!dWriteCreateFactoryProc) {
-        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
-        if (!IS_ERROR(hr)) {
-            hr = ERROR_PROC_NOT_FOUND;
-        }
-        HRVM(hr, "Could not get DWriteCreateFactory proc.");
-    }
-
-    HRVM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED,
-                                 __uuidof(IDWriteFactory),
-                                 reinterpret_cast<IUnknown**>(factory)),
-         "Could not create DirectWrite factory.");
-}
-
-static IDWriteFactory* get_dwrite_factory() {
-    static IDWriteFactory* gDWriteFactory = NULL;
-    SK_DECLARE_STATIC_ONCE(once);
-    SkOnce(&once, create_dwrite_factory, &gDWriteFactory);
-
-    return gDWriteFactory;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 class StreamFontFileLoader;
@@ -215,7 +145,7 @@ private:
 };
 
 const void* DWriteOffscreen::draw(const SkGlyph& glyph, bool isBW) {
-    IDWriteFactory* factory = get_dwrite_factory();
+    IDWriteFactory* factory = sk_get_dwrite_factory();
     SkASSERT(factory != NULL);
 
     if (fWidth < glyph.fWidth || fHeight < glyph.fHeight) {
@@ -560,16 +490,19 @@ public:
                                                fontFileLoader, fontCollectionLoader));
     }
 
-    ~DWriteFontTypeface() {
+protected:
+    virtual void weak_dispose() const SK_OVERRIDE {
         if (fDWriteFontCollectionLoader.get() == NULL) return;
 
-        IDWriteFactory* factory = get_dwrite_factory();
+        IDWriteFactory* factory = sk_get_dwrite_factory();
         SkASSERT(factory != NULL);
         HRV(factory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get()));
         HRV(factory->UnregisterFontFileLoader(fDWriteFontFileLoader.get()));
+
+        //SkTypefaceCache::Remove(this);
+        INHERITED::weak_dispose();
     }
 
-protected:
     virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
     virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
     virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
@@ -585,6 +518,9 @@ protected:
     virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const SK_OVERRIDE;
+
+private:
+    typedef SkTypeface INHERITED;
 };
 
 class SkScalerContext_DW : public SkScalerContext {
@@ -816,7 +752,7 @@ void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
     run.isSideways = FALSE;
     run.glyphOffsets = &offset;
 
-    IDWriteFactory* factory = get_dwrite_factory();
+    IDWriteFactory* factory = sk_get_dwrite_factory();
     SkASSERT(factory != NULL);
 
     const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
@@ -1067,7 +1003,7 @@ void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
     HRV(dwFamilyNames->GetString(0, dwFamilyNameChar.get(), dwFamilyNamesLength+1));
 
     SkString utf8FamilyName;
-    HRV(wchar_to_skstring(dwFamilyNameChar.get(), &utf8FamilyName));
+    HRV(sk_wchar_to_skstring(dwFamilyNameChar.get(), &utf8FamilyName));
 
     desc->setFamilyName(utf8FamilyName.c_str());
     *isLocalStream = SkToBool(fDWriteFontFileLoader.get());
@@ -1177,7 +1113,7 @@ public:
         SkSMallocWCHAR wString(stringLength);
         HRBM(fStrings->GetString(fIndex, wString.get(), stringLength), "Could not get string.");
 
-        HRB(wchar_to_skstring(wString.get(), &localizedString->fString));
+        HRB(sk_wchar_to_skstring(wString.get(), &localizedString->fString));
 
         // Locale
         UINT32 localeLength;
@@ -1187,7 +1123,7 @@ public:
         SkSMallocWCHAR wLocale(localeLength);
         HRBM(fStrings->GetLocaleName(fIndex, wLocale.get(), localeLength), "Could not get locale.");
 
-        HRB(wchar_to_skstring(wLocale.get(), &localizedString->fLanguage));
+        HRB(sk_wchar_to_skstring(wLocale.get(), &localizedString->fLanguage));
 
         ++fIndex;
         return true;
@@ -1292,7 +1228,7 @@ private:
 };
 
 static SkTypeface* create_from_stream(SkStream* stream, int ttcIndex) {
-    IDWriteFactory* factory = get_dwrite_factory();
+    IDWriteFactory* factory = sk_get_dwrite_factory();
     if (NULL == factory) {
         return NULL;
     }
@@ -1531,7 +1467,7 @@ SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics(
     wFamilyName[familyNameLength] = L' ';
     hr = faceNames->GetString(0, &wFamilyName[familyNameLength+1], size - faceNameLength + 1);
 
-    hr = wchar_to_skstring(wFamilyName.get(), &info->fFontName);
+    hr = sk_wchar_to_skstring(wFamilyName.get(), &info->fFontName);
 
     if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
         populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
@@ -1648,28 +1584,6 @@ SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics(
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static void get_locale_string(IDWriteLocalizedStrings* names, const WCHAR* preferedLocale,
-                              SkString* skname) {
-    UINT32 nameIndex = 0;
-    if (preferedLocale) {
-        // Ignore any errors and continue with index 0 if there is a problem.
-        BOOL nameExists;
-        names->FindLocaleName(preferedLocale, &nameIndex, &nameExists);
-        if (!nameExists) {
-            nameIndex = 0;
-        }
-    }
-
-    UINT32 nameLength;
-    HRVM(names->GetStringLength(nameIndex, &nameLength), "Could not get name length.");
-    nameLength += 1;
-
-    SkSMallocWCHAR name(nameLength);
-    HRVM(names->GetString(nameIndex, name.get(), nameLength), "Could not get string.");
-
-    HRV(wchar_to_skstring(name.get(), skname));
-}
-
 SkTypeface* SkFontMgr_DirectWrite::createTypefaceFromDWriteFont(
         IDWriteFontFace* fontFace,
         IDWriteFont* font,
@@ -1698,7 +1612,7 @@ void SkFontMgr_DirectWrite::onGetFamilyName(int index, SkString* familyName) con
     SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
     HRVM(fontFamily->GetFamilyNames(&familyNames), "Could not get family names.");
 
-    get_locale_string(familyNames.get(), fLocaleName.get(), familyName);
+    sk_get_locale_string(familyNames.get(), fLocaleName.get(), familyName);
 }
 
 SkFontStyleSet* SkFontMgr_DirectWrite::onCreateStyleSet(int index) const {
@@ -1710,7 +1624,7 @@ SkFontStyleSet* SkFontMgr_DirectWrite::onCreateStyleSet(int index) const {
 
 SkFontStyleSet* SkFontMgr_DirectWrite::onMatchFamily(const char familyName[]) const {
     SkSMallocWCHAR dwFamilyName;
-    HRN(cstring_to_wchar(familyName, &dwFamilyName));
+    HRN(sk_cstring_to_wchar(familyName, &dwFamilyName));
 
     UINT32 index;
     BOOL exists;
@@ -1785,7 +1699,7 @@ SkTypeface* SkFontMgr_DirectWrite::onLegacyCreateTypeface(const char familyName[
     SkTScopedComPtr<IDWriteFontFamily> fontFamily;
     if (familyName) {
         SkSMallocWCHAR wideFamilyName;
-        if (SUCCEEDED(cstring_to_wchar(familyName, &wideFamilyName))) {
+        if (SUCCEEDED(sk_cstring_to_wchar(familyName, &wideFamilyName))) {
             this->getByFamilyName(wideFamilyName, &fontFamily);
         }
     }
@@ -1855,7 +1769,7 @@ void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString*
     if (styleName) {
         SkTScopedComPtr<IDWriteLocalizedStrings> faceNames;
         if (SUCCEEDED(font->GetFaceNames(&faceNames))) {
-            get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName);
+            sk_get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName);
         }
     }
 }
@@ -1879,7 +1793,7 @@ SkTypeface* SkFontStyleSet_DirectWrite::matchStyle(const SkFontStyle& pattern) {
     SkTScopedComPtr<IDWriteFont> font;
     // TODO: perhaps use GetMatchingFonts and get the least simulated?
     HRNM(fFontFamily->GetFirstMatchingFont(weight, width, slant, &font),
-            "Could not match font in family.");
+         "Could not match font in family.");
 
     SkTScopedComPtr<IDWriteFontFace> fontFace;
     HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
@@ -1890,23 +1804,8 @@ SkTypeface* SkFontStyleSet_DirectWrite::matchStyle(const SkFontStyle& pattern) {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-typedef decltype(GetUserDefaultLocaleName)* GetUserDefaultLocaleNameProc;
-static HRESULT GetGetUserDefaultLocaleNameProc(GetUserDefaultLocaleNameProc* proc) {
-    *proc = reinterpret_cast<GetUserDefaultLocaleNameProc>(
-        GetProcAddress(LoadLibraryW(L"Kernel32.dll"), "GetUserDefaultLocaleName")
-    );
-    if (!*proc) {
-        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
-        if (!IS_ERROR(hr)) {
-            hr = ERROR_PROC_NOT_FOUND;
-        }
-        return hr;
-    }
-    return S_OK;
-}
-
 SkFontMgr* SkFontMgr_New_DirectWrite() {
-    IDWriteFactory* factory = get_dwrite_factory();
+    IDWriteFactory* factory = sk_get_dwrite_factory();
     if (NULL == factory) {
         return NULL;
     }
@@ -1920,8 +1819,8 @@ SkFontMgr* SkFontMgr_New_DirectWrite() {
     int localeNameLen = 0;
 
     // Dynamically load GetUserDefaultLocaleName function, as it is not available on XP.
-    GetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = NULL;
-    HRESULT hr = GetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc);
+    SkGetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = NULL;
+    HRESULT hr = SkGetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc);
     if (NULL == getUserDefaultLocaleNameProc) {
         SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName.");
     } else {
@@ -1933,3 +1832,12 @@ SkFontMgr* SkFontMgr_New_DirectWrite() {
 
     return SkNEW_ARGS(SkFontMgr_DirectWrite, (sysFontCollection.get(), localeName, localeNameLen));
 }
+
+#include "SkFontMgr_indirect.h"
+SkFontMgr* SkFontMgr_New_DirectWriteRenderer(SkRemotableFontMgr* proxy) {
+    SkAutoTUnref<SkFontMgr> impl(SkFontMgr_New_DirectWrite());
+    if (impl.get() == NULL) {
+        return NULL;
+    }
+    return SkNEW_ARGS(SkFontMgr_Indirect, (impl.get(), proxy));
+}
diff --git a/src/ports/SkRemotableFontMgr_win_dw.cpp b/src/ports/SkRemotableFontMgr_win_dw.cpp
new file mode 100644 (file)
index 0000000..50fc5d4
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDataTable.h"
+#include "SkDWrite.h"
+#include "SkDWriteFontFileStream.h"
+#include "SkHRESULT.h"
+#include "SkRemotableFontMgr.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTArray.h"
+#include "SkThread.h"
+#include "SkTScopedComPtr.h"
+#include "SkTypeface_win.h"
+#include "SkTypes.h"
+#include "SkUtils.h"
+
+#include <dwrite.h>
+
+struct DWriteStyle {
+    explicit DWriteStyle(const SkFontStyle& pattern) {
+        switch (pattern.slant()) {
+        case SkFontStyle::kUpright_Slant:
+            fSlant = DWRITE_FONT_STYLE_NORMAL;
+            break;
+        case SkFontStyle::kItalic_Slant:
+            fSlant = DWRITE_FONT_STYLE_ITALIC;
+            break;
+        default:
+            SkASSERT(false);
+        }
+
+        fWeight = (DWRITE_FONT_WEIGHT)pattern.weight();
+        fWidth = (DWRITE_FONT_STRETCH)pattern.width();
+    }
+    DWRITE_FONT_STYLE fSlant;
+    DWRITE_FONT_WEIGHT fWeight;
+    DWRITE_FONT_STRETCH fWidth;
+};
+
+class SK_API SkRemotableFontMgr_DirectWrite : public SkRemotableFontMgr {
+private:
+    struct DataId {
+        IUnknown* fLoader;  // In COM only IUnknown pointers may be safely used for identity.
+        void* fKey;
+        UINT32 fKeySize;
+
+        DataId() { }
+
+        // This is actually a move!!!
+        explicit DataId(DataId& that)
+            : fLoader(that.fLoader), fKey(that.fKey), fKeySize(that.fKeySize)
+        {
+            that.fLoader = NULL;
+            that.fKey = NULL;
+            SkDEBUGCODE(that.fKeySize = 0xFFFFFFFF;)
+        }
+
+        ~DataId() {
+            if (fLoader) {
+                fLoader->Release();
+            }
+            sk_free(fKey);
+        }
+    };
+
+    mutable SkTArray<DataId> fDataIdCache;
+    mutable SkMutex fDataIdCacheMutex;
+
+    int FindOrAdd(IDWriteFontFileLoader* fontFileLoader,
+                  const void* refKey, UINT32 refKeySize) const
+    {
+        SkTScopedComPtr<IUnknown> fontFileLoaderId;
+        HR_GENERAL(fontFileLoader->QueryInterface(&fontFileLoaderId),
+                   "Failed to re-convert to IDWriteFontFileLoader.",
+                   SkFontIdentity::kInvalidDataId);
+
+        SkAutoMutexAcquire ama(fDataIdCacheMutex);
+        int count = fDataIdCache.count();
+        int i;
+        for (i = 0; i < count; ++i) {
+            const DataId& current = fDataIdCache[i];
+            if (fontFileLoaderId.get() == current.fLoader &&
+                refKeySize == current.fKeySize &&
+                0 == memcmp(refKey, current.fKey, refKeySize))
+            {
+                return i;
+            }
+        }
+        DataId& added = fDataIdCache.push_back();
+        added.fLoader = fontFileLoaderId.release();  // Ref is passed.
+        added.fKey = sk_malloc_throw(refKeySize);
+        memcpy(added.fKey, refKey, refKeySize);
+        added.fKeySize = refKeySize;
+
+        return i;
+    }
+
+public:
+    SK_DECLARE_INST_COUNT(SkRemotableFontMgr_DirectWrite)
+
+    /** localeNameLength must include the null terminator. */
+    SkRemotableFontMgr_DirectWrite(IDWriteFontCollection* fontCollection,
+                                   WCHAR* localeName, int localeNameLength)
+        : fFontCollection(SkRefComPtr(fontCollection))
+        , fLocaleName(localeNameLength)
+    {
+        memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR));
+    }
+
+    virtual SkDataTable* getFamilyNames() const SK_OVERRIDE {
+        int count = fFontCollection->GetFontFamilyCount();
+
+        SkDataTableBuilder names(1024);
+        for (int index = 0; index < count; ++index) {
+            SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+            HRNM(fFontCollection->GetFontFamily(index, &fontFamily),
+                 "Could not get requested family.");
+
+            SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
+            HRNM(fontFamily->GetFamilyNames(&familyNames), "Could not get family names.");
+
+            SkString familyName;
+            sk_get_locale_string(familyNames.get(), fLocaleName.get(), &familyName);
+
+            names.appendString(familyName);
+        }
+        return names.detachDataTable();
+    }
+
+    HRESULT FontToIdentity(IDWriteFont* font, SkFontIdentity* fontId) const {
+        SkTScopedComPtr<IDWriteFontFace> fontFace;
+        HRM(font->CreateFontFace(&fontFace), "Could not create font face.");
+
+        UINT32 numFiles;
+        HR(fontFace->GetFiles(&numFiles, NULL));
+        if (numFiles > 1) {
+            return E_FAIL;
+        }
+
+        // data id
+        SkTScopedComPtr<IDWriteFontFile> fontFile;
+        HR(fontFace->GetFiles(&numFiles, &fontFile));
+
+        SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
+        HR(fontFile->GetLoader(&fontFileLoader));
+
+        const void* refKey;
+        UINT32 refKeySize;
+        HR(fontFile->GetReferenceKey(&refKey, &refKeySize));
+
+        fontId->fDataId = FindOrAdd(fontFileLoader.get(), refKey, refKeySize);
+
+        // index
+        fontId->fTtcIndex = fontFace->GetIndex();
+
+        // style
+        SkFontStyle::Slant slant;
+        switch (font->GetStyle()) {
+        case DWRITE_FONT_STYLE_NORMAL:
+            slant = SkFontStyle::kUpright_Slant;
+            break;
+        case DWRITE_FONT_STYLE_OBLIQUE:
+        case DWRITE_FONT_STYLE_ITALIC:
+            slant = SkFontStyle::kItalic_Slant;
+            break;
+        default:
+            SkASSERT(false);
+        }
+
+        int weight = font->GetWeight();
+        int width = font->GetStretch();
+
+        fontId->fFontStyle = SkFontStyle(weight, width, slant);
+        return S_OK;
+    }
+
+    virtual SkRemotableFontIdentitySet* getIndex(int familyIndex) const SK_OVERRIDE {
+        SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+        HRNM(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
+             "Could not get requested family.");
+
+        int count = fontFamily->GetFontCount();
+        SkFontIdentity* fontIds;
+        SkAutoTUnref<SkRemotableFontIdentitySet> fontIdSet(
+            new SkRemotableFontIdentitySet(count, &fontIds));
+        for (int fontIndex = 0; fontIndex < count; ++fontIndex) {
+            SkTScopedComPtr<IDWriteFont> font;
+            HRNM(fontFamily->GetFont(fontIndex, &font), "Could not get font.");
+
+            HRN(FontToIdentity(font.get(), &fontIds[fontIndex]));
+        }
+        return fontIdSet.detach();
+    }
+
+    virtual SkFontIdentity matchIndexStyle(int familyIndex,
+                                           const SkFontStyle& pattern) const SK_OVERRIDE
+    {
+        SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
+
+        SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+        HR_GENERAL(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
+                   "Could not get requested family.",
+                   identity);
+
+        const DWriteStyle dwStyle(pattern);
+        SkTScopedComPtr<IDWriteFont> font;
+        HR_GENERAL(fontFamily->GetFirstMatchingFont(dwStyle.fWeight, dwStyle.fWidth,
+                                                    dwStyle.fSlant, &font),
+                   "Could not match font in family.",
+                   identity);
+
+        HR_GENERAL(FontToIdentity(font.get(), &identity), NULL, identity);
+
+        return identity;
+    }
+
+    static HRESULT getDefaultFontFamilyName(SkSMallocWCHAR* name) {
+        NONCLIENTMETRICSW metrics;
+        metrics.cbSize = sizeof(metrics);
+        if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
+                                       sizeof(metrics),
+                                       &metrics,
+                                       0)) {
+            return E_UNEXPECTED;
+        }
+
+        size_t len = wcsnlen_s(metrics.lfMessageFont.lfFaceName, LF_FACESIZE) + 1;
+        if (0 != wcsncpy_s(name->reset(len), len, metrics.lfMessageFont.lfFaceName, _TRUNCATE)) {
+            return E_UNEXPECTED;
+        }
+
+        return S_OK;
+    }
+
+    virtual SkRemotableFontIdentitySet* matchName(const char familyName[]) const SK_OVERRIDE {
+        SkSMallocWCHAR dwFamilyName;
+        if (NULL == familyName) {
+            HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName),
+                       NULL, SkRemotableFontIdentitySet::NewEmpty());
+        } else {
+            HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName),
+                       NULL, SkRemotableFontIdentitySet::NewEmpty());
+        }
+
+        UINT32 index;
+        BOOL exists;
+        HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
+                   "Failed while finding family by name.",
+                   SkRemotableFontIdentitySet::NewEmpty());
+        if (!exists) {
+            return SkRemotableFontIdentitySet::NewEmpty();
+        }
+
+        return this->getIndex(index);
+    }
+
+    virtual SkFontIdentity matchNameStyle(const char familyName[],
+                                          const SkFontStyle& style) const SK_OVERRIDE
+    {
+        SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
+
+        SkSMallocWCHAR dwFamilyName;
+        if (NULL == familyName) {
+            HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), NULL, identity);
+        } else {
+            HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), NULL, identity);
+        }
+
+        UINT32 index;
+        BOOL exists;
+        HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
+                   "Failed while finding family by name.",
+                   identity);
+        if (!exists) {
+            return identity;
+        }
+
+        return this->matchIndexStyle(index, style);
+    }
+
+    class FontFallbackRenderer : public IDWriteTextRenderer {
+    public:
+        FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite* outer, UINT32 character)
+            : fRefCount(1), fOuter(SkSafeRef(outer)), fCharacter(character) {
+          fIdentity.fDataId = SkFontIdentity::kInvalidDataId;
+        }
+
+        // IDWriteTextRenderer methods
+        virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(
+            void* clientDrawingContext,
+            FLOAT baselineOriginX,
+            FLOAT baselineOriginY,
+            DWRITE_MEASURING_MODE measuringMode,
+            DWRITE_GLYPH_RUN const* glyphRun,
+            DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+            IUnknown* clientDrawingEffect) SK_OVERRIDE
+        {
+            SkTScopedComPtr<IDWriteFont> font;
+            HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font),
+                "Could not get font from font face.");
+
+            // It is possible that the font passed does not actually have the requested character,
+            // due to no font being found and getting the fallback font.
+            // Check that the font actually contains the requested character.
+            BOOL exists;
+            HRM(font->HasCharacter(fCharacter, &exists), "Could not find character.");
+
+            if (exists) {
+                HR(fOuter->FontToIdentity(font.get(), &fIdentity));
+            }
+
+            return S_OK;
+        }
+
+        virtual HRESULT STDMETHODCALLTYPE DrawUnderline(
+            void* clientDrawingContext,
+            FLOAT baselineOriginX,
+            FLOAT baselineOriginY,
+            DWRITE_UNDERLINE const* underline,
+            IUnknown* clientDrawingEffect) SK_OVERRIDE
+        { return E_NOTIMPL; }
+
+        virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(
+            void* clientDrawingContext,
+            FLOAT baselineOriginX,
+            FLOAT baselineOriginY,
+            DWRITE_STRIKETHROUGH const* strikethrough,
+            IUnknown* clientDrawingEffect) SK_OVERRIDE
+        { return E_NOTIMPL; }
+
+        virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(
+            void* clientDrawingContext,
+            FLOAT originX,
+            FLOAT originY,
+            IDWriteInlineObject* inlineObject,
+            BOOL isSideways,
+            BOOL isRightToLeft,
+            IUnknown* clientDrawingEffect) SK_OVERRIDE
+        { return E_NOTIMPL; }
+
+        // IDWritePixelSnapping methods
+        virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
+            void* clientDrawingContext,
+            BOOL* isDisabled) SK_OVERRIDE
+        {
+            *isDisabled = FALSE;
+            return S_OK;
+        }
+
+        virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(
+            void* clientDrawingContext,
+            DWRITE_MATRIX* transform) SK_OVERRIDE
+        {
+            const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
+            *transform = ident;
+            return S_OK;
+        }
+
+        virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(
+            void* clientDrawingContext,
+            FLOAT* pixelsPerDip) SK_OVERRIDE
+        {
+            *pixelsPerDip = 1.0f;
+            return S_OK;
+        }
+
+        // IUnknown methods
+        virtual ULONG STDMETHODCALLTYPE AddRef() SK_OVERRIDE {
+            return InterlockedIncrement(&fRefCount);
+        }
+
+        virtual ULONG STDMETHODCALLTYPE Release() SK_OVERRIDE {
+            ULONG newCount = InterlockedDecrement(&fRefCount);
+            if (0 == newCount) {
+                delete this;
+            }
+            return newCount;
+        }
+
+        virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+            IID const& riid, void** ppvObject) SK_OVERRIDE 
+        {
+            if (__uuidof(IUnknown) == riid ||
+                __uuidof(IDWritePixelSnapping) == riid ||
+                __uuidof(IDWriteTextRenderer) == riid)
+            {
+                *ppvObject = this;
+                this->AddRef();
+                return S_OK;
+            }
+            *ppvObject = NULL;
+            return E_FAIL;
+        }
+
+        const SkFontIdentity FallbackIdentity() { return fIdentity; }
+
+    protected:
+        ULONG fRefCount;
+        SkAutoTUnref<const SkRemotableFontMgr_DirectWrite> fOuter;
+        UINT32 fCharacter;
+        SkFontIdentity fIdentity;
+    };
+
+    virtual SkFontIdentity matchNameStyleCharacter(const char familyName[],
+                                                   const SkFontStyle& pattern,
+                                                   const char bpc47[],
+                                                   SkUnichar character) const  SK_OVERRIDE
+    {
+        SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
+
+        IDWriteFactory* dwFactory = sk_get_dwrite_factory();
+        if (NULL == dwFactory) {
+            return identity;
+        }
+
+        // TODO: use IDWriteFactory2::GetSystemFontFallback when available.
+
+        const DWriteStyle dwStyle(pattern);
+
+        SkSMallocWCHAR dwFamilyName;
+        if (NULL == familyName) {
+            HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), NULL, identity);
+        } else {
+            HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), NULL, identity);
+        }
+
+        const SkSMallocWCHAR* dwBpc47;
+        SkSMallocWCHAR dwBpc47Local;
+        if (NULL == bpc47) {
+            dwBpc47 = &fLocaleName;
+        } else {
+            HR_GENERAL(sk_cstring_to_wchar(bpc47, &dwBpc47Local), NULL, identity);
+            dwBpc47 = &dwBpc47Local;
+        }
+
+        SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
+        HR_GENERAL(dwFactory->CreateTextFormat(dwFamilyName,
+                                               fFontCollection.get(),
+                                               dwStyle.fWeight,
+                                               dwStyle.fSlant,
+                                               dwStyle.fWidth,
+                                               72.0f,
+                                               *dwBpc47,
+                                               &fallbackFormat),
+                   "Could not create text format.",
+                   identity);
+
+        WCHAR str[16];
+        UINT32 strLen = SkUTF16_FromUnichar(character, reinterpret_cast<uint16_t*>(str));
+        SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
+        HR_GENERAL(dwFactory->CreateTextLayout(str, strLen, fallbackFormat.get(),
+                                               200.0f, 200.0f,
+                                               &fallbackLayout),
+                   "Could not create text layout.",
+                   identity);
+
+        SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer(
+            new FontFallbackRenderer(this, character));
+
+        HR_GENERAL(fallbackLayout->Draw(NULL, fontFallbackRenderer.get(), 50.0f, 50.0f),
+                   "Could not draw layout with renderer.",
+                   identity);
+
+        return fontFallbackRenderer->FallbackIdentity();
+    }
+
+    virtual SkStreamAsset* getData(int dataId) const SK_OVERRIDE {
+        SkAutoMutexAcquire ama(fDataIdCacheMutex);
+        if (dataId >= fDataIdCache.count()) {
+            return NULL;
+        }
+        const DataId& id = fDataIdCache[dataId];
+
+        SkTScopedComPtr<IDWriteFontFileLoader> loader;
+        HRNM(id.fLoader->QueryInterface(&loader), "QuerryInterface IDWriteFontFileLoader failed");
+
+        SkTScopedComPtr<IDWriteFontFileStream> fontFileStream;
+        HRNM(loader->CreateStreamFromKey(id.fKey, id.fKeySize, &fontFileStream),
+             "Could not create font file stream.");
+
+        return SkNEW_ARGS(SkDWriteFontFileStream, (fontFileStream.get()));
+    }
+
+private:
+    SkTScopedComPtr<IDWriteFontCollection> fFontCollection;
+    SkSMallocWCHAR fLocaleName;
+
+    typedef SkRemotableFontMgr INHERITED;
+};
+
+SkRemotableFontMgr* SkRemotableFontMgr_New_DirectWrite() {
+    IDWriteFactory* factory = sk_get_dwrite_factory();
+    if (NULL == factory) {
+        return NULL;
+    }
+
+    SkTScopedComPtr<IDWriteFontCollection> sysFontCollection;
+    HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE),
+         "Could not get system font collection.");
+
+    WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH];
+    WCHAR* localeName = NULL;
+    int localeNameLen = 0;
+
+    // Dynamically load GetUserDefaultLocaleName function, as it is not available on XP.
+    SkGetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = NULL;
+    HRESULT hr = SkGetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc);
+    if (NULL == getUserDefaultLocaleNameProc) {
+        SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName.");
+    } else {
+        localeNameLen = getUserDefaultLocaleNameProc(localeNameStorage, LOCALE_NAME_MAX_LENGTH);
+        if (localeNameLen) {
+            localeName = localeNameStorage;
+        };
+    }
+
+    return SkNEW_ARGS(SkRemotableFontMgr_DirectWrite, (sysFontCollection.get(),
+                                                       localeName, localeNameLen));
+}
diff --git a/src/utils/win/SkDWrite.cpp b/src/utils/win/SkDWrite.cpp
new file mode 100644 (file)
index 0000000..16e8ddc
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDWrite.h"
+#include "SkHRESULT.h"
+#include "SkOnce.h"
+#include "SkString.h"
+
+#include <dwrite.h>
+
+static IDWriteFactory* gDWriteFactory = NULL;
+
+static void create_dwrite_factory(IDWriteFactory** factory) {
+    typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
+    DWriteCreateFactoryProc dWriteCreateFactoryProc = reinterpret_cast<DWriteCreateFactoryProc>(
+        GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"));
+
+    if (!dWriteCreateFactoryProc) {
+        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
+        if (!IS_ERROR(hr)) {
+            hr = ERROR_PROC_NOT_FOUND;
+        }
+        HRVM(hr, "Could not get DWriteCreateFactory proc.");
+    }
+
+    HRVM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED,
+                                 __uuidof(IDWriteFactory),
+                                 reinterpret_cast<IUnknown**>(factory)),
+         "Could not create DirectWrite factory.");
+}
+
+static void release_dwrite_factory() {
+    if (gDWriteFactory) {
+        gDWriteFactory->Release();
+    }
+}
+
+IDWriteFactory* sk_get_dwrite_factory() {
+    SK_DECLARE_STATIC_ONCE(once);
+    SkOnce(&once, create_dwrite_factory, &gDWriteFactory, release_dwrite_factory);
+
+    return gDWriteFactory;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// String conversion
+
+/** Converts a utf8 string to a WCHAR string. */
+HRESULT sk_cstring_to_wchar(const char* skname, SkSMallocWCHAR* name) {
+    int wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, NULL, 0);
+    if (0 == wlen) {
+        HRM(HRESULT_FROM_WIN32(GetLastError()),
+            "Could not get length for wchar to utf-8 conversion.");
+    }
+    name->reset(wlen);
+    wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, name->get(), wlen);
+    if (0 == wlen) {
+        HRM(HRESULT_FROM_WIN32(GetLastError()), "Could not convert wchar to utf-8.");
+    }
+    return S_OK;
+}
+
+/** Converts a WCHAR string to a utf8 string. */
+HRESULT sk_wchar_to_skstring(WCHAR* name, SkString* skname) {
+    int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
+    if (0 == len) {
+        HRM(HRESULT_FROM_WIN32(GetLastError()),
+            "Could not get length for utf-8 to wchar conversion.");
+    }
+    skname->resize(len - 1);
+
+    // TODO: remove after https://code.google.com/p/skia/issues/detail?id=1989 is fixed.
+    // If we resize to 0 then the skname points to gEmptyRec (the unique empty SkString::Rec).
+    // gEmptyRec is static const and on Windows this means the value is in a read only page.
+    // Writing to it in the following call to WideCharToMultiByte will cause an access violation.
+    if (1 == len) {
+        return S_OK;
+    }
+
+    len = WideCharToMultiByte(CP_UTF8, 0, name, -1, skname->writable_str(), len, NULL, NULL);
+    if (0 == len) {
+        HRM(HRESULT_FROM_WIN32(GetLastError()), "Could not convert utf-8 to wchar.");
+    }
+    return S_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Locale
+
+void sk_get_locale_string(IDWriteLocalizedStrings* names, const WCHAR* preferedLocale,
+                          SkString* skname) {
+    UINT32 nameIndex = 0;
+    if (preferedLocale) {
+        // Ignore any errors and continue with index 0 if there is a problem.
+        BOOL nameExists;
+        names->FindLocaleName(preferedLocale, &nameIndex, &nameExists);
+        if (!nameExists) {
+            nameIndex = 0;
+        }
+    }
+
+    UINT32 nameLength;
+    HRVM(names->GetStringLength(nameIndex, &nameLength), "Could not get name length.");
+    nameLength += 1;
+
+    SkSMallocWCHAR name(nameLength);
+    HRVM(names->GetString(nameIndex, name.get(), nameLength), "Could not get string.");
+
+    HRV(sk_wchar_to_skstring(name.get(), skname));
+}
+
+HRESULT SkGetGetUserDefaultLocaleNameProc(SkGetUserDefaultLocaleNameProc* proc) {
+    *proc = reinterpret_cast<SkGetUserDefaultLocaleNameProc>(
+        GetProcAddress(LoadLibraryW(L"Kernel32.dll"), "GetUserDefaultLocaleName")
+    );
+    if (!*proc) {
+        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
+        if (!IS_ERROR(hr)) {
+            hr = ERROR_PROC_NOT_FOUND;
+        }
+        return hr;
+    }
+    return S_OK;
+}
diff --git a/src/utils/win/SkDWrite.h b/src/utils/win/SkDWrite.h
new file mode 100644 (file)
index 0000000..06e9c8b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTemplates.h"
+
+#include <dwrite.h>
+
+class SkString;
+
+////////////////////////////////////////////////////////////////////////////////
+// Factory
+
+IDWriteFactory* sk_get_dwrite_factory();
+
+////////////////////////////////////////////////////////////////////////////////
+// String conversion
+
+/** Prefer to use this type to prevent template proliferation. */
+typedef SkAutoSTMalloc<16, WCHAR> SkSMallocWCHAR;
+
+/** Converts a utf8 string to a WCHAR string. */
+HRESULT sk_cstring_to_wchar(const char* skname, SkSMallocWCHAR* name);
+
+/** Converts a WCHAR string to a utf8 string. */
+HRESULT sk_wchar_to_skstring(WCHAR* name, SkString* skname);
+
+////////////////////////////////////////////////////////////////////////////////
+// Locale
+
+void sk_get_locale_string(IDWriteLocalizedStrings* names, const WCHAR* preferedLocale,
+                       SkString* skname);
+
+typedef decltype(GetUserDefaultLocaleName)* SkGetUserDefaultLocaleNameProc;
+HRESULT SkGetGetUserDefaultLocaleNameProc(SkGetUserDefaultLocaleNameProc* proc);