A remotable font management interface and DirectWrite implementation.
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
diff --git a/gyp/ports.gyp b/gyp/ports.gyp
index 7ecda5f..ee32570 100644
--- a/gyp/ports.gyp
+++ b/gyp/ports.gyp
@@ -27,14 +27,20 @@
'../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',
@@ -52,6 +58,12 @@
'../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"]', {
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index d94f4a2..070d18a 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -120,6 +120,8 @@
'../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',
@@ -196,6 +198,8 @@
'../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',
diff --git a/include/ports/SkFontMgr.h b/include/ports/SkFontMgr.h
index df57aa7..a2fad9a 100644
--- a/include/ports/SkFontMgr.h
+++ b/include/ports/SkFontMgr.h
@@ -44,6 +44,9 @@
/**
* 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 @@
* 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 @@
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
index 0000000..52cb450
--- /dev/null
+++ b/include/ports/SkFontMgr_indirect.h
@@ -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
index 0000000..25a725b
--- /dev/null
+++ b/include/ports/SkRemotableFontMgr.h
@@ -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
+
diff --git a/include/ports/SkTypeface_win.h b/include/ports/SkTypeface_win.h
index e7dd4ab..77173e2 100644
--- a/include/ports/SkTypeface_win.h
+++ b/include/ports/SkTypeface_win.h
@@ -36,7 +36,25 @@
// 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
diff --git a/src/core/SkFontHost.cpp b/src/core/SkFontHost.cpp
index fa20b47..9e7eeb1 100644
--- a/src/core/SkFontHost.cpp
+++ b/src/core/SkFontHost.cpp
@@ -110,6 +110,12 @@
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 @@
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
index 0000000..bc8dce1
--- /dev/null
+++ b/src/fonts/SkFontMgr_indirect.cpp
@@ -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
index 0000000..1139972
--- /dev/null
+++ b/src/fonts/SkRemotableFontMgr.cpp
@@ -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;
+}
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 9dc720a..8ed3b66 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -284,15 +284,6 @@
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.
*/
static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
@@ -309,6 +300,15 @@
}
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;
};
diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp
index 39b83e9..462ca1d 100644
--- a/src/ports/SkFontHost_win_dw.cpp
+++ b/src/ports/SkFontHost_win_dw.cpp
@@ -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 @@
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 @@
};
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
};
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 @@
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 @@
///////////////////////////////////////////////////////////////////////////////
-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 @@
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::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 @@
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 @@
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 @@
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 @@
///////////////////////////////////////////////////////////////////////////////
-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 @@
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 @@
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
index 0000000..50fc5d4
--- /dev/null
+++ b/src/ports/SkRemotableFontMgr_win_dw.cpp
@@ -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
index 0000000..16e8ddc
--- /dev/null
+++ b/src/utils/win/SkDWrite.cpp
@@ -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
index 0000000..06e9c8b
--- /dev/null
+++ b/src/utils/win/SkDWrite.h
@@ -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);