/*
* 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>
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));
}
SkDataTable* getFamilyNames() const 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;
}
SkRemotableFontIdentitySet* getIndex(int familyIndex) const 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 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;
}
SkRemotableFontIdentitySet* matchName(const char familyName[]) const 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 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;
}
virtual ~FontFallbackRenderer() { }
// 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) 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) override
{ return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const* strikethrough,
IUnknown* clientDrawingEffect) override
{ return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(
void* clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject* inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown* clientDrawingEffect) override
{ return E_NOTIMPL; }
// IDWritePixelSnapping methods
virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
void* clientDrawingContext,
BOOL* isDisabled) override
{
*isDisabled = FALSE;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(
void* clientDrawingContext,
DWRITE_MATRIX* transform) 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) override
{
*pixelsPerDip = 1.0f;
return S_OK;
}
// IUnknown methods
ULONG STDMETHODCALLTYPE AddRef() override {
return InterlockedIncrement(&fRefCount);
}
ULONG STDMETHODCALLTYPE Release() override {
ULONG newCount = InterlockedDecrement(&fRefCount);
if (0 == newCount) {
delete this;
}
return newCount;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
IID const& riid, void** ppvObject) 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* bcp47[], int bcp47Count,
SkUnichar character) const 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* dwBcp47;
SkSMallocWCHAR dwBcp47Local;
if (bcp47Count < 1) {
dwBcp47 = &fLocaleName;
} else {
//TODO: support fallback stack.
HR_GENERAL(sk_cstring_to_wchar(bcp47[bcp47Count-1], &dwBcp47Local), NULL, identity);
dwBcp47 = &dwBcp47Local;
}
SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
HR_GENERAL(dwFactory->CreateTextFormat(dwFamilyName,
fFontCollection.get(),
dwStyle.fWeight,
dwStyle.fSlant,
dwStyle.fWidth,
72.0f,
*dwBcp47,
&fallbackFormat),
"Could not create text format.",
identity);
WCHAR str[16];
UINT32 strLen = static_cast<UINT32>(
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();
}
SkStreamAsset* getData(int dataId) const 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));
}