aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/fontmgr.cpp6
-rw-r--r--include/core/SkTemplates.h16
-rw-r--r--include/utils/win/SkTScopedComPtr.h5
-rw-r--r--src/gpu/gl/GrGLCaps.cpp14
-rw-r--r--src/ports/SkFontHost_win_dw.cpp307
-rw-r--r--tests/FontMgrTest.cpp2
6 files changed, 287 insertions, 63 deletions
diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp
index 4ff55a7a6c..70b91150d0 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 24705dbed0..566834cab3 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -295,11 +295,17 @@ private:
template <size_t N, typename T> 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 85c314a97a..fa64ac4803 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<typename T> T* SkRefComPtr(T* ptr) {
+ ptr->AddRef();
+ return ptr;
+}
+
template<typename T>
class SkTScopedComPtr : SkNoncopyable {
private:
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index b868d6a51f..716989e0c8 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 489bad727a..33fc322836 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<wchar_t> dwFaceFontFamilyNameChar(new wchar_t[dwFaceFontFamilyNameLength+1], dwFaceFontFamilyNameLength+1);
- SkTDArray<wchar_t> 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<wchar_t> dwFontFamilyNameChar(new wchar_t[dwFontFamilyNameLength+1], dwFontFamilyNameLength+1);
- SkTDArray<wchar_t> 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<wchar_t> 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<char> 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<IDWriteFontCollection> 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<wchar_t> 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<wchar_t> 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<char> 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"
+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<IDWriteFont> font;
+ HRNM(fFontFamily->GetFont(index, &font), "Could not get font.");
+
+ SkTScopedComPtr<IDWriteFontFace> 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<IDWriteFont> font;
+ HRNM(fFontFamily->GetFirstMatchingFont(weight, width, slant, &font),
+ "Could not match font in family.");
+
+ SkTScopedComPtr<IDWriteFontFace> fontFace;
+ HRNM(font->CreateFontFace(&fontFace), "Could not create font face.");
+
+ return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fFontFamily.get());
+ }
+
+private:
+ SkAutoTUnref<const SkFontMgr_DirectWrite> fFontMgr;
+ SkTScopedComPtr<IDWriteFontFamily> 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<IDWriteFontCollection> fFontCollection;
+ SkSMallocWCHAR fLocaleName;
+
+protected:
+ virtual int onCountFamilies() SK_OVERRIDE {
+ return fFontCollection->GetFontFamilyCount();
+ }
+ virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
+ SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+ HRVM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family.");
+
+ SkTScopedComPtr<IDWriteLocalizedStrings> 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<IDWriteFontFamily> 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<SkFontStyleSet> 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<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data)));
+ return this->createFromStream(stream, ttcIndex);
+ }
+ virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) SK_OVERRIDE {
+ SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
+ return this->createFromStream(stream, ttcIndex);
+ }
+};
+
+void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString* styleName) {
+ SkTScopedComPtr<IDWriteFont> 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<IDWriteLocalizedStrings> faceNames;
+ if (SUCCEEDED(font->GetFaceNames(&faceNames))) {
+ get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName);
+ }
+}
+
SkFontMgr* SkFontMgr::Factory() {
- // todo
- return NULL;
+ IDWriteFactory* factory;
+ HRNM(get_dwrite_factory(&factory), "Could not get factory.");
+
+ 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 = 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 46aa334a20..6b6ee9a73f 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<SkFontStyleSet> fnset(fm->matchFamily(fname.c_str()));
SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
+ REPORTER_ASSERT(reporter, fnset->count() == set->count());
if (verbose) {
SkDebugf("[%2d] %s\n", i, fname.c_str());