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 "FontCacheEntry.h"
#include <string.h> #include <string.h>
#include <new>
#include <agg_array.h> #include <agg_array.h>
#include <Autolock.h> #include <Autolock.h>
@@ -40,88 +43,114 @@ BLocker
FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock"); FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
class FontCacheEntry::GlyphCachePool { class FontCacheEntry::GlyphCachePool {
public: public:
enum block_size_e { block_size = 16384-16 };
GlyphCachePool() GlyphCachePool()
: fAllocator(block_size)
{ {
memset(fGlyphs, 0, sizeof(fGlyphs));
} }
const GlyphCache* FindGlyph(uint16 glyphCode) const ~GlyphCachePool()
{ {
unsigned msb = (glyphCode >> 8) & 0xFF; GlyphCache* glyph = fGlyphTable.Clear(true);
if (fGlyphs[msb]) while (glyph != NULL) {
return fGlyphs[msb][glyphCode & 0xFF]; GlyphCache* next = glyph->fNext;
return 0; delete glyph;
glyph = next;
}
} }
GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex, status_t Init()
unsigned dataSize, glyph_data_type dataType, const agg::rect_i& bounds, {
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) float advanceX, float advanceY, float insetLeft, float insetRight)
{ {
unsigned msb = (glyphCode >> 8) & 0xFF; GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex);
if (fGlyphs[msb] == 0) { if (glyph != NULL)
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)
return NULL; return NULL;
glyph->glyph_index = glyphIndex; glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType,
glyph->data = fAllocator.allocate(dataSize); bounds, advanceX, advanceY, insetLeft, insetRight);
glyph->data_size = dataSize; if (glyph == NULL || glyph->data == NULL) {
glyph->data_type = dataType; delete glyph;
glyph->bounds = bounds; return NULL;
glyph->advance_x = advanceX; }
glyph->advance_y = advanceY;
glyph->inset_left = insetLeft;
glyph->inset_right = insetRight;
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: private:
agg::block_allocator fAllocator; struct GlyphHashTableDefinition {
GlyphCache** fGlyphs[256]; 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 - // #pragma mark -
// constructor
FontCacheEntry::FontCacheEntry() FontCacheEntry::FontCacheEntry()
: MultiLocker("FontCacheEntry lock") :
, Referenceable() MultiLocker("FontCacheEntry lock"),
, fGlyphCache(new GlyphCachePool()) Referenceable(),
, fEngine() fGlyphCache(new(std::nothrow) GlyphCachePool()),
, fLastUsedTime(LONGLONG_MIN) fEngine(),
, fUseCounter(0) fLastUsedTime(LONGLONG_MIN),
fUseCounter(0)
{ {
} }
// destructor
FontCacheEntry::~FontCacheEntry() FontCacheEntry::~FontCacheEntry()
{ {
//printf("~FontCacheEntry()\n"); //printf("~FontCacheEntry()\n");
delete fGlyphCache; delete fGlyphCache;
} }
// Init
bool bool
FontCacheEntry::Init(const ServerFont& font) FontCacheEntry::Init(const ServerFont& font)
{ {
if (fGlyphCache == NULL)
return false;
glyph_rendering renderingType = _RenderTypeFor(font); glyph_rendering renderingType = _RenderTypeFor(font);
// TODO: encoding from font // TODO: encoding from font
@@ -134,17 +163,24 @@ FontCacheEntry::Init(const ServerFont& font)
"file %s\n", font.Path()); "file %s\n", font.Path());
return false; 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; return true;
} }
// HasGlyphs
bool bool
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{ {
uint32 charCode; uint32 charCode;
const char* start = utf8String; const char* start = utf8String;
while ((charCode = UTF8ToCharCode(&utf8String))) { while ((charCode = UTF8ToCharCode(&utf8String))) {
if (!fGlyphCache->FindGlyph(charCode)) uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(charCode);
if (!fGlyphCache->FindGlyph(glyphIndex))
return false; return false;
if (utf8String - start + 1 > length) if (utf8String - start + 1 > length)
break; break;
@@ -152,30 +188,31 @@ FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
return true; return true;
} }
// Glyph
const GlyphCache* 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) { if (glyph) {
return glyph; return glyph;
} else { } else {
if (fEngine.PrepareGlyph(glyphCode)) { if (fEngine.PrepareGlyph(glyphIndex)) {
glyph = fGlyphCache->CacheGlyph(glyphCode, glyph = fGlyphCache->CacheGlyph(glyphIndex,
fEngine.GlyphIndex(), fEngine.DataSize(), fEngine.DataSize(), fEngine.DataType(), fEngine.Bounds(),
fEngine.DataType(), fEngine.Bounds(),
fEngine.AdvanceX(), fEngine.AdvanceY(), fEngine.AdvanceX(), fEngine.AdvanceY(),
fEngine.InsetLeft(), fEngine.InsetRight()); fEngine.InsetLeft(), fEngine.InsetRight());
fEngine.WriteGlyphTo(glyph->data); if (glyph != NULL)
fEngine.WriteGlyphTo(glyph->data);
return glyph; return glyph;
} }
} }
return 0; return NULL;
} }
// InitAdaptors
void void
FontCacheEntry::InitAdaptors(const GlyphCache* glyph, FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
double x, double y, GlyphMonoAdapter& monoAdapter, double x, double y, GlyphMonoAdapter& monoAdapter,
@@ -207,15 +244,15 @@ FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
} }
} }
// GetKerning
bool bool
FontCacheEntry::GetKerning(uint16 glyphCode1, uint16 glyphCode2, FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2,
double* x, double* y) double* x, double* y)
{ {
return fEngine.GetKerning(glyphCode1, glyphCode2, x, y); return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
} }
// GenerateSignature
/*static*/ void /*static*/ void
FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize, FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
const ServerFont& font) const ServerFont& font)
@@ -232,7 +269,7 @@ FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
font.Face(), int(renderingType), font.Size(), hinting, averageWeight); font.Face(), int(renderingType), font.Size(), hinting, averageWeight);
} }
// UpdateUsage
void void
FontCacheEntry::UpdateUsage() FontCacheEntry::UpdateUsage()
{ {
@@ -248,7 +285,6 @@ FontCacheEntry::UpdateUsage()
} }
// _RenderTypeFor
/*static*/ glyph_rendering /*static*/ glyph_rendering
FontCacheEntry::_RenderTypeFor(const ServerFont& font) FontCacheEntry::_RenderTypeFor(const ServerFont& font)
{ {
+31 -8
View File
@@ -12,8 +12,8 @@
// Anti-Grain Geometry - Version 2.4 // Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
// //
// Permission to copy, use, modify, sell and distribute this software // Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies. // is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied // This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose. // warranty, and with no claim as to its suitability for any purpose.
// //
@@ -33,6 +33,8 @@
#include <agg_conv_contour.h> #include <agg_conv_contour.h>
#include <agg_conv_transform.h> #include <agg_conv_transform.h>
#include <util/OpenHashTable.h>
#include "ServerFont.h" #include "ServerFont.h"
#include "FontEngine.h" #include "FontEngine.h"
#include "MultiLocker.h" #include "MultiLocker.h"
@@ -40,10 +42,31 @@
#include "Transformable.h" #include "Transformable.h"
struct GlyphCache { struct GlyphCache : public HashTableLink<GlyphCache> {
unsigned glyph_index; 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; uint8* data;
unsigned data_size; uint32 data_size;
glyph_data_type data_type; glyph_data_type data_type;
agg::rect_i bounds; agg::rect_i bounds;
float advance_x; float advance_x;
@@ -80,7 +103,7 @@ class FontCacheEntry : public MultiLocker, public Referenceable {
bool HasGlyphs(const char* utf8String, bool HasGlyphs(const char* utf8String,
ssize_t glyphCount) const; ssize_t glyphCount) const;
const GlyphCache* Glyph(uint16 glyphCode); const GlyphCache* Glyph(uint32 glyphCode);
void InitAdaptors(const GlyphCache* glyph, void InitAdaptors(const GlyphCache* glyph,
double x, double y, double x, double y,
@@ -89,8 +112,8 @@ class FontCacheEntry : public MultiLocker, public Referenceable {
GlyphPathAdapter& pathAdapter, GlyphPathAdapter& pathAdapter,
double scale = 1.0); double scale = 1.0);
bool GetKerning(uint16 glyphCode1, bool GetKerning(uint32 glyphCode1,
uint16 glyphCode2, double* x, double* y); uint32 glyphCode2, double* x, double* y);
static void GenerateSignature(char* signature, static void GenerateSignature(char* signature,
size_t signatureSize, size_t signatureSize,
+37 -39
View File
@@ -42,14 +42,13 @@
static const bool kFlipY = true; static const bool kFlipY = true;
// int26p6_to_dbl
static inline double static inline double
int26p6_to_dbl(int p) int26p6_to_dbl(int p)
{ {
return double(p) / 64.0; return double(p) / 64.0;
} }
// dbl_to_int26p6
static inline int static inline int
dbl_to_int26p6(double p) dbl_to_int26p6(double p)
{ {
@@ -57,7 +56,6 @@ dbl_to_int26p6(double p)
} }
// decompose_ft_outline
template<class PathStorage> template<class PathStorage>
bool bool
decompose_ft_outline(const FT_Outline& outline, bool flip_y, PathStorage& path) 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> template<class Scanline, class ScanlineStorage>
void void
decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y, 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> template<class Scanline, class ScanlineStorage>
void void
decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y, 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> template<class Scanline, class ScanlineStorage>
void void
decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y, 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 #endif
} }
// #pragma mark - // #pragma mark -
// constructor
FontEngine::FontEngine() FontEngine::FontEngine()
: fLastError(0) :
, fLibraryInitialized(false) fLastError(0),
, fLibrary(0) fLibraryInitialized(false),
, fFace(NULL) fLibrary(0),
fFace(NULL),
, fGlyphRendering(glyph_ren_native_gray8) fGlyphRendering(glyph_ren_native_gray8),
, fHinting(true) fHinting(true),
, fGlyphIndex(0) fDataSize(0),
, fDataSize(0) fDataType(glyph_data_invalid),
, fDataType(glyph_data_invalid) fBounds(1, 1, 0, 0),
, fBounds(1, 1, 0, 0) fAdvanceX(0.0),
, fAdvanceX(0.0) fAdvanceY(0.0),
, fAdvanceY(0.0) fInsetLeft(0.0),
, fInsetLeft(0.0) fInsetRight(0.0),
, fInsetRight(0.0)
, fPath() fPath(),
, fCurves(fPath) fCurves(fPath),
, fScanlineAA() fScanlineAA(),
, fScanlineBin() fScanlineBin(),
, fScanlineSubpix() fScanlineSubpix(),
, fScanlineStorageAA() fScanlineStorageAA(),
, fScanlineStorageBin() fScanlineStorageBin(),
, fScanlineStorageSubpix() fScanlineStorageSubpix()
{ {
fCurves.approximation_scale(4.0); fCurves.approximation_scale(4.0);
@@ -596,7 +591,6 @@ FontEngine::FontEngine()
} }
// destructor
FontEngine::~FontEngine() FontEngine::~FontEngine()
{ {
FT_Done_Face(fFace); FT_Done_Face(fFace);
@@ -606,7 +600,6 @@ FontEngine::~FontEngine()
} }
// CountFaces
unsigned unsigned
FontEngine::CountFaces() const FontEngine::CountFaces() const
{ {
@@ -617,13 +610,19 @@ FontEngine::CountFaces() const
} }
// PrepareGlyph uint32
bool FontEngine::GlyphIndexForGlyphCode(uint32 glyphCode) const
FontEngine::PrepareGlyph(unsigned glyphCode)
{ {
fGlyphIndex = FT_Get_Char_Index(fFace, glyphCode); FT_Get_Char_Index(fFace, glyphCode);
fLastError = FT_Load_Glyph(fFace, fGlyphIndex, }
(fHinting ? (FT_LOAD_DEFAULT | FT_LOAD_TARGET_LCD) : FT_LOAD_NO_HINTING));
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) if (fLastError != 0)
return false; return false;
@@ -740,8 +739,7 @@ FontEngine::WriteGlyphTo(uint8* data) const
// GetKerning // GetKerning
bool bool
FontEngine::GetKerning(unsigned first, unsigned second, FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y)
double* x, double* y)
{ {
if (fFace && first && second && FT_HAS_KERNING(fFace)) { if (fFace && first && second && FT_HAS_KERNING(fFace)) {
FT_Vector delta; FT_Vector delta;
+5 -7
View File
@@ -92,11 +92,10 @@ class FontEngine {
{ return fHinting; } { return fHinting; }
bool PrepareGlyph(unsigned glyphCode); uint32 GlyphIndexForGlyphCode(uint32 glyphCode) const;
bool PrepareGlyph(uint32 glyphIndex);
unsigned GlyphIndex() const uint32 DataSize() const
{ return fGlyphIndex; }
unsigned DataSize() const
{ return fDataSize; } { return fDataSize; }
glyph_data_type DataType() const glyph_data_type DataType() const
{ return fDataType; } { return fDataType; }
@@ -114,7 +113,7 @@ class FontEngine {
void WriteGlyphTo(uint8* data) const; void WriteGlyphTo(uint8* data) const;
bool GetKerning(unsigned first, unsigned second, bool GetKerning(uint32 first, uint32 second,
double* x, double* y); double* x, double* y);
private: private:
@@ -132,8 +131,7 @@ class FontEngine {
// members needed to generate individual glyphs according // members needed to generate individual glyphs according
// to glyph rendering type // to glyph rendering type
unsigned fGlyphIndex; uint32 fDataSize;
unsigned fDataSize;
glyph_data_type fDataType; glyph_data_type fDataType;
agg::rect_i fBounds; agg::rect_i fBounds;
double fAdvanceX; double fAdvanceX;
+1 -1
View File
@@ -1,7 +1,7 @@
SubDir HAIKU_TOP src servers app drawing ; SubDir HAIKU_TOP src servers app drawing ;
UseLibraryHeaders agg ; UseLibraryHeaders agg ;
UsePrivateHeaders app graphics interface shared ; UsePrivateHeaders app graphics interface kernel shared ;
UsePrivateHeaders [ FDirName graphics common ] ; UsePrivateHeaders [ FDirName graphics common ] ;
UsePrivateSystemHeaders ; UsePrivateSystemHeaders ;
+1 -1
View File
@@ -4,7 +4,7 @@ SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ; AddSubDirSupportedPlatforms libbe_test ;
UseLibraryHeaders agg ; 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 ] ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ; UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter drawing_modes ] ; UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter drawing_modes ] ;