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:
@@ -27,6 +27,9 @@
|
||||
#include "FontCacheEntry.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <agg_array.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
@@ -41,87 +44,113 @@ FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
|
||||
|
||||
class FontCacheEntry::GlyphCachePool {
|
||||
public:
|
||||
enum block_size_e { block_size = 16384-16 };
|
||||
|
||||
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];
|
||||
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());
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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 ;
|
||||
|
||||
|
||||
@@ -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 ] ;
|
||||
|
||||
Reference in New Issue
Block a user