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/ChecksumBench.cpp',
|
||||||
'../bench/ChartBench.cpp',
|
'../bench/ChartBench.cpp',
|
||||||
'../bench/ChromeBench.cpp',
|
'../bench/ChromeBench.cpp',
|
||||||
|
'../bench/CmapBench.cpp',
|
||||||
'../bench/ColorFilterBench.cpp',
|
'../bench/ColorFilterBench.cpp',
|
||||||
'../bench/DashBench.cpp',
|
'../bench/DashBench.cpp',
|
||||||
'../bench/DecodeBench.cpp',
|
'../bench/DecodeBench.cpp',
|
||||||
|
@ -148,6 +148,36 @@ public:
|
|||||||
const uint32_t* glyphIDs = NULL,
|
const uint32_t* glyphIDs = NULL,
|
||||||
uint32_t glyphIDsCount = 0) const;
|
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
|
// Table getters -- may fail if the underlying font format is not organized
|
||||||
// as 4-byte tables.
|
// as 4-byte tables.
|
||||||
|
|
||||||
@ -233,6 +263,10 @@ protected:
|
|||||||
virtual SkStream* onOpenStream(int* ttcIndex) const = 0;
|
virtual SkStream* onOpenStream(int* ttcIndex) const = 0;
|
||||||
virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) 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 onGetUPEM() const;
|
||||||
|
|
||||||
virtual int onGetTableTags(SkFontTableTag tags[]) const;
|
virtual int onGetTableTags(SkFontTableTag tags[]) const;
|
||||||
|
@ -172,6 +172,24 @@ SkStream* SkTypeface::openStream(int* ttcIndex) const {
|
|||||||
return this->onOpenStream(ttcIndex);
|
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 {
|
int SkTypeface::getUnitsPerEm() const {
|
||||||
// should we try to cache this in the base-class?
|
// should we try to cache this in the base-class?
|
||||||
return this->onGetUPEM();
|
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 SkTypeface::onGetUPEM() const {
|
||||||
int upem = 0;
|
int upem = 0;
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "SkUtils.h"
|
#include "SkUtils.h"
|
||||||
#include "SkTypefaceCache.h"
|
#include "SkTypefaceCache.h"
|
||||||
#include "SkFontMgr.h"
|
#include "SkFontMgr.h"
|
||||||
|
#include "SkUtils.h"
|
||||||
|
|
||||||
//#define HACK_COLORGLYPHS
|
//#define HACK_COLORGLYPHS
|
||||||
|
|
||||||
@ -461,6 +462,9 @@ protected:
|
|||||||
virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
|
virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
|
||||||
SkAdvancedTypefaceMetrics::PerGlyphInfo,
|
SkAdvancedTypefaceMetrics::PerGlyphInfo,
|
||||||
const uint32_t*, uint32_t) const SK_OVERRIDE;
|
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:
|
private:
|
||||||
|
|
||||||
@ -1900,6 +1904,61 @@ void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
|
|||||||
*isLocalStream = false;
|
*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
|
#if 1
|
||||||
|
@ -1,15 +1,112 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2011 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Test.h"
|
#include "Test.h"
|
||||||
#include "SkPath.h"
|
#include "SkPath.h"
|
||||||
#include "SkPaint.h"
|
#include "SkPaint.h"
|
||||||
#include "SkLayerDrawLooper.h"
|
#include "SkLayerDrawLooper.h"
|
||||||
#include "SkBlurMaskFilter.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
|
// temparary api for bicubic, just be sure we can set/clear it
|
||||||
static void test_bicubic(skiatest::Reporter* reporter) {
|
static void test_bicubic(skiatest::Reporter* reporter) {
|
||||||
@ -134,6 +231,11 @@ static void TestPaint(skiatest::Reporter* reporter) {
|
|||||||
regression_measureText(reporter);
|
regression_measureText(reporter);
|
||||||
|
|
||||||
test_bicubic(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"
|
#include "TestClassDef.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user