* moved AGGTextRenderer alongside it's pal, Painter, it felt lonely,

removed font_support folder
* ServerApp can use ServerFont::StringWidth() directly again
* more ServerFont functions implemented via GlyphLayoutEngine and
  custom consumer
* extended GlyphCache data structure to hole the left/right insets
  of the glyph shape between its advance width, took it from the earlier
  ServerFont implementation, have not tested if that gives same result
  as R5
* TODO: implement GetGylphShapes via GlyphCache, although it might not
  look as clean as it does now


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21805 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2007-08-03 01:11:27 +00:00
parent 25c969f5a8
commit 25a7061652
10 changed files with 179 additions and 94 deletions

View File

@ -56,7 +56,7 @@ class FontCacheEntry::GlyphCachePool {
GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex,
unsigned dataSize, glyph_data_type dataType, const agg::rect_i& bounds,
double advanceX, double advanceY)
float advanceX, float advanceY, float insetLeft, float insetRight)
{
unsigned msb = (glyphCode >> 8) & 0xFF;
if (fGlyphs[msb] == 0) {
@ -81,6 +81,8 @@ class FontCacheEntry::GlyphCachePool {
glyph->bounds = bounds;
glyph->advance_x = advanceX;
glyph->advance_y = advanceY;
glyph->inset_left = insetLeft;
glyph->inset_right = insetRight;
return fGlyphs[msb][lsb] = glyph;
}
@ -133,7 +135,7 @@ FontCacheEntry::Init(const ServerFont& font)
// HasGlyphs
bool
FontCacheEntry::HasGlyphs(const char* utf8String, size_t length) const
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{
uint32 charCode;
const char* start = utf8String;
@ -158,7 +160,8 @@ FontCacheEntry::Glyph(uint16 glyphCode)
glyph = fGlyphCache->CacheGlyph(glyphCode,
fEngine.GlyphIndex(), fEngine.DataSize(),
fEngine.DataType(), fEngine.Bounds(),
fEngine.AdvanceX(), fEngine.AdvanceY());
fEngine.AdvanceX(), fEngine.AdvanceY(),
fEngine.InsetLeft(), fEngine.InsetRight());
fEngine.WriteGlyphTo(glyph->data);

View File

@ -45,8 +45,10 @@ struct GlyphCache {
unsigned data_size;
glyph_data_type data_type;
agg::rect_i bounds;
double advance_x;
double advance_y;
float advance_x;
float advance_y;
float inset_left;
float inset_right;
};
class FontCache;
@ -74,7 +76,7 @@ class FontCacheEntry : public MultiLocker, public Referenceable {
bool Init(const ServerFont& font);
bool HasGlyphs(const char* utf8String,
size_t glyphCount) const;
ssize_t glyphCount) const;
const GlyphCache* Glyph(uint16 glyphCode);

View File

@ -365,6 +365,8 @@ FontEngine::FontEngine()
, fBounds(1, 1, 0, 0)
, fAdvanceX(0.0)
, fAdvanceY(0.0)
, fInsetLeft(0.0)
, fInsetRight(0.0)
, fPath()
, fCurves(fPath)
@ -411,6 +413,12 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
if (fLastError != 0)
return false;
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
fInsetLeft = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX);
fInsetRight = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX
+ fFace->glyph->metrics.width - fFace->glyph->metrics.horiAdvance);
switch(fGlyphRendering) {
case glyph_ren_native_mono:
fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_MONO);
@ -428,8 +436,6 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
fBounds.y2 = fScanlineStorageBin.max_y();
fDataSize = fScanlineStorageBin.byte_size();
fDataType = glyph_data_mono;
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
return true;
}
break;
@ -451,8 +457,6 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
fBounds.y2 = fScanlineStorageAA.max_y();
fDataSize = fScanlineStorageAA.byte_size();
fDataType = glyph_data_gray8;
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
return true;
}
break;
@ -470,8 +474,6 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
fBounds.y1 = int(floor(bnd.y1));
fBounds.x2 = int(ceil(bnd.x2));
fBounds.y2 = int(ceil(bnd.y2));
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
return true;
}
break;

View File

