From 7103344b64f3f0df88e76857c16edc8eedb58366 Mon Sep 17 00:00:00 2001 From: "bungeman@google.com" Date: Wed, 1 May 2013 14:21:20 +0000 Subject: [PATCH] Add FontMgr to DirectWrite. https://codereview.chromium.org/14314008/ git-svn-id: http://skia.googlecode.com/svn/trunk@8934 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/fontmgr.cpp | 6 +- include/core/SkTemplates.h | 16 +- include/utils/win/SkTScopedComPtr.h | 5 + src/gpu/gl/GrGLCaps.cpp | 14 +- src/ports/SkFontHost_win_dw.cpp | 309 +++++++++++++++++++++++----- tests/FontMgrTest.cpp | 2 + 6 files changed, 288 insertions(+), 64 deletions(-) diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp index 4ff55a7a6..70b91150d 100644 --- a/gm/fontmgr.cpp +++ b/gm/fontmgr.cpp @@ -71,7 +71,11 @@ protected: virtual uint32_t onGetFlags() const SK_OVERRIDE { // fontdescriptors (and therefore serialization) don't yet understand // these new styles, so skip tests that exercise that for now. - return kSkipPicture_Flag | kSkipPipe_Flag; + + // If certain fonts are picked up (e.g. Microsoft Jhenghei 20MB for Regular, 12MB for Bold), + // the resulting pdf can be ~700MB and crashes Chrome's PDF viewer. + + return kSkipPicture_Flag | kSkipPipe_Flag | kSkipPDF_Flag; } private: diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h index 24705dbed..566834cab 100644 --- a/include/core/SkTemplates.h +++ b/include/core/SkTemplates.h @@ -295,11 +295,17 @@ private: template class SK_API SkAutoSTMalloc : SkNoncopyable { public: + SkAutoSTMalloc() { + fPtr = NULL; + } + SkAutoSTMalloc(size_t count) { - if (count <= N) { + if (count > N) { + fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + } else if (count) { fPtr = fTStorage; } else { - fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + fPtr = NULL; } } @@ -314,10 +320,12 @@ public: if (fPtr != fTStorage) { sk_free(fPtr); } - if (count <= N) { + if (count > N) { + fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + } else if (count) { fPtr = fTStorage; } else { - fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); + fPtr = NULL; } } diff --git a/include/utils/win/SkTScopedComPtr.h b/include/utils/win/SkTScopedComPtr.h index 85c314a97..fa64ac480 100644 --- a/include/utils/win/SkTScopedComPtr.h +++ b/include/utils/win/SkTScopedComPtr.h @@ -19,6 +19,11 @@ private: virtual ULONG STDMETHODCALLTYPE Release(void) = 0; }; +template T* SkRefComPtr(T* ptr) { + ptr->AddRef(); + return ptr; +} + template class SkTScopedComPtr : SkNoncopyable { private: diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index b868d6a51..716989e0c 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -219,12 +219,14 @@ void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { GrGLint numFormats; GR_GL_GetIntegerv(gli, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats); - SkAutoSTMalloc<10, GrGLint> formats(numFormats); - GR_GL_GetIntegerv(gli, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats); - for (int i = 0; i < numFormats; ++i) { - if (formats[i] == GR_GL_PALETTE8_RGBA8) { - f8BitPaletteSupport = true; - break; + if (numFormats) { + SkAutoSTMalloc<10, GrGLint> formats(numFormats); + GR_GL_GetIntegerv(gli, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats); + for (int i = 0; i < numFormats; ++i) { + if (formats[i] == GR_GL_PALETTE8_RGBA8) { + f8BitPaletteSupport = true; + break; + } } } diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp index 489bad727..33fc32283 100644 --- a/src/ports/SkFontHost_win_dw.cpp +++ b/src/ports/SkFontHost_win_dw.cpp @@ -41,6 +41,37 @@ 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; + +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; +} + +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); + 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; +} + /////////////////////////////////////////////////////////////////////////////// class DWriteOffscreen { @@ -669,18 +700,18 @@ static bool FindByDWriteFont(SkTypeface* face, SkTypeface::Style requestedStyle, return false; } - SkTDArray dwFaceFontFamilyNameChar(new wchar_t[dwFaceFontFamilyNameLength+1], dwFaceFontFamilyNameLength+1); - SkTDArray dwFaceFontNameChar(new wchar_t[dwFaceFontNameLength+1], dwFaceFontNameLength+1); - HRB(dwFaceFontFamilyNames->GetString(0, dwFaceFontFamilyNameChar.begin(), dwFaceFontFamilyNameChar.count())); - HRB(dwFaceFontNames->GetString(0, dwFaceFontNameChar.begin(), dwFaceFontNameChar.count())); + SkSMallocWCHAR dwFaceFontFamilyNameChar(dwFaceFontFamilyNameLength+1); + SkSMallocWCHAR dwFaceFontNameChar(dwFaceFontNameLength+1); + HRB(dwFaceFontFamilyNames->GetString(0, dwFaceFontFamilyNameChar.get(), dwFaceFontFamilyNameLength+1)); + HRB(dwFaceFontNames->GetString(0, dwFaceFontNameChar.get(), dwFaceFontNameLength+1)); - SkTDArray dwFontFamilyNameChar(new wchar_t[dwFontFamilyNameLength+1], dwFontFamilyNameLength+1); - SkTDArray dwFontNameChar(new wchar_t[dwFontNameLength+1], dwFontNameLength+1); - HRB(dwFontFamilyNames->GetString(0, dwFontFamilyNameChar.begin(), dwFontFamilyNameChar.count())); - HRB(dwFontNames->GetString(0, dwFontNameChar.begin(), dwFontNameChar.count())); + SkSMallocWCHAR dwFontFamilyNameChar(dwFontFamilyNameLength+1); + SkSMallocWCHAR dwFontNameChar(dwFontNameLength+1); + HRB(dwFontFamilyNames->GetString(0, dwFontFamilyNameChar.get(), dwFontFamilyNameLength+1)); + HRB(dwFontNames->GetString(0, dwFontNameChar.get(), dwFontNameLength+1)); - return wcscmp(dwFaceFontFamilyNameChar.begin(), dwFontFamilyNameChar.begin()) == 0 && - wcscmp(dwFaceFontNameChar.begin(), dwFontNameChar.begin()) == 0; + return wcscmp(dwFaceFontFamilyNameChar.get(), dwFontFamilyNameChar.get()) == 0 && + wcscmp(dwFaceFontNameChar.get(), dwFontNameChar.get()) == 0; } SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFontFace* fontFace, @@ -1049,24 +1080,17 @@ void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, UINT32 dwFamilyNamesLength; HRV(dwFamilyNames->GetStringLength(0, &dwFamilyNamesLength)); - SkTDArray dwFamilyNameChar(new wchar_t[dwFamilyNamesLength+1], dwFamilyNamesLength+1); - HRV(dwFamilyNames->GetString(0, dwFamilyNameChar.begin(), dwFamilyNameChar.count())); + SkSMallocWCHAR dwFamilyNameChar(dwFamilyNamesLength+1); + HRV(dwFamilyNames->GetString(0, dwFamilyNameChar.get(), dwFamilyNamesLength+1)); - // Convert the family name to utf8. - // Get the buffer size needed first. - int str_len = WideCharToMultiByte(CP_UTF8, 0, dwFamilyNameChar.begin(), -1, - NULL, 0, NULL, NULL); - // Allocate a buffer (str_len already has terminating null accounted for). - SkTDArray utf8FamilyName(new char[str_len], str_len); - // Now actually convert the string. - str_len = WideCharToMultiByte(CP_UTF8, 0, dwFamilyNameChar.begin(), -1, - utf8FamilyName.begin(), str_len, NULL, NULL); + SkString utf8FamilyName; + HRV(wchar_to_skstring(dwFamilyNameChar.get(), &utf8FamilyName)); - desc->setFamilyName(utf8FamilyName.begin()); + desc->setFamilyName(utf8FamilyName.c_str()); *isLocalStream = SkToBool(fDWriteFontFileLoader.get()); } -SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { +static SkTypeface* create_from_stream(SkStream* stream) { IDWriteFactory* factory; HRN(get_dwrite_factory(&factory)); @@ -1095,6 +1119,10 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { fontFileLoader.get(), streamFontCollectionLoader.get()); } +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { + return create_from_stream(stream); +} + SkStream* DWriteFontTypeface::onOpenStream(int* ttcIndex) const { *ttcIndex = 0; @@ -1135,23 +1163,12 @@ static HRESULT get_by_family_name(const char familyName[], IDWriteFontFamily** f SkTScopedComPtr sysFontCollection; HR(factory->GetSystemFontCollection(&sysFontCollection, FALSE)); - // Get the buffer size needed first. - int wlen = ::MultiByteToWideChar(CP_UTF8, 0, familyName,-1, NULL, 0); - if (0 == wlen) { - return HRESULT_FROM_WIN32(GetLastError()); - } - // Allocate a buffer - SkTDArray wideFamilyName(new wchar_t[wlen], wlen); - // Now actually convert the string. - wlen = ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1, - wideFamilyName.begin(), wlen); - if (0 == wlen) { - return HRESULT_FROM_WIN32(GetLastError()); - } + SkSMallocWCHAR wideFamilyName; + HR(cstring_to_wchar(familyName, &wideFamilyName)); UINT32 index; BOOL exists; - HR(sysFontCollection->FindFamilyName(wideFamilyName.begin(), &index, &exists)); + HR(sysFontCollection->FindFamilyName(wideFamilyName.get(), &index, &exists)); if (exists) { HR(sysFontCollection->GetFontFamily(index, fontFamily)); @@ -1389,22 +1406,13 @@ SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( UINT32 faceNameLength; hr = faceNames->GetStringLength(0, &faceNameLength); - size_t size = familyNameLength+1+faceNameLength+1; - SkTDArray wFamilyName(new wchar_t[size], size); - hr = familyNames->GetString(0, wFamilyName.begin(), size); + UINT32 size = familyNameLength+1+faceNameLength+1; + SkSMallocWCHAR wFamilyName(size); + hr = familyNames->GetString(0, wFamilyName.get(), size); wFamilyName[familyNameLength] = L' '; hr = faceNames->GetString(0, &wFamilyName[familyNameLength+1], size - faceNameLength + 1); - size_t str_len = WideCharToMultiByte(CP_UTF8, 0, wFamilyName.begin(), -1, NULL, 0, NULL, NULL); - if (0 == str_len) { - //TODO: error - } - SkTDArray familyName(new char[str_len], str_len); - str_len = WideCharToMultiByte(CP_UTF8, 0, wFamilyName.begin(), -1, familyName.begin(), str_len, NULL, NULL); - if (0 == str_len) { - //TODO: error - } - info->fFontName.set(familyName.begin(), str_len); + hr = wchar_to_skstring(wFamilyName.get(), &info->fFontName); if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) { populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode)); @@ -1523,7 +1531,202 @@ SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( #include "SkFontMgr.h" -SkFontMgr* SkFontMgr::Factory() { - // todo - return NULL; +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)); +} + +class SkFontMgr_DirectWrite; + +class SkFontStyleSet_DirectWrite : public SkFontStyleSet { +public: + SkFontStyleSet_DirectWrite(const SkFontMgr_DirectWrite* fontMgr, IDWriteFontFamily* fontFamily) + : fFontMgr(SkRef(fontMgr)) + , fFontFamily(SkRefComPtr(fontFamily)) + { } + + virtual int count() SK_OVERRIDE { + return fFontFamily->GetFontCount(); + } + + virtual void getStyle(int index, SkFontStyle* fs, SkString* styleName); + + virtual SkTypeface* createTypeface(int index) SK_OVERRIDE { + SkTScopedComPtr font; + HRNM(fFontFamily->GetFont(index, &font), "Could not get font."); + + SkTScopedComPtr fontFace; + HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); + + return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fFontFamily.get()); + } + + virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE { + DWRITE_FONT_STYLE slant; + switch (pattern.slant()) { + case SkFontStyle::kUpright_Slant: + slant = DWRITE_FONT_STYLE_NORMAL; + break; + case SkFontStyle::kItalic_Slant: + slant = DWRITE_FONT_STYLE_ITALIC; + break; + default: + SkASSERT(false); + } + + DWRITE_FONT_WEIGHT weight = (DWRITE_FONT_WEIGHT)pattern.weight(); + DWRITE_FONT_STRETCH width = (DWRITE_FONT_STRETCH)pattern.width(); + + SkTScopedComPtr font; + HRNM(fFontFamily->GetFirstMatchingFont(weight, width, slant, &font), + "Could not match font in family."); + + SkTScopedComPtr fontFace; + HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); + + return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fFontFamily.get()); + } + +private: + SkAutoTUnref fFontMgr; + SkTScopedComPtr fFontFamily; +}; + +class SkFontMgr_DirectWrite : public SkFontMgr { +public: + /** localeNameLength must include the null terminator. */ + SkFontMgr_DirectWrite(IDWriteFontCollection* fontCollection, + WCHAR* localeName, int localeNameLength) + : fFontCollection(SkRefComPtr(fontCollection)) + , fLocaleName(localeNameLength) + { + memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR)); + } + +private: + friend class SkFontStyleSet_DirectWrite; + SkTScopedComPtr fFontCollection; + SkSMallocWCHAR fLocaleName; + +protected: + virtual int onCountFamilies() SK_OVERRIDE { + return fFontCollection->GetFontFamilyCount(); + } + virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE { + SkTScopedComPtr fontFamily; + HRVM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); + + SkTScopedComPtr familyNames; + HRVM(fontFamily->GetFamilyNames(&familyNames), "Could not get family names."); + + get_locale_string(familyNames.get(), fLocaleName.get(), familyName); + } + virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE { + SkTScopedComPtr fontFamily; + HRNM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); + + return SkNEW_ARGS(SkFontStyleSet_DirectWrite, (this, fontFamily.get())); + } + virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE { + SkSMallocWCHAR dwFamilyName; + HRN(cstring_to_wchar(familyName, &dwFamilyName)); + + UINT32 index; + BOOL exists; + HRNM(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists), + "Failed while finding family by name."); + if (!exists) { + return NULL; + } + + return this->onCreateStyleSet(index); + } + + virtual SkTypeface* onMatchFamilyStyle(const char familyName[], + const SkFontStyle& fontstyle) SK_OVERRIDE { + SkAutoTUnref sset(this->matchFamily(familyName)); + return sset->matchStyle(fontstyle); + } + virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember, + const SkFontStyle& fontstyle) SK_OVERRIDE { + SkString familyName; + SkFontStyleSet_DirectWrite sset( + this, ((DWriteFontTypeface*)familyMember)->fDWriteFontFamily.get() + ); + return sset.matchStyle(fontstyle); + } + virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) SK_OVERRIDE { + return create_from_stream(stream); + } + virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) SK_OVERRIDE { + SkAutoTUnref stream(SkNEW_ARGS(SkMemoryStream, (data))); + return this->createFromStream(stream, ttcIndex); + } + virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) SK_OVERRIDE { + SkAutoTUnref stream(SkStream::NewFromFile(path)); + return this->createFromStream(stream, ttcIndex); + } +}; + +void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString* styleName) { + SkTScopedComPtr font; + HRVM(fFontFamily->GetFont(index, &font), "Could not get font."); + + 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(); + + *fs = SkFontStyle(weight, width, slant); + + SkTScopedComPtr faceNames; + if (SUCCEEDED(font->GetFaceNames(&faceNames))) { + get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName); + } +} + +SkFontMgr* SkFontMgr::Factory() { + IDWriteFactory* factory; + HRNM(get_dwrite_factory(&factory), "Could not get factory."); + + SkTScopedComPtr sysFontCollection; + HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE), + "Could not get system font collection."); + + WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH]; + WCHAR* localeName = NULL; + int localeNameLen = GetUserDefaultLocaleName(localeNameStorage, LOCALE_NAME_MAX_LENGTH); + if (localeNameLen) { + localeName = localeNameStorage; + }; + + return SkNEW_ARGS(SkFontMgr_DirectWrite, (sysFontCollection.get(), localeName, localeNameLen)); } diff --git a/tests/FontMgrTest.cpp b/tests/FontMgrTest.cpp index 46aa334a2..6b6ee9a73 100644 --- a/tests/FontMgrTest.cpp +++ b/tests/FontMgrTest.cpp @@ -20,7 +20,9 @@ static void test_fontiter(skiatest::Reporter* reporter, bool verbose) { fm->getFamilyName(i, &fname); REPORTER_ASSERT(reporter, fname.size() > 0); + SkAutoTUnref fnset(fm->matchFamily(fname.c_str())); SkAutoTUnref set(fm->createStyleSet(i)); + REPORTER_ASSERT(reporter, fnset->count() == set->count()); if (verbose) { SkDebugf("[%2d] %s\n", i, fname.c_str());