Implement charToGlyph on remaining ports.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 24 Oct 2013 21:39:35 +0000 (21:39 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 24 Oct 2013 21:39:35 +0000 (21:39 +0000)
R=reed@google.com

Review URL: https://codereview.chromium.org/22859070

git-svn-id: http://skia.googlecode.com/svn/trunk@11955 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkTypeface.h
src/core/SkTypeface.cpp
src/fonts/SkGScalerContext.cpp
src/fonts/SkGScalerContext.h
src/ports/SkFontHost_win.cpp
src/ports/SkFontHost_win_dw.cpp
tests/FontHostTest.cpp
tests/ImageDecodingTest.cpp

index ca88a1b..eebb127 100644 (file)
@@ -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;
index 25453f7..4da2738 100644 (file)
@@ -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;
-}
index 2c4ebe5..053efb3 100644 (file)
@@ -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();
 }
index 5e73850..2b51bbd 100644 (file)
@@ -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;
 
index 17aa0e6..d0af011 100755 (executable)
@@ -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(&currentUtf8);
+        }
+        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(&currentUtf8);
+                }
+            }
+            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(&currentUtf8);
+                }
+            }
+        }
+        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);
index 974b90f..aa57b57 100644 (file)
@@ -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();
 }
index 7358b10..826055a 100644 (file)
@@ -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);
         }
     }
 }
index 5146b08..56193f4 100644 (file)
@@ -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());