@ -95,6 +95,10 @@ class FontEngine {
{ return fAdvanceX; }
double AdvanceY() const
{ return fAdvanceY; }
double InsetLeft() const
{ return fInsetLeft; }
double InsetRight() const
{ return fInsetRight; }
void WriteGlyphTo(uint8* data) const;
@ -123,6 +127,8 @@ class FontEngine {
agg::rect_i fBounds;
double fAdvanceX;
double fAdvanceY;
double fInsetLeft;
double fInsetRight;
// these members are for caching memory allocations
// when rendering glyphs

View File

@ -28,17 +28,17 @@ class GlyphLayoutEngine {
bool kerning = true,
uint8 spacing = B_BITMAP_SPACING);
static bool IsWhiteSpace(uint32 glyphCode);
private:
GlyphLayoutEngine();
virtual ~GlyphLayoutEngine();
static bool _IsWhiteSpace(uint32 glyph);
};
// _IsWhiteSpace
// IsWhiteSpace
inline bool
GlyphLayoutEngine::_IsWhiteSpace(uint32 charCode)
GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
{
switch (charCode) {
case 0x0009: /* tab */
@ -64,6 +64,7 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
const char* utf8String, int32 length,
const escapement_delta* delta, bool kerning, uint8 spacing)
{
// TODO: implement spacing modes
FontCache* cache = FontCache::Default();
FontCacheEntry* entry = cache->FontCacheEntryFor(font);
@ -113,7 +114,7 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
y += advanceY;
if (delta)
x += _IsWhiteSpace(charCode) ? delta->space : delta->nonspace;
x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace;
if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, y))
break;

View File

@ -1416,14 +1416,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
if (!stringArray[i] || lengthArray[i] <= 0)
widthArray[i] = 0.0;
else {
widthArray[i] = fDesktop->GetDrawingEngine()->
StringWidth(stringArray[i], lengthArray[i], font);
// NOTE: The line below will return the exact same thing.
// However, the line above uses the AGG rendering backend,
// for which glyph caching actually works. It is about
// 20 times faster!
// widthArray[i] = font.StringWidth(stringArray[i],
// lengthArray[i]);
widthArray[i] = font.StringWidth(stringArray[i],
lengthArray[i]);
}
}

View File

