diff options
author | bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2014-03-21 22:48:32 +0000 |
---|---|---|
committer | bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2014-03-21 22:48:32 +0000 |
commit | 72cf4fcafa54cfa04c5ec7cb8eaa3acb144712dd (patch) | |
tree | f6d0aff66805377067c2345deefcb6a832f29ef4 | |
parent | 34ce63ca6b853724c152a116e1467c0eead87003 (diff) |
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
-rw-r--r-- | gyp/ports.gyp | 12 | ||||
-rw-r--r-- | gyp/utils.gyp | 4 | ||||
-rw-r--r-- | include/ports/SkFontMgr.h | 22 | ||||
-rw-r--r-- | include/ports/SkFontMgr_indirect.h | 107 | ||||
-rw-r--r-- | include/ports/SkRemotableFontMgr.h | 153 | ||||
-rw-r--r-- | include/ports/SkTypeface_win.h | 18 | ||||
-rw-r--r-- | src/core/SkFontHost.cpp | 11 | ||||
-rw-r--r-- | src/fonts/SkFontMgr_indirect.cpp | 297 | ||||
-rw-r--r-- | src/fonts/SkRemotableFontMgr.cpp | 32 | ||||
-rwxr-xr-x | src/ports/SkFontHost_win.cpp | 18 | ||||
-rw-r--r-- | src/ports/SkFontHost_win_dw.cpp | 160 | ||||
-rw-r--r-- | src/ports/SkRemotableFontMgr_win_dw.cpp | 524 | ||||
-rw-r--r-- | src/utils/win/SkDWrite.cpp | 128 | ||||
-rw-r--r-- | src/utils/win/SkDWrite.h | 38 |
14 files changed, 1389 insertions, 135 deletions
diff --git a/gyp/ports.gyp b/gyp/ports.gyp index 7ecda5f35e..ee325706c0 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 d94f4a2d51..070d18a07e 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 df57aa7622..a2fad9aba6 100644 --- a/include/ports/SkFontMgr.h +++ b/include/ports/SkFontMgr.h @@ -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 index 0000000000..52cb450e99 --- /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 0000000000..25a725b728 --- /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 e7dd4ab50a..77173e2cf7 100644 --- a/include/ports/SkTypeface_win.h +++ b/include/ports/SkTypeface_win.h @@ -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 diff --git a/src/core/SkFontHost.cpp b/src/core/SkFontHost.cpp index fa20b47e10..9e7eeb182d 100644 --- a/src/core/SkFontHost.cpp +++ b/src/core/SkFontHost.cpp @@ -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 index 0000000000..bc8dce1e8f --- /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 0000000000..1139972848 --- /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 9dc720a643..8ed3b666aa 100755 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -284,15 +284,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. */ static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE 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; }; diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp index 39b83e9d1a..462ca1d3a6 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 @@ 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 index 0000000000..50fc5d4a38 --- /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 0000000000..16e8ddc4d0 --- /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 0000000000..06e9c8bcb7 --- /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); |