Home | History | Annotate | Download | only in ports
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 #include "SkTypes.h"
      8 #if defined(SK_BUILD_FOR_WIN)
      9 
     10 #include "SkDWrite.h"
     11 #include "SkDWriteFontFileStream.h"
     12 #include "SkHRESULT.h"
     13 #include "SkMutex.h"
     14 #include "SkRemotableFontMgr.h"
     15 #include "SkStream.h"
     16 #include "SkString.h"
     17 #include "SkTArray.h"
     18 #include "SkTScopedComPtr.h"
     19 #include "SkTypeface_win_dw.h"
     20 #include "SkTypes.h"
     21 #include "SkUtils.h"
     22 
     23 #include <dwrite.h>
     24 
     25 class SK_API SkRemotableFontMgr_DirectWrite : public SkRemotableFontMgr {
     26 private:
     27     struct DataId {
     28         IUnknown* fLoader;  // In COM only IUnknown pointers may be safely used for identity.
     29         void* fKey;
     30         UINT32 fKeySize;
     31 
     32         DataId() { }
     33 
     34         DataId(DataId&& that) : fLoader(that.fLoader), fKey(that.fKey), fKeySize(that.fKeySize) {
     35             that.fLoader = nullptr;
     36             that.fKey = nullptr;
     37             SkDEBUGCODE(that.fKeySize = 0xFFFFFFFF;)
     38         }
     39 
     40         ~DataId() {
     41             if (fLoader) {
     42                 fLoader->Release();
     43             }
     44             sk_free(fKey);
     45         }
     46     };
     47 
     48     mutable SkTArray<DataId> fDataIdCache;
     49     mutable SkMutex fDataIdCacheMutex;
     50 
     51     int FindOrAdd(IDWriteFontFileLoader* fontFileLoader,
     52                   const void* refKey, UINT32 refKeySize) const
     53     {
     54         SkTScopedComPtr<IUnknown> fontFileLoaderId;
     55         HR_GENERAL(fontFileLoader->QueryInterface(&fontFileLoaderId),
     56                    "Failed to re-convert to IDWriteFontFileLoader.",
     57                    SkFontIdentity::kInvalidDataId);
     58 
     59         SkAutoMutexAcquire ama(fDataIdCacheMutex);
     60         int count = fDataIdCache.count();
     61         int i;
     62         for (i = 0; i < count; ++i) {
     63             const DataId& current = fDataIdCache[i];
     64             if (fontFileLoaderId.get() == current.fLoader &&
     65                 refKeySize == current.fKeySize &&
     66                 0 == memcmp(refKey, current.fKey, refKeySize))
     67             {
     68                 return i;
     69             }
     70         }
     71         DataId& added = fDataIdCache.push_back();
     72         added.fLoader = fontFileLoaderId.release();  // Ref is passed.
     73         added.fKey = sk_malloc_throw(refKeySize);
     74         memcpy(added.fKey, refKey, refKeySize);
     75         added.fKeySize = refKeySize;
     76 
     77         return i;
     78     }
     79 
     80 public:
     81 
     82 
     83     /** localeNameLength must include the null terminator. */
     84     SkRemotableFontMgr_DirectWrite(IDWriteFontCollection* fontCollection,
     85                                    WCHAR* localeName, int localeNameLength)
     86         : fFontCollection(SkRefComPtr(fontCollection))
     87         , fLocaleName(localeNameLength)
     88     {
     89         memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR));
     90     }
     91 
     92     HRESULT FontToIdentity(IDWriteFont* font, SkFontIdentity* fontId) const {
     93         SkTScopedComPtr<IDWriteFontFace> fontFace;
     94         HRM(font->CreateFontFace(&fontFace), "Could not create font face.");
     95 
     96         UINT32 numFiles;
     97         HR(fontFace->GetFiles(&numFiles, nullptr));
     98         if (numFiles > 1) {
     99             return E_FAIL;
    100         }
    101 
    102         // data id
    103         SkTScopedComPtr<IDWriteFontFile> fontFile;
    104         HR(fontFace->GetFiles(&numFiles, &fontFile));
    105 
    106         SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
    107         HR(fontFile->GetLoader(&fontFileLoader));
    108 
    109         const void* refKey;
    110         UINT32 refKeySize;
    111         HR(fontFile->GetReferenceKey(&refKey, &refKeySize));
    112 
    113         fontId->fDataId = FindOrAdd(fontFileLoader.get(), refKey, refKeySize);
    114 
    115         // index
    116         fontId->fTtcIndex = fontFace->GetIndex();
    117 
    118         // style
    119         fontId->fFontStyle = get_style(font);
    120         return S_OK;
    121     }
    122 
    123     SkRemotableFontIdentitySet* getIndex(int familyIndex) const override {
    124         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
    125         HRNM(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
    126              "Could not get requested family.");
    127 
    128         int count = fontFamily->GetFontCount();
    129         SkFontIdentity* fontIds;
    130         sk_sp<SkRemotableFontIdentitySet> fontIdSet(
    131             new SkRemotableFontIdentitySet(count, &fontIds));
    132         for (int fontIndex = 0; fontIndex < count; ++fontIndex) {
    133             SkTScopedComPtr<IDWriteFont> font;
    134             HRNM(fontFamily->GetFont(fontIndex, &font), "Could not get font.");
    135 
    136             HRN(FontToIdentity(font.get(), &fontIds[fontIndex]));
    137         }
    138         return fontIdSet.release();
    139     }
    140 
    141     virtual SkFontIdentity matchIndexStyle(int familyIndex,
    142                                            const SkFontStyle& pattern) const override
    143     {
    144         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
    145 
    146         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
    147         HR_GENERAL(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
    148                    "Could not get requested family.",
    149                    identity);
    150 
    151         const DWriteStyle dwStyle(pattern);
    152         SkTScopedComPtr<IDWriteFont> font;
    153         HR_GENERAL(fontFamily->GetFirstMatchingFont(dwStyle.fWeight, dwStyle.fWidth,
    154                                                     dwStyle.fSlant, &font),
    155                    "Could not match font in family.",
    156                    identity);
    157 
    158         HR_GENERAL(FontToIdentity(font.get(), &identity), nullptr, identity);
    159 
    160         return identity;
    161     }
    162 
    163     static HRESULT getDefaultFontFamilyName(SkSMallocWCHAR* name) {
    164         NONCLIENTMETRICSW metrics;
    165         metrics.cbSize = sizeof(metrics);
    166         if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
    167                                        sizeof(metrics),
    168                                        &metrics,
    169                                        0)) {
    170             return E_UNEXPECTED;
    171         }
    172 
    173         size_t len = wcsnlen_s(metrics.lfMessageFont.lfFaceName, LF_FACESIZE) + 1;
    174         if (0 != wcsncpy_s(name->reset(len), len, metrics.lfMessageFont.lfFaceName, _TRUNCATE)) {
    175             return E_UNEXPECTED;
    176         }
    177 
    178         return S_OK;
    179     }
    180 
    181     SkRemotableFontIdentitySet* matchName(const char familyName[]) const override {
    182         SkSMallocWCHAR dwFamilyName;
    183         if (nullptr == familyName) {
    184             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName),
    185                        nullptr, SkRemotableFontIdentitySet::NewEmpty());
    186         } else {
    187             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName),
    188                        nullptr, SkRemotableFontIdentitySet::NewEmpty());
    189         }
    190 
    191         UINT32 index;
    192         BOOL exists;
    193         HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
    194                    "Failed while finding family by name.",
    195                    SkRemotableFontIdentitySet::NewEmpty());
    196         if (!exists) {
    197             return SkRemotableFontIdentitySet::NewEmpty();
    198         }
    199 
    200         return this->getIndex(index);
    201     }
    202 
    203     virtual SkFontIdentity matchNameStyle(const char familyName[],
    204                                           const SkFontStyle& style) const override
    205     {
    206         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
    207 
    208         SkSMallocWCHAR dwFamilyName;
    209         if (nullptr == familyName) {
    210             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), nullptr, identity);
    211         } else {
    212             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), nullptr, identity);
    213         }
    214 
    215         UINT32 index;
    216         BOOL exists;
    217         HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
    218                    "Failed while finding family by name.",
    219                    identity);
    220         if (!exists) {
    221             return identity;
    222         }
    223 
    224         return this->matchIndexStyle(index, style);
    225     }
    226 
    227     class FontFallbackRenderer : public IDWriteTextRenderer {
    228     public:
    229         FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite* outer, UINT32 character)
    230             : fRefCount(1), fOuter(SkSafeRef(outer)), fCharacter(character) {
    231           fIdentity.fDataId = SkFontIdentity::kInvalidDataId;
    232         }
    233 
    234         virtual ~FontFallbackRenderer() { }
    235 
    236         // IDWriteTextRenderer methods
    237         virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(
    238             void* clientDrawingContext,
    239             FLOAT baselineOriginX,
    240             FLOAT baselineOriginY,
    241             DWRITE_MEASURING_MODE measuringMode,
    242             DWRITE_GLYPH_RUN const* glyphRun,
    243             DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
    244             IUnknown* clientDrawingEffect) override
    245         {
    246             SkTScopedComPtr<IDWriteFont> font;
    247             HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font),
    248                 "Could not get font from font face.");
    249 
    250             // It is possible that the font passed does not actually have the requested character,
    251             // due to no font being found and getting the fallback font.
    252             // Check that the font actually contains the requested character.
    253             BOOL exists;
    254             HRM(font->HasCharacter(fCharacter, &exists), "Could not find character.");
    255 
    256             if (exists) {
    257                 HR(fOuter->FontToIdentity(font.get(), &fIdentity));
    258             }
    259 
    260             return S_OK;
    261         }
    262 
    263         virtual HRESULT STDMETHODCALLTYPE DrawUnderline(
    264             void* clientDrawingContext,
    265             FLOAT baselineOriginX,
    266             FLOAT baselineOriginY,
    267             DWRITE_UNDERLINE const* underline,
    268             IUnknown* clientDrawingEffect) override
    269         { return E_NOTIMPL; }
    270 
    271         virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(
    272             void* clientDrawingContext,
    273             FLOAT baselineOriginX,
    274             FLOAT baselineOriginY,
    275             DWRITE_STRIKETHROUGH const* strikethrough,
    276             IUnknown* clientDrawingEffect) override
    277         { return E_NOTIMPL; }
    278 
    279         virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(
    280             void* clientDrawingContext,
    281             FLOAT originX,
    282             FLOAT originY,
    283             IDWriteInlineObject* inlineObject,
    284             BOOL isSideways,
    285             BOOL isRightToLeft,
    286             IUnknown* clientDrawingEffect) override
    287         { return E_NOTIMPL; }
    288 
    289         // IDWritePixelSnapping methods
    290         virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
    291             void* clientDrawingContext,
    292             BOOL* isDisabled) override
    293         {
    294             *isDisabled = FALSE;
    295             return S_OK;
    296         }
    297 
    298         virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(
    299             void* clientDrawingContext,
    300             DWRITE_MATRIX* transform) override
    301         {
    302             const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
    303             *transform = ident;
    304             return S_OK;
    305         }
    306 
    307         virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(
    308             void* clientDrawingContext,
    309             FLOAT* pixelsPerDip) override
    310         {
    311             *pixelsPerDip = 1.0f;
    312             return S_OK;
    313         }
    314 
    315         // IUnknown methods
    316         ULONG STDMETHODCALLTYPE AddRef() override {
    317             return InterlockedIncrement(&fRefCount);
    318         }
    319 
    320         ULONG STDMETHODCALLTYPE Release() override {
    321             ULONG newCount = InterlockedDecrement(&fRefCount);
    322             if (0 == newCount) {
    323                 delete this;
    324             }
    325             return newCount;
    326         }
    327 
    328         virtual HRESULT STDMETHODCALLTYPE QueryInterface(
    329             IID const& riid, void** ppvObject) override
    330         {
    331             if (__uuidof(IUnknown) == riid ||
    332                 __uuidof(IDWritePixelSnapping) == riid ||
    333                 __uuidof(IDWriteTextRenderer) == riid)
    334             {
    335                 *ppvObject = this;
    336                 this->AddRef();
    337                 return S_OK;
    338             }
    339             *ppvObject = nullptr;
    340             return E_FAIL;
    341         }
    342 
    343         const SkFontIdentity FallbackIdentity() { return fIdentity; }
    344 
    345     protected:
    346         ULONG fRefCount;
    347         sk_sp<const SkRemotableFontMgr_DirectWrite> fOuter;
    348         UINT32 fCharacter;
    349         SkFontIdentity fIdentity;
    350     };
    351 
    352     virtual SkFontIdentity matchNameStyleCharacter(const char familyName[],
    353                                                    const SkFontStyle& pattern,
    354                                                    const char* bcp47[], int bcp47Count,
    355                                                    SkUnichar character) const override
    356     {
    357         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
    358 
    359         IDWriteFactory* dwFactory = sk_get_dwrite_factory();
    360         if (nullptr == dwFactory) {
    361             return identity;
    362         }
    363 
    364         // TODO: use IDWriteFactory2::GetSystemFontFallback when available.
    365 
    366         const DWriteStyle dwStyle(pattern);
    367 
    368         SkSMallocWCHAR dwFamilyName;
    369         if (nullptr == familyName) {
    370             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), nullptr, identity);
    371         } else {
    372             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), nullptr, identity);
    373         }
    374 
    375         const SkSMallocWCHAR* dwBcp47;
    376         SkSMallocWCHAR dwBcp47Local;
    377         if (bcp47Count < 1) {
    378             dwBcp47 = &fLocaleName;
    379         } else {
    380             //TODO: support fallback stack.
    381             HR_GENERAL(sk_cstring_to_wchar(bcp47[bcp47Count-1], &dwBcp47Local), nullptr, identity);
    382             dwBcp47 = &dwBcp47Local;
    383         }
    384 
    385         SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
    386         HR_GENERAL(dwFactory->CreateTextFormat(dwFamilyName,
    387                                                fFontCollection.get(),
    388                                                dwStyle.fWeight,
    389                                                dwStyle.fSlant,
    390                                                dwStyle.fWidth,
    391                                                72.0f,
    392                                                *dwBcp47,
    393                                                &fallbackFormat),
    394                    "Could not create text format.",
    395                    identity);
    396 
    397         WCHAR str[16];
    398         UINT32 strLen = static_cast<UINT32>(
    399             SkUTF16_FromUnichar(character, reinterpret_cast<uint16_t*>(str)));
    400         SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
    401         HR_GENERAL(dwFactory->CreateTextLayout(str, strLen, fallbackFormat.get(),
    402                                                200.0f, 200.0f,
    403                                                &fallbackLayout),
    404                    "Could not create text layout.",
    405                    identity);
    406 
    407         SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer(
    408             new FontFallbackRenderer(this, character));
    409 
    410         HR_GENERAL(fallbackLayout->Draw(nullptr, fontFallbackRenderer.get(), 50.0f, 50.0f),
    411                    "Could not draw layout with renderer.",
    412                    identity);
    413 
    414         return fontFallbackRenderer->FallbackIdentity();
    415     }
    416 
    417     SkStreamAsset* getData(int dataId) const override {
    418         SkAutoMutexAcquire ama(fDataIdCacheMutex);
    419         if (dataId >= fDataIdCache.count()) {
    420             return nullptr;
    421         }
    422         const DataId& id = fDataIdCache[dataId];
    423 
    424         SkTScopedComPtr<IDWriteFontFileLoader> loader;
    425         HRNM(id.fLoader->QueryInterface(&loader), "QuerryInterface IDWriteFontFileLoader failed");
    426 
    427         SkTScopedComPtr<IDWriteFontFileStream> fontFileStream;
    428         HRNM(loader->CreateStreamFromKey(id.fKey, id.fKeySize, &fontFileStream),
    429              "Could not create font file stream.");
    430 
    431         return new SkDWriteFontFileStream(fontFileStream.get());
    432     }
    433 
    434 private:
    435     SkTScopedComPtr<IDWriteFontCollection> fFontCollection;
    436     SkSMallocWCHAR fLocaleName;
    437 
    438     typedef SkRemotableFontMgr INHERITED;
    439 };
    440 
    441 SkRemotableFontMgr* SkRemotableFontMgr_New_DirectWrite() {
    442     IDWriteFactory* factory = sk_get_dwrite_factory();
    443     if (nullptr == factory) {
    444         return nullptr;
    445     }
    446 
    447     SkTScopedComPtr<IDWriteFontCollection> sysFontCollection;
    448     HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE),
    449          "Could not get system font collection.");
    450 
    451     WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH];
    452     WCHAR* localeName = nullptr;
    453     int localeNameLen = 0;
    454 
    455     // Dynamically load GetUserDefaultLocaleName function, as it is not available on XP.
    456     SkGetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = nullptr;
    457     HRESULT hr = SkGetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc);
    458     if (nullptr == getUserDefaultLocaleNameProc) {
    459         SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName.");
    460     } else {
    461         localeNameLen = getUserDefaultLocaleNameProc(localeNameStorage, LOCALE_NAME_MAX_LENGTH);
    462         if (localeNameLen) {
    463             localeName = localeNameStorage;
    464         };
    465     }
    466 
    467     return new SkRemotableFontMgr_DirectWrite(sysFontCollection.get(), localeName, localeNameLen);
    468 }
    469 #endif//defined(SK_BUILD_FOR_WIN)
    470