aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-07-02 13:56:39 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-07-02 13:56:39 +0000
commitbcb42aecf1bdb9ae80d766d203b4f636b954cf03 (patch)
treedfd351e5a83a420732c97c46150e20bdf0723cd1
parent63e9627fca441ae7117eb3a06b978470cebed84c (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.cpp93
-rw-r--r--gyp/bench.gypi1
-rw-r--r--include/core/SkTypeface.h34
-rw-r--r--src/core/SkTypeface.cpp32
-rwxr-xr-xsrc/ports/SkFontHost_mac.cpp59
-rw-r--r--tests/PaintTest.cpp104
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"