@ -410,92 +410,171 @@ ServerFont::GetHasGlyphs(const char charArray[], int32 numChars,
}
status_t
ServerFont::GetEdges(const char charArray[], int32 numChars,
edge_info edgeArray[]) const
{
if (!charArray || numChars <= 0 || !edgeArray)
return B_BAD_DATA;
FT_Face face = GetTransformedFace(false, false);
if (!face)
return B_ERROR;
const char *string = charArray;
for (int i = 0; i < numChars; i++) {
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
/ 64 / fSize;
edgeArray[i].right = float(face->glyph->metrics.horiBearingX
+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
/ 64 / fSize;
class EdgesConsumer {
public:
EdgesConsumer(edge_info* edges, float size)
: fEdges(edges)
, fSize(size)
{
}
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
fEdges[index].left = 0.0;
fEdges[index].right = 0.0;
}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y)
{
fEdges[index].left = glyph->inset_left / fSize;
fEdges[index].right = glyph->inset_right / fSize;
return true;
}
PutTransformedFace(face);
return B_OK;
private:
edge_info* fEdges;
float fSize;
};
status_t
ServerFont::GetEdges(const char* string, int32 numChars,
edge_info* edges) const
{
if (!string || numChars <= 0 || !edges)
return B_BAD_DATA;
bool kerning = true; // TODO make this a property?
EdgesConsumer consumer(edges, fSize);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars,
NULL, kerning, fSpacing))
return B_OK;
return B_ERROR;
// FT_Face face = GetTransformedFace(false, false);
// if (!face)
// return B_ERROR;
//
// const char *string = charArray;
// for (int i = 0; i < numChars; i++) {
// FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
// edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
// / 64 / fSize;
// edgeArray[i].right = float(face->glyph->metrics.horiBearingX
// + face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
// / 64 / fSize;
// }
//
// PutTransformedFace(face);
// return B_OK;
}
status_t
ServerFont::GetEscapements(const char charArray[], int32 numChars,
escapement_delta delta, BPoint escapementArray[],
BPoint offsetArray[]) const
{
if (!charArray || numChars <= 0 || !escapementArray)
return B_BAD_DATA;
FT_Face face = GetTransformedFace(true, false);
if (!face)
return B_ERROR;
const char *string = charArray;
for (int i = 0; i < numChars; i++) {
uint32 charCode = UTF8ToCharCode(&string);
FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
escapementArray[i].x = is_white_space(charCode) ? delta.space : delta.nonspace;
escapementArray[i].x += float(face->glyph->advance.x) / 64;
escapementArray[i].y = -float(face->glyph->advance.y) / 64;
escapementArray[i].x /= fSize;
escapementArray[i].y /= fSize;
if (offsetArray) {
class BPointEscapementConsumer {
public:
BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, float size)
: fEscapements(escapements)
, fOffsets(offsets)
, fSize(size)
{
}
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
_Set(index, 0, 0);
}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y)
{
_Set(index, glyph->advance_x, glyph->advance_y);
return true;
}
private:
inline void _Set(int32 index, double x, double y)
{
fEscapements[index].x = x / fSize;
fEscapements[index].y = y / fSize;
if (fOffsets) {
// ToDo: According to the BeBook: "The offsetArray is applied by
// the dynamic spacing in order to improve the relative position
// of the character's width with relation to another character,
// without altering the width." So this will probably depend on
// the spacing mode.
offsetArray[i].x = 0;
offsetArray[i].y = 0;
fOffsets[index].x = 0;
fOffsets[index].y = 0;
}
}
PutTransformedFace(face);
return B_OK;
}
BPoint* fEscapements;
BPoint* fOffsets;
float fSize;
};
status_t
ServerFont::GetEscapements(const char charArray[], int32 numChars,
escapement_delta delta, float widthArray[]) const
ServerFont::GetEscapements(const char* string, int32 numChars,
escapement_delta delta, BPoint escapementArray[],
BPoint offsetArray[]) const
{
if (!charArray || numChars <= 0 || !widthArray)
if (!string || numChars <= 0 || !escapementArray)
return B_BAD_DATA;
FT_Face face = GetTransformedFace(false, false);
if (!face)
return B_ERROR;
bool kerning = true; // TODO make this a property?
const char *string = charArray;
for (int i = 0; i < numChars; i++) {
uint32 charCode = UTF8ToCharCode(&string);
FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
widthArray[i] = is_white_space(charCode) ? delta.space : delta.nonspace;
widthArray[i] += float(face->glyph->metrics.horiAdvance) / 64.0;
widthArray[i] /= fSize;
BPointEscapementConsumer consumer(escapementArray, offsetArray, fSize);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars,
&delta, kerning, fSpacing))
return B_OK;
return B_ERROR;
}
class WidthEscapementConsumer {
public:
WidthEscapementConsumer(float* widths, float size)
: fWidths(widths)
, fSize(size)
{
}
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
fWidths[index] = 0.0;
}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y)
{
fWidths[index] = glyph->advance_x / fSize;
return true;
}
PutTransformedFace(face);
return B_OK;
private:
float* fWidths;
float fSize;
};
status_t
ServerFont::GetEscapements(const char* string, int32 numChars,
escapement_delta delta, float widthArray[]) const
{
if (!string || numChars <= 0 || !widthArray)
return B_BAD_DATA;
bool kerning = true; // TODO make this a property?
WidthEscapementConsumer consumer(widthArray, fSize);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars,
&delta, kerning, fSpacing))
return B_OK;
return B_ERROR;
}
@ -523,18 +602,18 @@ class BoundingBoxConsumer {
const agg::rect_i& r = glyph->bounds;
if (fAsString) {
rectArray[index].left = r.x1 + x;
rectArray[index].top = r.y1 + y - 1;
rectArray[index].top = r.y1 + y;
rectArray[index].right = r.x2 + x + 1;
rectArray[index].bottom = r.y2 + y + 1;
} else {
if (rectArray) {
rectArray[index].left = r.x1;
rectArray[index].top = r.y1 - 1;
rectArray[index].top = r.y1;
rectArray[index].right = r.x2 + 1;
rectArray[index].bottom = r.y2 + 1;
} else {
stringBoundingBox = stringBoundingBox
| BRect(r.x1 + x, r.y1 + y - 1,
| BRect(r.x1 + x, r.y1 + y,
r.x2 + x + 1, r.y2 + y + 1);
}
}

View File

@ -8,11 +8,9 @@ UsePrivateHeaders app graphics interface 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 ] ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter font_support ] ;
UseFreeTypeHeaders ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) drawing_modes ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) font_support ] ;
StaticLibrary libpainter.a :
Painter.cpp