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
This commit is contained in:
reed@google.com 2013-07-02 13:56:39 +00:00
parent 63e9627fca
commit bcb42aecf1
6 changed files with 322 additions and 1 deletions

93
bench/CmapBench.cpp Normal file
View File

@ -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"); )

View File

@ -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',

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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"