aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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"