bonefish + stippi:

Reworked the glyph caching:
* It is now glyph index based (a font file has an internal glyph code to
  index mapping, so that for example the same fallback glyph is used for
  all unsupported codes).
* We do not truncate to uint16 anymore at various places, but support
  the full 32 bit char codes.
* This made it necessary to switch to an OpenHashTable based cache lookup.
* In CharacterMap, you can now see that the second Unicode plane does not
  wrap around anymore.
TODO: Remove old entries from the hash table after a while to free up memory.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31780 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2009-07-26 18:04:01 +00:00
parent f75b8de3fc
commit fb2c7f9394
6 changed files with 179 additions and 124 deletions
+104 -68
View File
@@ -27,6 +27,9 @@
#include "FontCacheEntry.h"
#include <string.h>
#include <new>
#include <agg_array.h>
#include <Autolock.h>
@@ -40,88 +43,114 @@ BLocker
FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
class FontCacheEntry::GlyphCachePool {
public:
enum block_size_e { block_size = 16384-16 };
public:
GlyphCachePool()
: fAllocator(block_size)
{
memset(fGlyphs, 0, sizeof(fGlyphs));
}
const GlyphCache* FindGlyph(uint16 glyphCode) const
~GlyphCachePool()
{
unsigned msb = (glyphCode >> 8) & 0xFF;
if (fGlyphs[msb])
return fGlyphs[msb][glyphCode & 0xFF];
return 0;
GlyphCache* glyph = fGlyphTable.Clear(true);
while (glyph != NULL) {
GlyphCache* next = glyph->fNext;
delete glyph;
glyph = next;
}
}
GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex,
unsigned dataSize, glyph_data_type dataType, const agg::rect_i& bounds,
status_t Init()
{
return fGlyphTable.Init();
}
const GlyphCache* FindGlyph(uint32 glyphIndex) const
{
return fGlyphTable.Lookup(glyphIndex);
}
GlyphCache* CacheGlyph(uint32 glyphIndex,
uint32 dataSize, glyph_data_type dataType, const agg::rect_i& bounds,
float advanceX, float advanceY, float insetLeft, float insetRight)
{
unsigned msb = (glyphCode >> 8) & 0xFF;
if (fGlyphs[msb] == 0) {
fGlyphs[msb]
= (GlyphCache**)fAllocator.allocate(sizeof(GlyphCache*) * 256,
sizeof(GlyphCache*));
memset(fGlyphs[msb], 0, sizeof(GlyphCache*) * 256);
}
unsigned lsb = glyphCode & 0xFF;
if (fGlyphs[msb][lsb])
return NULL; // already exists, do not overwrite
GlyphCache* glyph
= (GlyphCache*)fAllocator.allocate(sizeof(GlyphCache),
sizeof(double));
if (glyph == NULL)
GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex);
if (glyph != NULL)
return NULL;
glyph->glyph_index = glyphIndex;
glyph->data = fAllocator.allocate(dataSize);
glyph->data_size = dataSize;
glyph->data_type = dataType;
glyph->bounds = bounds;
glyph->advance_x = advanceX;
glyph->advance_y = advanceY;
glyph->inset_left = insetLeft;
glyph->inset_right = insetRight;
glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType,
bounds, advanceX, advanceY, insetLeft, insetRight);
if (glyph == NULL || glyph->data == NULL) {
delete glyph;
return NULL;
}
return fGlyphs[msb][lsb] = glyph;
// TODO: The HashTable grows without bounds. We should cleanup
// older entries from time to time.
fGlyphTable.Insert(glyph);
return glyph;
}
private:
agg::block_allocator fAllocator;
GlyphCache** fGlyphs[256];
private:
struct GlyphHashTableDefinition {
typedef uint32 KeyType;
typedef GlyphCache ValueType;
size_t HashKey(uint32 key) const
{
return key;
}
size_t Hash(GlyphCache* value) const
{
return value->glyph_index;
}
bool Compare(uint32 key, GlyphCache* value) const
{
return value->glyph_index == key;
}
HashTableLink<GlyphCache>* GetLink(GlyphCache* value) const
{
return value;
}
};
typedef OpenHashTable<GlyphHashTableDefinition> GlyphTable;
GlyphTable fGlyphTable;
};
// #pragma mark -
// constructor
FontCacheEntry::FontCacheEntry()
: MultiLocker("FontCacheEntry lock")
, Referenceable()
, fGlyphCache(new GlyphCachePool())
, fEngine()
, fLastUsedTime(LONGLONG_MIN)
, fUseCounter(0)
:
MultiLocker("FontCacheEntry lock"),
Referenceable(),
fGlyphCache(new(std::nothrow) GlyphCachePool()),
fEngine(),
fLastUsedTime(LONGLONG_MIN),
fUseCounter(0)
{
}
// destructor
FontCacheEntry::~FontCacheEntry()
{
//printf("~FontCacheEntry()\n");
delete fGlyphCache;
}
// Init
bool
FontCacheEntry::Init(const ServerFont& font)
{
if (fGlyphCache == NULL)
return false;
glyph_rendering renderingType = _RenderTypeFor(font);
// TODO: encoding from font
@@ -134,17 +163,24 @@ FontCacheEntry::Init(const ServerFont& font)
"file %s\n", font.Path());
return false;
}
if (fGlyphCache->Init() != B_OK) {
fprintf(stderr, "FontCacheEntry::Init() - failed to allocate "
"GlyphCache table for font file %s\n", font.Path());
return false;
}
return true;
}
// HasGlyphs
bool
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{
uint32 charCode;
const char* start = utf8String;
while ((charCode = UTF8ToCharCode(&utf8String))) {
if (!fGlyphCache->FindGlyph(charCode))
uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(charCode);
if (!fGlyphCache->FindGlyph(glyphIndex))
return false;
if (utf8String - start + 1 > length)
break;
@@ -152,30 +188,31 @@ FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
return true;
}
// Glyph
const GlyphCache*
FontCacheEntry::Glyph(uint16 glyphCode)
FontCacheEntry::Glyph(uint32 glyphCode)
{
const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode);
uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(glyphCode);
const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphIndex);
if (glyph) {
return glyph;
} else {
if (fEngine.PrepareGlyph(glyphCode)) {
glyph = fGlyphCache->CacheGlyph(glyphCode,
fEngine.GlyphIndex(), fEngine.DataSize(),
fEngine.DataType(), fEngine.Bounds(),
if (fEngine.PrepareGlyph(glyphIndex)) {
glyph = fGlyphCache->CacheGlyph(glyphIndex,
fEngine.DataSize(), fEngine.DataType(), fEngine.Bounds(),
fEngine.AdvanceX(), fEngine.AdvanceY(),
fEngine.InsetLeft(), fEngine.InsetRight());
fEngine.WriteGlyphTo(glyph->data);
if (glyph != NULL)
fEngine.WriteGlyphTo(glyph->data);
return glyph;
}
}
return 0;
return NULL;
}
// InitAdaptors
void
FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
double x, double y, GlyphMonoAdapter& monoAdapter,
@@ -207,15 +244,15 @@ FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
}
}
// GetKerning
bool
FontCacheEntry::GetKerning(uint16 glyphCode1, uint16 glyphCode2,
FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2,
double* x, double* y)
{
return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
}
// GenerateSignature
/*static*/ void
FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
const ServerFont& font)
@@ -232,7 +269,7 @@ FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
font.Face(), int(renderingType), font.Size(), hinting, averageWeight);
}
// UpdateUsage
void
FontCacheEntry::UpdateUsage()
{
@@ -248,7 +285,6 @@ FontCacheEntry::UpdateUsage()
}
// _RenderTypeFor
/*static*/ glyph_rendering
FontCacheEntry::_RenderTypeFor(const ServerFont& font)
{
+29 -6
View File
@@ -33,6 +33,8 @@
#include <agg_conv_contour.h>
#include <agg_conv_transform.h>
#include <util/OpenHashTable.h>
#include "ServerFont.h"
#include "FontEngine.h"
#include "MultiLocker.h"
@@ -40,10 +42,31 @@
#include "Transformable.h"
struct GlyphCache {
unsigned glyph_index;
struct GlyphCache : public HashTableLink<GlyphCache> {
GlyphCache(uint32 glyphIndex, uint32 dataSize, glyph_data_type dataType,
const agg::rect_i& bounds, float advanceX, float advanceY,
float insetLeft, float insetRight)
:
glyph_index(glyphIndex),
data((uint8*)malloc(dataSize)),
data_size(dataSize),
data_type(dataType),
bounds(bounds),
advance_x(advanceX),
advance_y(advanceY),
inset_left(insetLeft),
inset_right(insetRight)
{
}
~GlyphCache()
{
free(data);
}
uint32 glyph_index;
uint8* data;
unsigned data_size;
uint32 data_size;
glyph_data_type data_type;
agg::rect_i bounds;
float advance_x;
@@ -80,7 +103,7 @@ class FontCacheEntry : public MultiLocker, public Referenceable {
bool HasGlyphs(const char* utf8String,
ssize_t glyphCount) const;
const GlyphCache* Glyph(uint16 glyphCode);
const GlyphCache* Glyph(uint32 glyphCode);
void InitAdaptors(const GlyphCache* glyph,
double x, double y,
@@ -89,8 +112,8 @@ class FontCacheEntry : public MultiLocker, public Referenceable {
GlyphPathAdapter& pathAdapter,
double scale = 1.0);
bool GetKerning(uint16 glyphCode1,
uint16 glyphCode2, double* x, double* y);
bool GetKerning(uint32 glyphCode1,
uint32 glyphCode2, double* x, double* y);
static void GenerateSignature(char* signature,
size_t signatureSize,
+37 -39
View File
@@ -42,14 +42,13 @@
static const bool kFlipY = true;
// int26p6_to_dbl
static inline double
int26p6_to_dbl(int p)
{
return double(p) / 64.0;
}
// dbl_to_int26p6
static inline int
dbl_to_int26p6(double p)
{
@@ -57,7 +56,6 @@ dbl_to_int26p6(double p)
}
// decompose_ft_outline
template<class PathStorage>
bool
decompose_ft_outline(const FT_Outline& outline, bool flip_y, PathStorage& path)
@@ -289,7 +287,6 @@ decompose_ft_outline(const FT_Outline& outline, bool flip_y, PathStorage& path)
}
// decompose_ft_bitmap_mono
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y,
@@ -321,7 +318,6 @@ decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y,
}
// decompose_ft_bitmap_gray8
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
@@ -365,7 +361,6 @@ decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
}
// decompose_ft_bitmap_subpix
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y,
@@ -557,36 +552,36 @@ decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y,
#endif
}
// #pragma mark -
// constructor
FontEngine::FontEngine()
: fLastError(0)
, fLibraryInitialized(false)
, fLibrary(0)
, fFace(NULL)
:
fLastError(0),
fLibraryInitialized(false),
fLibrary(0),
fFace(NULL),
, fGlyphRendering(glyph_ren_native_gray8)
, fHinting(true)
fGlyphRendering(glyph_ren_native_gray8),
fHinting(true),
, fGlyphIndex(0)
, fDataSize(0)
, fDataType(glyph_data_invalid)
, fBounds(1, 1, 0, 0)
, fAdvanceX(0.0)
, fAdvanceY(0.0)
, fInsetLeft(0.0)
, fInsetRight(0.0)
fDataSize(0),
fDataType(glyph_data_invalid),
fBounds(1, 1, 0, 0),
fAdvanceX(0.0),
fAdvanceY(0.0),
fInsetLeft(0.0),
fInsetRight(0.0),
, fPath()
, fCurves(fPath)
, fScanlineAA()
, fScanlineBin()
, fScanlineSubpix()
, fScanlineStorageAA()
, fScanlineStorageBin()
, fScanlineStorageSubpix()
fPath(),
fCurves(fPath),
fScanlineAA(),
fScanlineBin(),
fScanlineSubpix(),
fScanlineStorageAA(),
fScanlineStorageBin(),
fScanlineStorageSubpix()
{
fCurves.approximation_scale(4.0);
@@ -596,7 +591,6 @@ FontEngine::FontEngine()
}
// destructor
FontEngine::~FontEngine()
{
FT_Done_Face(fFace);
@@ -606,7 +600,6 @@ FontEngine::~FontEngine()
}
// CountFaces
unsigned
FontEngine::CountFaces() const
{
@@ -617,13 +610,19 @@ FontEngine::CountFaces() const
}
// PrepareGlyph
bool
FontEngine::PrepareGlyph(unsigned glyphCode)
uint32
FontEngine::GlyphIndexForGlyphCode(uint32 glyphCode) const
{
fGlyphIndex = FT_Get_Char_Index(fFace, glyphCode);
fLastError = FT_Load_Glyph(fFace, fGlyphIndex,
(fHinting ? (FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD) : FT_LOAD_NO_HINTING));
FT_Get_Char_Index(fFace, glyphCode);
}
bool
FontEngine::PrepareGlyph(uint32 glyphIndex)
{
fLastError = FT_Load_Glyph(fFace, glyphIndex,
(fHinting ? (FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD)
: FT_LOAD_NO_HINTING));
if (fLastError != 0)
return false;
@@ -740,8 +739,7 @@ FontEngine::WriteGlyphTo(uint8* data) const
// GetKerning
bool
FontEngine::GetKerning(unsigned first, unsigned second,
double* x, double* y)
FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y)
{
if (fFace && first && second && FT_HAS_KERNING(fFace)) {
FT_Vector delta;
+5 -7
View File
@@ -92,11 +92,10 @@ class FontEngine {
{ return fHinting; }
bool PrepareGlyph(unsigned glyphCode);
uint32 GlyphIndexForGlyphCode(uint32 glyphCode) const;
bool PrepareGlyph(uint32 glyphIndex);
unsigned GlyphIndex() const
{ return fGlyphIndex; }
unsigned DataSize() const
uint32 DataSize() const
{ return fDataSize; }
glyph_data_type DataType() const
{ return fDataType; }
@@ -114,7 +113,7 @@ class FontEngine {
void WriteGlyphTo(uint8* data) const;
bool GetKerning(unsigned first, unsigned second,
bool GetKerning(uint32 first, uint32 second,
double* x, double* y);
private:
@@ -132,8 +131,7 @@ class FontEngine {
// members needed to generate individual glyphs according
// to glyph rendering type
unsigned fGlyphIndex;
unsigned fDataSize;
uint32 fDataSize;
glyph_data_type fDataType;
agg::rect_i fBounds;
double fAdvanceX;
+1 -1
View File
@@ -1,7 +1,7 @@
SubDir HAIKU_TOP src servers app drawing ;
UseLibraryHeaders agg ;
UsePrivateHeaders app graphics interface shared ;
UsePrivateHeaders app graphics interface kernel shared ;
UsePrivateHeaders [ FDirName graphics common ] ;
UsePrivateSystemHeaders ;
+1 -1
View File
@@ -4,7 +4,7 @@ SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ;
UseLibraryHeaders agg ;
UsePrivateHeaders app graphics interface shared ;
UsePrivateHeaders app graphics interface kernel shared ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app ] ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter drawing_modes ] ;