diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-02 13:56:39 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-02 13:56:39 +0000 |
commit | bcb42aecf1bdb9ae80d766d203b4f636b954cf03 (patch) | |
tree | dfd351e5a83a420732c97c46150e20bdf0723cd1 | |
parent | 63e9627fca441ae7117eb3a06b978470cebed84c (diff) |
add charsToGlyphs to SkTypeface
Will disable new unittest until all backends are implemented.
On Mac, new API is 4x faster than old paint one, so next CL I will reimplement the paint calls in terms of the new typeface call.
R=eae@chromium.org
Review URL: https://codereview.chromium.org/18083023
git-svn-id: http://skia.googlecode.com/svn/trunk@9860 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | bench/CmapBench.cpp | 93 | ||||
-rw-r--r-- | gyp/bench.gypi | 1 | ||||
-rw-r--r-- | include/core/SkTypeface.h | 34 | ||||
-rw-r--r-- | src/core/SkTypeface.cpp | 32 | ||||
-rwxr-xr-x | src/ports/SkFontHost_mac.cpp | 59 | ||||
-rw-r--r-- | tests/PaintTest.cpp | 104 |
6 files changed, 322 insertions, 1 deletions
diff --git a/bench/CmapBench.cpp b/bench/CmapBench.cpp new file mode 100644 index 0000000000..9697477850 --- /dev/null +++ b/bench/CmapBench.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBenchmark.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkTypeface.h" + +enum { + LOOP = SkBENCHLOOP(1000), + NGLYPHS = 100 +}; + +static SkTypeface::Encoding paint2Encoding(const SkPaint& paint) { + SkPaint::TextEncoding enc = paint.getTextEncoding(); + SkASSERT(SkPaint::kGlyphID_TextEncoding != enc); + return (SkTypeface::Encoding)enc; +} + +typedef void (*TypefaceProc)(const SkPaint&, const void* text, size_t len, + int glyphCount); + +static void containsText_proc(const SkPaint& paint, const void* text, size_t len, + int glyphCount) { + for (int i = 0; i < LOOP; ++i) { + paint.containsText(text, len); + } +} + +static void textToGlyphs_proc(const SkPaint& paint, const void* text, size_t len, + int glyphCount) { + uint16_t glyphs[NGLYPHS]; + SkASSERT(glyphCount <= NGLYPHS); + + for (int i = 0; i < LOOP; ++i) { + paint.textToGlyphs(text, len, glyphs); + } +} + +static void charsToGlyphs_proc(const SkPaint& paint, const void* text, + size_t len, int glyphCount) { + SkTypeface::Encoding encoding = paint2Encoding(paint); + uint16_t glyphs[NGLYPHS]; + SkASSERT(glyphCount <= NGLYPHS); + + SkTypeface* face = paint.getTypeface(); + for (int i = 0; i < LOOP; ++i) { + face->charsToGlyphs(text, encoding, glyphs, glyphCount); + } +} + +class CMAPBench : public SkBenchmark { + TypefaceProc fProc; + SkString fName; + char fText[NGLYPHS]; + SkPaint fPaint; + +public: + CMAPBench(void* param, TypefaceProc proc, const char name[]) : SkBenchmark(param) { + fProc = proc; + fName.printf("cmap_%s", name); + + for (int i = 0; i < NGLYPHS; ++i) { + // we're just jamming values into utf8, so we must keep it legal + fText[i] = i; + } + fPaint.setTypeface(SkTypeface::RefDefault())->unref(); + } + +protected: + virtual const char* onGetName() SK_OVERRIDE { + return fName.c_str(); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + fProc(fPaint, fText, sizeof(fText), NGLYPHS); + } + +private: + + typedef SkBenchmark INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_BENCH( return new CMAPBench(p, containsText_proc, "paint_containsText"); ) +DEF_BENCH( return new CMAPBench(p, textToGlyphs_proc, "paint_textToGlyphs"); ) +DEF_BENCH( return new CMAPBench(p, charsToGlyphs_proc, "face_charsToGlyphs"); ) + diff --git a/gyp/bench.gypi b/gyp/bench.gypi index 7c5e1a3480..37e65526a7 100644 --- a/gyp/bench.gypi +++ b/gyp/bench.gypi @@ -17,6 +17,7 @@ '../bench/ChecksumBench.cpp', '../bench/ChartBench.cpp', '../bench/ChromeBench.cpp', + '../bench/CmapBench.cpp', '../bench/ColorFilterBench.cpp', '../bench/DashBench.cpp', '../bench/DecodeBench.cpp', diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index d34854d329..9c23fb200b 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -148,6 +148,36 @@ public: const uint32_t* glyphIDs = NULL, uint32_t glyphIDsCount = 0) const; + enum Encoding { + kUTF8_Encoding, + kUTF16_Encoding, + kUTF32_Encoding + }; + + /** + * Given an array of character codes, of the specified encoding, + * 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 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. + * @param glyphCount number of code points in 'chars' to process. If glyphs + * is not NULL, then it must point sufficient memory to write + * glyphCount values into it. + * @return the number of number of continuous non-zero glyph IDs computed + * from the beginning of chars. This value is valid, even if the + * glyphs parameter is NULL. + */ + int charsToGlyphs(const void* chars, Encoding encoding, uint16_t glyphs[], + int glyphCount) const; + + /** + * Return the number of glyphs in the typeface. + */ + int countGlyphs() const; + // Table getters -- may fail if the underlying font format is not organized // as 4-byte tables. @@ -233,6 +263,10 @@ protected: virtual SkStream* onOpenStream(int* ttcIndex) const = 0; virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0; + virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[], + int glyphCount) const; + virtual int onCountGlyphs() const; + virtual int onGetUPEM() const; virtual int onGetTableTags(SkFontTableTag tags[]) const; diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index ba58c4c424..dcdd5e29f3 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -172,6 +172,24 @@ SkStream* SkTypeface::openStream(int* ttcIndex) const { return this->onOpenStream(ttcIndex); } +int SkTypeface::charsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + if (glyphCount <= 0) { + return 0; + } + if (NULL == chars || (unsigned)encoding > kUTF32_Encoding) { + if (glyphs) { + sk_bzero(glyphs, glyphCount * sizeof(glyphs[0])); + } + return 0; + } + return this->onCharsToGlyphs(chars, encoding, glyphs, glyphCount); +} + +int SkTypeface::countGlyphs() const { + return this->onCountGlyphs(); +} + int SkTypeface::getUnitsPerEm() const { // should we try to cache this in the base-class? return this->onGetUPEM(); @@ -187,6 +205,20 @@ SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics( /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +int SkTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + SkDebugf("onCharsToGlyphs unimplemented\n"); + if (glyphs && glyphCount > 0) { + sk_bzero(glyphs, glyphCount * sizeof(glyphs[0])); + } + return 0; +} + +int SkTypeface::onCountGlyphs() const { + SkDebugf("onCountGlyphs unimplemented\n"); + return 0; +} + int SkTypeface::onGetUPEM() const { int upem = 0; diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp index ac01e5ba84..ef51aef018 100755 --- a/src/ports/SkFontHost_mac.cpp +++ b/src/ports/SkFontHost_mac.cpp @@ -42,6 +42,7 @@ #include "SkUtils.h" #include "SkTypefaceCache.h" #include "SkFontMgr.h" +#include "SkUtils.h" //#define HACK_COLORGLYPHS @@ -461,6 +462,9 @@ protected: virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics( SkAdvancedTypefaceMetrics::PerGlyphInfo, const uint32_t*, uint32_t) const SK_OVERRIDE; + virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[], + int glyphCount) const SK_OVERRIDE; + virtual int onCountGlyphs() const SK_OVERRIDE; private: @@ -1900,6 +1904,61 @@ void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc, *isLocalStream = false; } +int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + // UniChar is utf16 + SkAutoSTMalloc<1024, UniChar> charStorage; + const UniChar* src; + switch (encoding) { + case kUTF8_Encoding: { + const char* u8 = (const char*)chars; + const UniChar* u16 = src = charStorage.reset(2 * glyphCount); + for (int i = 0; i < glyphCount; ++i) { + SkUnichar uni = SkUTF8_NextUnichar(&u8); + int n = SkUTF16_FromUnichar(uni, (uint16_t*)u16); + u16 += n; + } + break; + } + case kUTF16_Encoding: + src = (const UniChar*)chars; + break; + case kUTF32_Encoding: { + const SkUnichar* u32 = (const SkUnichar*)chars; + const UniChar* u16 = src = charStorage.reset(2 * glyphCount); + for (int i = 0; i < glyphCount; ++i) { + int n = SkUTF16_FromUnichar(u32[i], (uint16_t*)u16); + u16 += n; + } + break; + } + } + + // Our caller may not want glyphs for output, but we need to give that + // storage to CT, so we can walk it looking for the first non-zero. + SkAutoSTMalloc<1024, uint16_t> glyphStorage; + uint16_t* macGlyphs = glyphs; + if (NULL == macGlyphs) { + macGlyphs = glyphStorage.reset(glyphCount); + } + + if (CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, glyphCount)) { + return glyphCount; + } + // If we got false, then we need to manually look for first failure + for (int i = 0; i < glyphCount; ++i) { + if (0 == macGlyphs[i]) { + return i; + } + } + // odd to get here, as we expected CT to have returned true up front. + return glyphCount; +} + +int SkTypeface_Mac::onCountGlyphs() const { + return CTFontGetGlyphCount(fFontRef); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #if 1 diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp index 0db34b6f09..6d12ebec28 100644 --- a/tests/PaintTest.cpp +++ b/tests/PaintTest.cpp @@ -1,15 +1,112 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "Test.h" #include "SkPath.h" #include "SkPaint.h" #include "SkLayerDrawLooper.h" #include "SkBlurMaskFilter.h" +#include "SkRandom.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) { + char* u8 = (char*)dst; + for (int i = 0; i < count; ++i) { + int n = SkUTF8_FromUnichar(src[i], u8); + u8 += n; + } + return u8 - (char*)dst; +} + +static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) { + uint16_t* u16 = (uint16_t*)dst; + for (int i = 0; i < count; ++i) { + int n = SkUTF16_FromUnichar(src[i], u16); + u16 += n; + } + return (char*)u16 - (char*)dst; +} + +static size_t uni_to_utf32(const SkUnichar src[], void* dst, int count) { + SkUnichar* u32 = (SkUnichar*)dst; + if (src != u32) { + memcpy(u32, src, count * sizeof(SkUnichar)); + } + return count * sizeof(SkUnichar); +} + +static SkTypeface::Encoding paint2encoding(const SkPaint& paint) { + SkPaint::TextEncoding enc = paint.getTextEncoding(); + SkASSERT(SkPaint::kGlyphID_TextEncoding != enc); + return (SkTypeface::Encoding)enc; +} + +static int find_first_zero(const uint16_t glyphs[], int count) { + for (int i = 0; i < count; ++i) { + if (0 == glyphs[i]) { + return i; + } + } + return count; +} + +static void test_cmap(skiatest::Reporter* reporter) { + static const int NGLYPHS = 64; + + SkUnichar src[NGLYPHS]; + SkUnichar dst[NGLYPHS]; // used for utf8, utf16, utf32 storage + + static const struct { + size_t (*fSeedTextProc)(const SkUnichar[], void* dst, int count); + SkPaint::TextEncoding fEncoding; + } gRec[] = { + { uni_to_utf8, SkPaint::kUTF8_TextEncoding }, + { uni_to_utf16, SkPaint::kUTF16_TextEncoding }, + { uni_to_utf32, SkPaint::kUTF32_TextEncoding }, + }; + + SkRandom rand; + SkPaint paint; + paint.setTypeface(SkTypeface::RefDefault())->unref(); + SkTypeface* face = paint.getTypeface(); + + for (int i = 0; i < 1000; ++i) { + // generate some random text + for (int j = 0; j < NGLYPHS; ++j) { + src[j] = ' ' + j; + } + // inject some random chars, to sometimes abort early + src[rand.nextU() & 63] = rand.nextU() & 0xFFF; + + for (size_t k = 0; k < SK_ARRAY_COUNT(gRec); ++k) { + paint.setTextEncoding(gRec[k].fEncoding); + + size_t len = gRec[k].fSeedTextProc(src, dst, NGLYPHS); + + uint16_t glyphs0[NGLYPHS], glyphs1[NGLYPHS]; + + bool contains = paint.containsText(dst, len); + int nglyphs = paint.textToGlyphs(dst, len, glyphs0); + int first = face->charsToGlyphs(dst, paint2encoding(paint), glyphs1, NGLYPHS); + int index = find_first_zero(glyphs1, NGLYPHS); + + REPORTER_ASSERT(reporter, NGLYPHS == nglyphs); + REPORTER_ASSERT(reporter, index == first); + REPORTER_ASSERT(reporter, + !memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t))); + if (contains) { + REPORTER_ASSERT(reporter, NGLYPHS == first); + } else { + REPORTER_ASSERT(reporter, NGLYPHS > first); + } + } + } +} // temparary api for bicubic, just be sure we can set/clear it static void test_bicubic(skiatest::Reporter* reporter) { @@ -134,6 +231,11 @@ static void TestPaint(skiatest::Reporter* reporter) { regression_measureText(reporter); test_bicubic(reporter); +#ifdef SK_BUILD_FOR_MAC + // need to implement charsToGlyphs on other backends (e.g. linux, win) + // before we can run this tests everywhre + test_cmap(reporter); +#endif } #include "TestClassDef.h" |