* Copyright 2007-2009, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Maxim Shemanarev <mcseemagg@yahoo.com>
* Stephan Aßmus <superstippi@gmx.de>
* Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
*/
#include "FontCacheEntry.h"
#include <string.h>
#include <new>
#include <Autolock.h>
#include <agg_array.h>
#include <utf8_functions.h>
#include <util/OpenHashTable.h>
#include "GlobalSubpixelSettings.h"
BLocker FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
class FontCacheEntry::GlyphCachePool {
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;
}
GlyphCache*& GetLink(GlyphCache* value) const
{
return value->hash_link;
}
};
public:
GlyphCachePool()
{
}
~GlyphCachePool()
{
GlyphCache* glyph = fGlyphTable.Clear(true);
while (glyph != NULL) {
GlyphCache* next = glyph->hash_link;
delete glyph;
glyph = next;
}
}
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 preciseAdvanceX,
float preciseAdvanceY, float insetLeft, float insetRight)
{
GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex);
if (glyph != NULL)
return NULL;
glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType,
bounds, advanceX, advanceY, preciseAdvanceX, preciseAdvanceY,
insetLeft, insetRight);
if (glyph == NULL || glyph->data == NULL) {
delete glyph;
return NULL;
}
fGlyphTable.Insert(glyph);
return glyph;
}
private:
typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable;
GlyphTable fGlyphTable;
};
FontCacheEntry::FontCacheEntry()
:
MultiLocker("FontCacheEntry lock"),
fGlyphCache(new(std::nothrow) GlyphCachePool()),
fEngine(),
fLastUsedTime(LONGLONG_MIN),
fUseCounter(0)
{
}
FontCacheEntry::~FontCacheEntry()
{
}
bool
FontCacheEntry::Init(const ServerFont& font, bool forceVector)
{
if (!fGlyphCache.IsSet())
return false;
glyph_rendering renderingType = _RenderTypeFor(font, forceVector);
FT_Encoding charMap = FT_ENCODING_NONE;
bool hinting = font.Hinting();
bool success;
if (font.FontData() != NULL)
success = fEngine.Init(NULL, font.FaceIndex(), font.Size(), charMap,
renderingType, hinting, (const void*)font.FontData(), font.FontDataSize());
else
success = fEngine.Init(font.Path(), font.FaceIndex(), font.Size(), charMap,
renderingType, hinting);
if (!success) {
fprintf(stderr, "FontCacheEntry::Init() - some error loading 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;
}
bool
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{
uint32 glyphCode;
const char* start = utf8String;
while ((glyphCode = UTF8ToCharCode(&utf8String))) {
if (fGlyphCache->FindGlyph(glyphCode) == NULL)
return false;
if (utf8String - start + 1 > length)
break;
}
return true;
}
inline bool
render_as_space(uint32 glyphCode)
{
return (glyphCode >= 0x0009 && glyphCode <= 0x000d)
|| (glyphCode == 0x0085)
|| (glyphCode == 0x00a0)
|| (glyphCode == 0x1680)
|| (glyphCode >= 0x2000 && glyphCode <= 0x200a)
|| (glyphCode >= 0x2028 && glyphCode <= 0x2029)
|| (glyphCode == 0x202f)
|| (glyphCode == 0x205f)
|| (glyphCode == 0x3000)
;
}
inline bool
render_as_zero_width(uint32 glyphCode)
{
return (glyphCode == 0x00ad)
|| (glyphCode == 0x034f)
|| (glyphCode == 0x061c)
|| (glyphCode >= 0x115f && glyphCode <= 0x1160)
|| (glyphCode >= 0x17b4 && glyphCode <= 0x17b5)
|| (glyphCode >= 0x180b && glyphCode <= 0x180f)
|| (glyphCode >= 0x200b && glyphCode <= 0x200f)
|| (glyphCode >= 0x202a && glyphCode <= 0x202e)
|| (glyphCode >= 0x2060 && glyphCode <= 0x206f)
|| (glyphCode == 0x3164)
|| (glyphCode >= 0xfe00 && glyphCode <= 0xfe0f)
|| (glyphCode == 0xfeff)
|| (glyphCode == 0xffa0)
|| (glyphCode >= 0xfff0 && glyphCode <= 0xfff8)
|| (glyphCode >= 0x1bca0 && glyphCode <= 0x1bca3)
|| (glyphCode >= 0x1d173 && glyphCode <= 0x1d17a)
|| (glyphCode >= 0xe0000 && glyphCode <= 0xe01ef)
|| (glyphCode >= 0xe01f0 && glyphCode <= 0xe0fff)
|| ((glyphCode & 0xffff) >= 0xfffe)
|| ((glyphCode >= 0xfdd0 && glyphCode <= 0xfdef)
&& glyphCode != 0xfdd1)
;
}
const GlyphCache*
FontCacheEntry::CachedGlyph(uint32 glyphCode)
{
return fGlyphCache->FindGlyph(glyphCode);
}
bool
FontCacheEntry::CanCreateGlyph(uint32 glyphCode)
{
uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(glyphCode);
return glyphIndex != 0;
}
const GlyphCache*
FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry)
{
const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode);
if (glyph != NULL)
return glyph;
FontEngine* engine = &fEngine;
uint32 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
if (glyphIndex == 0 && fallbackEntry != NULL) {
engine = &fallbackEntry->fEngine;
glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
}
if (glyphIndex == 0) {
if (render_as_zero_width(glyphCode)) {
return fGlyphCache->CacheGlyph(glyphCode, 0, glyph_data_invalid,
agg::rect_i(0, 0, -1, -1), 0, 0, 0, 0, 0, 0);
}
engine = &fEngine;
if (render_as_space(glyphCode)) {
glyphIndex = engine->GlyphIndexForGlyphCode(0x20 );
}
}
if (engine->PrepareGlyph(glyphIndex)) {
glyph = fGlyphCache->CacheGlyph(glyphCode,
engine->DataSize(), engine->DataType(), engine->Bounds(),
engine->AdvanceX(), engine->AdvanceY(),
engine->PreciseAdvanceX(), engine->PreciseAdvanceY(),
engine->InsetLeft(), engine->InsetRight());
if (glyph != NULL)
engine->WriteGlyphTo(glyph->data);
}
return glyph;
}
void
FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
double x, double y, GlyphMonoAdapter& monoAdapter,
GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter,
double scale)
{
if (glyph == NULL)
return;
switch(glyph->data_type) {
case glyph_data_mono:
monoAdapter.init(glyph->data, glyph->data_size, x, y);
break;
case glyph_data_gray8:
gray8Adapter.init(glyph->data, glyph->data_size, x, y);
break;
case glyph_data_subpix:
gray8Adapter.init(glyph->data, glyph->data_size, x, y);
break;
case glyph_data_outline:
pathAdapter.init(glyph->data, glyph->data_size, x, y, scale);
break;
default:
break;
}
}
bool
FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2,
double* x, double* y)
{
return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
}
void
FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
const ServerFont& font, bool forceVector)
{
glyph_rendering renderingType = _RenderTypeFor(font, forceVector);
FT_Encoding charMap = FT_ENCODING_NONE;
bool hinting = font.Hinting();
uint8 averageWeight = gSubpixelAverageWeight;
snprintf(signature, signatureSize, "%" B_PRId32 ",%p,%u,%d,%d,%.1f,%d,%d",
font.GetFamilyAndStyle(), font.Manager(), charMap,
font.Face(), int(renderingType), font.Size(), hinting, averageWeight);
}
void
FontCacheEntry::UpdateUsage()
{
BAutolock _(sUsageUpdateLock);
fLastUsedTime = system_time();
fUseCounter++;
}
glyph_rendering
FontCacheEntry::_RenderTypeFor(const ServerFont& font, bool forceVector)
{
glyph_rendering renderingType = gSubpixelAntialiasing ?
glyph_ren_subpix : glyph_ren_native_gray8;
if (forceVector || font.Rotation() != 0.0 || font.Shear() != 90.0
|| font.FalseBoldWidth() != 0.0
|| (font.Flags() & B_DISABLE_ANTIALIASING) != 0
|| font.Size() > 30
|| !font.Hinting()) {
renderingType = glyph_ren_outline;
}
return renderingType;
}