diff options
-rw-r--r-- | include/core/SkTypeface.h | 4 | ||||
-rw-r--r-- | src/core/SkTypeface.cpp | 24 | ||||
-rw-r--r-- | src/fonts/SkGScalerContext.cpp | 5 | ||||
-rw-r--r-- | src/fonts/SkGScalerContext.h | 2 | ||||
-rwxr-xr-x | src/ports/SkFontHost_win.cpp | 205 | ||||
-rw-r--r-- | src/ports/SkFontHost_win_dw.cpp | 75 | ||||
-rw-r--r-- | tests/FontHostTest.cpp | 63 | ||||
-rw-r--r-- | tests/ImageDecodingTest.cpp | 1 |
8 files changed, 354 insertions, 25 deletions
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index ca88a1bc76..eebb1276f7 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -160,7 +160,7 @@ public: * optionally return their corresponding glyph IDs (if glyphs is not NULL). * * @param chars pointer to the array of character codes - * @param encoding how the characteds are encoded + * @param encoding how the characters are encoded * @param glyphs (optional) returns the corresponding glyph IDs for each * character code, up to glyphCount values. If a character code is * not found in the typeface, the corresponding glyph ID will be 0. @@ -307,7 +307,7 @@ protected: virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0; virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[], - int glyphCount) const; + int glyphCount) const = 0; virtual int onCountGlyphs() const = 0; virtual int onGetUPEM() const = 0; diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index 25453f748f..4da27388e7 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -49,6 +49,13 @@ protected: SkAdvancedTypefaceMetrics::PerGlyphInfo, const uint32_t*, uint32_t) const SK_OVERRIDE { return NULL; } virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE { } + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const SK_OVERRIDE { + if (glyphs && glyphCount > 0) { + sk_bzero(glyphs, glyphCount * sizeof(glyphs[0])); + } + return 0; + } virtual int onCountGlyphs() const SK_OVERRIDE { return 0; }; virtual int onGetUPEM() const SK_OVERRIDE { return 0; }; class EmptyLocalizedStrings : public SkTypeface::LocalizedStrings { @@ -247,20 +254,3 @@ SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics( SkTypeface* SkTypeface::refMatchingStyle(Style style) const { return this->onRefMatchingStyle(style); } - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -int SkTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, - uint16_t glyphs[], int glyphCount) const { - static bool printed = false; - if (!printed) { - // Only want to see this message once - SkDebugf("\n *** onCharsToGlyphs unimplemented ***\n"); - printed = true; - } - if (glyphs && glyphCount > 0) { - sk_bzero(glyphs, glyphCount * sizeof(glyphs[0])); - } - return 0; -} diff --git a/src/fonts/SkGScalerContext.cpp b/src/fonts/SkGScalerContext.cpp index 2c4ebe508d..053efb3706 100644 --- a/src/fonts/SkGScalerContext.cpp +++ b/src/fonts/SkGScalerContext.cpp @@ -194,6 +194,11 @@ void SkGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, fProxy->getFontDescriptor(desc, isLocal); } +int SkGTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount); +} + int SkGTypeface::onCountGlyphs() const { return fProxy->countGlyphs(); } diff --git a/src/fonts/SkGScalerContext.h b/src/fonts/SkGScalerContext.h index 5e73850af2..2b51bbd201 100644 --- a/src/fonts/SkGScalerContext.h +++ b/src/fonts/SkGScalerContext.h @@ -29,6 +29,8 @@ protected: virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE; virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const SK_OVERRIDE; + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const SK_OVERRIDE; virtual int onCountGlyphs() const SK_OVERRIDE; virtual int onGetUPEM() const SK_OVERRIDE; diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index 17aa0e65b0..d0af01128a 100755 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -267,6 +267,8 @@ protected: SkAdvancedTypefaceMetrics::PerGlyphInfo, const uint32_t*, uint32_t) const SK_OVERRIDE; virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE; + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const SK_OVERRIDE; virtual int onCountGlyphs() const SK_OVERRIDE; virtual int onGetUPEM() const SK_OVERRIDE; virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE; @@ -2079,6 +2081,209 @@ SkStream* LogFontTypeface::onOpenStream(int* ttcIndex) const { return stream; } +static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs, + bool Ox1FHack) +{ + DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS); + if (GDI_ERROR == result) { + for (int i = 0; i < count; ++i) { + glyphs[i] = 0; + } + return; + } + + if (Ox1FHack) { + for (int i = 0; i < count; ++i) { + if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) { + glyphs[i] = 0; + } + } + } else { + for (int i = 0; i < count; ++i) { + if (0xFFFF == glyphs[i]){ + glyphs[i] = 0; + } + } + } +} + +static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) { + uint16_t index = 0; + // Use uniscribe to detemine glyph index for non-BMP characters. + static const int numWCHAR = 2; + static const int maxItems = 2; + // MSDN states that this can be NULL, but some things don't work then. + SCRIPT_CONTROL scriptControl = { 0 }; + // Add extra item to SCRIPT_ITEM to work around a bug (now documented). + // https://bugzilla.mozilla.org/show_bug.cgi?id=366643 + SCRIPT_ITEM si[maxItems + 1]; + int numItems; + HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, NULL, si, &numItems), + "Could not itemize character."); + + // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs. + static const int maxGlyphs = 2; + SCRIPT_VISATTR vsa[maxGlyphs]; + WORD outGlyphs[maxGlyphs]; + WORD logClust[numWCHAR]; + int numGlyphs; + HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a, + outGlyphs, logClust, vsa, &numGlyphs), + "Could not shape character."); + if (1 == numGlyphs) { + index = outGlyphs[0]; + } + return index; +} + +class SkAutoHDC { +public: + SkAutoHDC(const LOGFONT& lf) + : fHdc(::CreateCompatibleDC(NULL)) + , fFont(::CreateFontIndirect(&lf)) + , fSavefont((HFONT)SelectObject(fHdc, fFont)) + { } + ~SkAutoHDC() { + SelectObject(fHdc, fSavefont); + DeleteObject(fFont); + DeleteDC(fHdc); + } + operator HDC() { return fHdc; } +private: + HDC fHdc; + HFONT fFont; + HFONT fSavefont; +}; + +int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t userGlyphs[], int glyphCount) const +{ + SkAutoHDC hdc(fLogFont); + + TEXTMETRIC tm; + if (0 == GetTextMetrics(hdc, &tm)) { + call_ensure_accessible(fLogFont); + if (0 == GetTextMetrics(hdc, &tm)) { + tm.tmPitchAndFamily = TMPF_TRUETYPE; + } + } + bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */; + + SkAutoSTMalloc<256, uint16_t> scratchGlyphs; + uint16_t* glyphs; + if (userGlyphs != NULL) { + glyphs = userGlyphs; + } else { + glyphs = scratchGlyphs.reset(glyphCount); + } + + SCRIPT_CACHE sc = 0; + switch (encoding) { + case SkTypeface::kUTF8_Encoding: { + static const int scratchCount = 256; + WCHAR scratch[scratchCount]; + int glyphIndex = 0; + const char* currentUtf8 = reinterpret_cast<const char*>(chars); + SkUnichar currentChar; + if (glyphCount) { + currentChar = SkUTF8_NextUnichar(¤tUtf8); + } + while (glyphIndex < glyphCount) { + // Try a run of bmp. + int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount); + int runLength = 0; + while (runLength < glyphsLeft && currentChar <= 0xFFFF) { + scratch[runLength] = static_cast<WCHAR>(currentChar); + ++runLength; + if (runLength < glyphsLeft) { + currentChar = SkUTF8_NextUnichar(¤tUtf8); + } + } + if (runLength) { + bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack); + glyphIndex += runLength; + } + + // Try a run of non-bmp. + while (glyphIndex < glyphCount && currentChar > 0xFFFF) { + SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch)); + glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch); + ++glyphIndex; + if (glyphIndex < glyphCount) { + currentChar = SkUTF8_NextUnichar(¤tUtf8); + } + } + } + break; + } + case SkTypeface::kUTF16_Encoding: { + int glyphIndex = 0; + const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars); + while (glyphIndex < glyphCount) { + // Try a run of bmp. + int glyphsLeft = glyphCount - glyphIndex; + int runLength = 0; + while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) { + ++runLength; + } + if (runLength) { + bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack); + glyphIndex += runLength; + currentUtf16 += runLength; + } + + // Try a run of non-bmp. + while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) { + glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16); + ++glyphIndex; + currentUtf16 += 2; + } + } + break; + } + case SkTypeface::kUTF32_Encoding: { + static const int scratchCount = 256; + WCHAR scratch[scratchCount]; + int glyphIndex = 0; + const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars); + while (glyphIndex < glyphCount) { + // Try a run of bmp. + int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount); + int runLength = 0; + while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) { + scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]); + ++runLength; + } + if (runLength) { + bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack); + glyphIndex += runLength; + } + + // Try a run of non-bmp. + while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) { + SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch)); + glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch); + ++glyphIndex; + } + } + break; + } + default: + SK_CRASH(); + } + + if (sc) { + ::ScriptFreeCache(&sc); + } + + for (int i = 0; i < glyphCount; ++i) { + if (0 == glyphs[i]) { + return i; + } + } + return glyphCount; +} + int LogFontTypeface::onCountGlyphs() const { HDC hdc = ::CreateCompatibleDC(NULL); HFONT font = CreateFontIndirect(&fLogFont); diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp index 974b90f903..aa57b57804 100644 --- a/src/ports/SkFontHost_win_dw.cpp +++ b/src/ports/SkFontHost_win_dw.cpp @@ -566,6 +566,8 @@ protected: SkAdvancedTypefaceMetrics::PerGlyphInfo, const uint32_t*, uint32_t) const SK_OVERRIDE; virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE; + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const SK_OVERRIDE; virtual int onCountGlyphs() const SK_OVERRIDE; virtual int onGetUPEM() const SK_OVERRIDE; virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE; @@ -1131,6 +1133,79 @@ void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, *isLocalStream = SkToBool(fDWriteFontFileLoader.get()); } +static SkUnichar next_utf8(const void** chars) { + return SkUTF8_NextUnichar((const char**)chars); +} + +static SkUnichar next_utf16(const void** chars) { + return SkUTF16_NextUnichar((const uint16_t**)chars); +} + +static SkUnichar next_utf32(const void** chars) { + const SkUnichar** uniChars = (const SkUnichar**)chars; + SkUnichar uni = **uniChars; + *uniChars += 1; + return uni; +} + +typedef SkUnichar (*EncodingProc)(const void**); + +static EncodingProc find_encoding_proc(SkTypeface::Encoding enc) { + static const EncodingProc gProcs[] = { + next_utf8, next_utf16, next_utf32 + }; + SkASSERT((size_t)enc < SK_ARRAY_COUNT(gProcs)); + return gProcs[enc]; +} + +int DWriteFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const +{ + if (NULL == glyphs) { + EncodingProc next_ucs4_proc = find_encoding_proc(encoding); + for (int i = 0; i < glyphCount; ++i) { + const SkUnichar c = next_ucs4_proc(&chars); + BOOL exists; + fDWriteFont->HasCharacter(c, &exists); + if (!exists) { + return i; + } + } + return glyphCount; + } + + switch (encoding) { + case SkTypeface::kUTF8_Encoding: + case SkTypeface::kUTF16_Encoding: { + static const int scratchCount = 256; + UINT32 scratch[scratchCount]; + EncodingProc next_ucs4_proc = find_encoding_proc(encoding); + for (int baseGlyph = 0; baseGlyph < glyphCount; baseGlyph += scratchCount) { + int glyphsLeft = glyphCount - baseGlyph; + int limit = SkTMin(glyphsLeft, scratchCount); + for (int i = 0; i < limit; ++i) { + scratch[i] = next_ucs4_proc(&chars); + } + fDWriteFontFace->GetGlyphIndices(scratch, limit, &glyphs[baseGlyph]); + } + break; + } + case SkTypeface::kUTF32_Encoding: + const UINT32* utf32 = reinterpret_cast<const UINT32*>(chars); + fDWriteFontFace->GetGlyphIndices(utf32, glyphCount, glyphs); + break; + default: + SK_CRASH(); + } + + for (int i = 0; i < glyphCount; ++i) { + if (0 == glyphs[i]) { + return i; + } + } + return glyphCount; +} + int DWriteFontTypeface::onCountGlyphs() const { return fDWriteFontFace->GetGlyphCount(); } diff --git a/tests/FontHostTest.cpp b/tests/FontHostTest.cpp index 7358b1089a..826055a783 100644 --- a/tests/FontHostTest.cpp +++ b/tests/FontHostTest.cpp @@ -8,6 +8,7 @@ #include "Test.h" #include "SkPaint.h" #include "SkFontStream.h" +#include "SkOSFile.h" #include "SkStream.h" #include "SkTypeface.h" #include "SkEndian.h" @@ -72,6 +73,49 @@ static void test_countGlyphs(skiatest::Reporter* reporter, SkTypeface* face) { } } +// The following three are all the same code points in various encodings. +static uint8_t utf8Chars[] = { 0x61, 0xE4, 0xB8, 0xAD, 0xD0, 0xAF, 0xD7, 0x99, 0xD7, 0x95, 0xF0, 0x9D, 0x84, 0x9E, 0x61 }; +static uint16_t utf16Chars[] = { 0x0061, 0x4E2D, 0x042F, 0x05D9, 0x05D5, 0xD834, 0xDD1E, 0x0061 }; +static uint32_t utf32Chars[] = { 0x00000061, 0x00004E2D, 0x0000042F, 0x000005D9, 0x000005D5, 0x0001D11E, 0x00000061 }; + +struct CharsToGlyphs_TestData { + const void* chars; + int charCount; + size_t charsByteLength; + SkTypeface::Encoding typefaceEncoding; + const char* name; +} static charsToGlyphs_TestData[] = { + { utf8Chars, 7, sizeof(utf8Chars), SkTypeface::kUTF8_Encoding, "Simple UTF-8" }, + { utf16Chars, 7, sizeof(utf16Chars), SkTypeface::kUTF16_Encoding, "Simple UTF-16" }, + { utf32Chars, 7, sizeof(utf32Chars), SkTypeface::kUTF32_Encoding, "Simple UTF-32" }, +}; + +// Test that SkPaint::textToGlyphs agrees with SkTypeface::charsToGlyphs. +static void test_charsToGlyphs(skiatest::Reporter* reporter, SkTypeface* face) { + uint16_t paintGlyphIds[256]; + uint16_t faceGlyphIds[256]; + + for (size_t testIndex = 0; testIndex < SK_ARRAY_COUNT(charsToGlyphs_TestData); ++testIndex) { + CharsToGlyphs_TestData& test = charsToGlyphs_TestData[testIndex]; + + SkPaint paint; + paint.setTypeface(face); + paint.setTextEncoding((SkPaint::TextEncoding)test.typefaceEncoding); + paint.textToGlyphs(test.chars, test.charsByteLength, paintGlyphIds); + + face->charsToGlyphs(test.chars, test.typefaceEncoding, faceGlyphIds, test.charCount); + + for (int i = 0; i < test.charCount; ++i) { + SkString name; + face->getFamilyName(&name); + SkString a; + a.appendf("%s, paintGlyphIds[%d] = %d, faceGlyphIds[%d] = %d, face = %s", + test.name, i, (int)paintGlyphIds[i], i, (int)faceGlyphIds[i], name.c_str()); + REPORTER_ASSERT_MESSAGE(reporter, paintGlyphIds[i] == faceGlyphIds[i], a.c_str()); + } + } +} + static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) { int n = SkFontStream::GetTableTags(stream, ttcIndex, NULL); @@ -110,11 +154,19 @@ static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream) { } static void test_fontstream(skiatest::Reporter* reporter) { - // TODO: replace when we get a tools/resources/fonts/test.ttc - const char* name = "/AmericanTypewriter.ttc"; - SkFILEStream stream(name); + // This test cannot run if there is no resource path. + SkString resourcePath = skiatest::Test::GetResourcePath(); + if (resourcePath.isEmpty()) { + SkDebugf("Could not run fontstream test because resourcePath not specified."); + return; + } + SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), "test.ttc"); + + SkFILEStream stream(filename.c_str()); if (stream.isValid()) { test_fontstream(reporter, &stream); + } else { + SkDebugf("Could not run fontstream test because test.ttc not found."); } } @@ -169,8 +221,7 @@ static void test_tables(skiatest::Reporter* reporter) { }; for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) { - SkTypeface* face = SkTypeface::CreateFromName(gNames[i], - SkTypeface::kNormal); + SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(gNames[i], SkTypeface::kNormal)); if (face) { #ifdef DUMP_TABLES SkDebugf("%s\n", gNames[i]); @@ -178,7 +229,7 @@ static void test_tables(skiatest::Reporter* reporter) { test_tables(reporter, face); test_unitsPerEm(reporter, face); test_countGlyphs(reporter, face); - face->unref(); + test_charsToGlyphs(reporter, face); } } } diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp index 5146b08ac1..56193f4e47 100644 --- a/tests/ImageDecodingTest.cpp +++ b/tests/ImageDecodingTest.cpp @@ -177,6 +177,7 @@ static void test_unpremul(skiatest::Reporter* reporter) { // This test cannot run if there is no resource path. SkString resourcePath = skiatest::Test::GetResourcePath(); if (resourcePath.isEmpty()) { + SkDebugf("Could not run unpremul test because resourcePath not specified."); return; } SkOSFile::Iter iter(resourcePath.c_str()); |