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:
parent
63e9627fca
commit
bcb42aecf1
93
bench/CmapBench.cpp
Normal file
93
bench/CmapBench.cpp
Normal 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"); )
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user