* 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
+6 -3
View File
@@ -56,7 +56,7 @@ class FontCacheEntry::GlyphCachePool {
GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex, GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex,
unsigned dataSize, glyph_data_type dataType, const agg::rect_i& bounds, 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; unsigned msb = (glyphCode >> 8) & 0xFF;
if (fGlyphs[msb] == 0) { if (fGlyphs[msb] == 0) {
@@ -81,6 +81,8 @@ class FontCacheEntry::GlyphCachePool {
glyph->bounds = bounds; glyph->bounds = bounds;
glyph->advance_x = advanceX; glyph->advance_x = advanceX;
glyph->advance_y = advanceY; glyph->advance_y = advanceY;
glyph->inset_left = insetLeft;
glyph->inset_right = insetRight;
return fGlyphs[msb][lsb] = glyph; return fGlyphs[msb][lsb] = glyph;
} }
@@ -133,7 +135,7 @@ FontCacheEntry::Init(const ServerFont& font)
// HasGlyphs // HasGlyphs
bool bool
FontCacheEntry::HasGlyphs(const char* utf8String, size_t length) const FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{ {
uint32 charCode; uint32 charCode;
const char* start = utf8String; const char* start = utf8String;
@@ -158,7 +160,8 @@ FontCacheEntry::Glyph(uint16 glyphCode)
glyph = fGlyphCache->CacheGlyph(glyphCode, glyph = fGlyphCache->CacheGlyph(glyphCode,
fEngine.GlyphIndex(), fEngine.DataSize(), fEngine.GlyphIndex(), fEngine.DataSize(),
fEngine.DataType(), fEngine.Bounds(), fEngine.DataType(), fEngine.Bounds(),
fEngine.AdvanceX(), fEngine.AdvanceY()); fEngine.AdvanceX(), fEngine.AdvanceY(),
fEngine.InsetLeft(), fEngine.InsetRight());
fEngine.WriteGlyphTo(glyph->data); fEngine.WriteGlyphTo(glyph->data);
+5 -3
View File
@@ -45,8 +45,10 @@ struct GlyphCache {
unsigned data_size; unsigned data_size;
glyph_data_type data_type; glyph_data_type data_type;
agg::rect_i bounds; agg::rect_i bounds;
double advance_x; float advance_x;
double advance_y; float advance_y;
float inset_left;
float inset_right;
}; };
class FontCache; class FontCache;
@@ -74,7 +76,7 @@ class FontCacheEntry : public MultiLocker, public Referenceable {
bool Init(const ServerFont& font); bool Init(const ServerFont& font);
bool HasGlyphs(const char* utf8String, bool HasGlyphs(const char* utf8String,
size_t glyphCount) const; ssize_t glyphCount) const;
const GlyphCache* Glyph(uint16 glyphCode); const GlyphCache* Glyph(uint16 glyphCode);
+8 -6
View File
@@ -365,6 +365,8 @@ FontEngine::FontEngine()
, 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)
, fInsetRight(0.0)
, fPath() , fPath()
, fCurves(fPath) , fCurves(fPath)
@@ -411,6 +413,12 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
if (fLastError != 0) if (fLastError != 0)
return false; 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) { switch(fGlyphRendering) {
case glyph_ren_native_mono: case glyph_ren_native_mono:
fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_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(); fBounds.y2 = fScanlineStorageBin.max_y();
fDataSize = fScanlineStorageBin.byte_size(); fDataSize = fScanlineStorageBin.byte_size();
fDataType = glyph_data_mono; fDataType = glyph_data_mono;
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
return true; return true;
} }
break; break;
@@ -451,8 +457,6 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
fBounds.y2 = fScanlineStorageAA.max_y(); fBounds.y2 = fScanlineStorageAA.max_y();
fDataSize = fScanlineStorageAA.byte_size(); fDataSize = fScanlineStorageAA.byte_size();
fDataType = glyph_data_gray8; fDataType = glyph_data_gray8;
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
return true; return true;
} }
break; break;
@@ -470,8 +474,6 @@ FontEngine::PrepareGlyph(unsigned glyph_code)
fBounds.y1 = int(floor(bnd.y1)); fBounds.y1 = int(floor(bnd.y1));
fBounds.x2 = int(ceil(bnd.x2)); fBounds.x2 = int(ceil(bnd.x2));
fBounds.y2 = int(ceil(bnd.y2)); fBounds.y2 = int(ceil(bnd.y2));
fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
return true; return true;
} }
break; break;
+6
View File
@@ -95,6 +95,10 @@ class FontEngine {
{ return fAdvanceX; } { return fAdvanceX; }
double AdvanceY() const double AdvanceY() const
{ return fAdvanceY; } { return fAdvanceY; }
double InsetLeft() const
{ return fInsetLeft; }
double InsetRight() const
{ return fInsetRight; }
void WriteGlyphTo(uint8* data) const; void WriteGlyphTo(uint8* data) const;
@@ -123,6 +127,8 @@ class FontEngine {
agg::rect_i fBounds; agg::rect_i fBounds;
double fAdvanceX; double fAdvanceX;
double fAdvanceY; double fAdvanceY;
double fInsetLeft;
double fInsetRight;
// these members are for caching memory allocations // these members are for caching memory allocations
// when rendering glyphs // when rendering glyphs
+6 -5
View File
@@ -28,17 +28,17 @@ class GlyphLayoutEngine {
bool kerning = true, bool kerning = true,
uint8 spacing = B_BITMAP_SPACING); uint8 spacing = B_BITMAP_SPACING);
static bool IsWhiteSpace(uint32 glyphCode);
private: private:
GlyphLayoutEngine(); GlyphLayoutEngine();
virtual ~GlyphLayoutEngine(); virtual ~GlyphLayoutEngine();
static bool _IsWhiteSpace(uint32 glyph);
}; };
// _IsWhiteSpace // IsWhiteSpace
inline bool inline bool
GlyphLayoutEngine::_IsWhiteSpace(uint32 charCode) GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
{ {
switch (charCode) { switch (charCode) {
case 0x0009: /* tab */ case 0x0009: /* tab */
@@ -64,6 +64,7 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
const char* utf8String, int32 length, const char* utf8String, int32 length,
const escapement_delta* delta, bool kerning, uint8 spacing) const escapement_delta* delta, bool kerning, uint8 spacing)
{ {
// TODO: implement spacing modes
FontCache* cache = FontCache::Default(); FontCache* cache = FontCache::Default();
FontCacheEntry* entry = cache->FontCacheEntryFor(font); FontCacheEntry* entry = cache->FontCacheEntryFor(font);
@@ -113,7 +114,7 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
y += advanceY; y += advanceY;
if (delta) 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)) if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, y))
break; break;
+2 -8
View File
@@ -1416,14 +1416,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
if (!stringArray[i] || lengthArray[i] <= 0) if (!stringArray[i] || lengthArray[i] <= 0)
widthArray[i] = 0.0; widthArray[i] = 0.0;
else { else {
widthArray[i] = fDesktop->GetDrawingEngine()-> widthArray[i] = font.StringWidth(stringArray[i],
StringWidth(stringArray[i], lengthArray[i], font); lengthArray[i]);
// 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]);
} }
} }
+146 -67
View File
@@ -410,92 +410,171 @@ ServerFont::GetHasGlyphs(const char charArray[], int32 numChars,
} }
status_t class EdgesConsumer {
ServerFont::GetEdges(const char charArray[], int32 numChars, public:
edge_info edgeArray[]) const EdgesConsumer(edge_info* edges, float size)
{ : fEdges(edges)
if (!charArray || numChars <= 0 || !edgeArray) , fSize(size)
return B_BAD_DATA; {
}
FT_Face face = GetTransformedFace(false, false); void Start() {}
if (!face) void Finish(double x, double y) {}
return B_ERROR; void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
const char *string = charArray; fEdges[index].left = 0.0;
for (int i = 0; i < numChars; i++) { fEdges[index].right = 0.0;
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); }
edgeArray[i].left = float(face->glyph->metrics.horiBearingX) bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
/ 64 / fSize; FontCacheEntry* entry, double x, double y)
edgeArray[i].right = float(face->glyph->metrics.horiBearingX {
+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance) fEdges[index].left = glyph->inset_left / fSize;
/ 64 / fSize; fEdges[index].right = glyph->inset_right / fSize;
return true;
} }
PutTransformedFace(face); private:
return B_OK; 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 class BPointEscapementConsumer {
ServerFont::GetEscapements(const char charArray[], int32 numChars, public:
escapement_delta delta, BPoint escapementArray[], BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, float size)
BPoint offsetArray[]) const : fEscapements(escapements)
{ , fOffsets(offsets)
if (!charArray || numChars <= 0 || !escapementArray) , fSize(size)
return B_BAD_DATA; {
}
FT_Face face = GetTransformedFace(true, false); void Start() {}
if (!face) void Finish(double x, double y) {}
return B_ERROR; void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
const char *string = charArray; _Set(index, 0, 0);
for (int i = 0; i < numChars; i++) { }
uint32 charCode = UTF8ToCharCode(&string); bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP); FontCacheEntry* entry, double x, double y)
escapementArray[i].x = is_white_space(charCode) ? delta.space : delta.nonspace; {
escapementArray[i].x += float(face->glyph->advance.x) / 64; _Set(index, glyph->advance_x, glyph->advance_y);
escapementArray[i].y = -float(face->glyph->advance.y) / 64; return true;
escapementArray[i].x /= fSize; }
escapementArray[i].y /= fSize; private:
inline void _Set(int32 index, double x, double y)
if (offsetArray) { {
fEscapements[index].x = x / fSize;
fEscapements[index].y = y / fSize;
if (fOffsets) {
// ToDo: According to the BeBook: "The offsetArray is applied by // ToDo: According to the BeBook: "The offsetArray is applied by
// the dynamic spacing in order to improve the relative position // the dynamic spacing in order to improve the relative position
// of the character's width with relation to another character, // of the character's width with relation to another character,
// without altering the width." So this will probably depend on // without altering the width." So this will probably depend on
// the spacing mode. // the spacing mode.
offsetArray[i].x = 0; fOffsets[index].x = 0;
offsetArray[i].y = 0; fOffsets[index].y = 0;
} }
} }
PutTransformedFace(face); BPoint* fEscapements;
return B_OK; BPoint* fOffsets;
} float fSize;
};
status_t status_t
ServerFont::GetEscapements(const char charArray[], int32 numChars, ServerFont::GetEscapements(const char* string, int32 numChars,
escapement_delta delta, float widthArray[]) const escapement_delta delta, BPoint escapementArray[],
BPoint offsetArray[]) const
{ {
if (!charArray || numChars <= 0 || !widthArray) if (!string || numChars <= 0 || !escapementArray)
return B_BAD_DATA; return B_BAD_DATA;
FT_Face face = GetTransformedFace(false, false); bool kerning = true; // TODO make this a property?
if (!face)
return B_ERROR;
const char *string = charArray; BPointEscapementConsumer consumer(escapementArray, offsetArray, fSize);
for (int i = 0; i < numChars; i++) { if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars,
uint32 charCode = UTF8ToCharCode(&string); &delta, kerning, fSpacing))
FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP); return B_OK;
widthArray[i] = is_white_space(charCode) ? delta.space : delta.nonspace;
widthArray[i] += float(face->glyph->metrics.horiAdvance) / 64.0; return B_ERROR;
widthArray[i] /= fSize; }
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); private:
return B_OK; 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; const agg::rect_i& r = glyph->bounds;
if (fAsString) { if (fAsString) {
rectArray[index].left = r.x1 + x; 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].right = r.x2 + x + 1;
rectArray[index].bottom = r.y2 + y + 1; rectArray[index].bottom = r.y2 + y + 1;
} else { } else {
if (rectArray) { if (rectArray) {
rectArray[index].left = r.x1; rectArray[index].left = r.x1;
rectArray[index].top = r.y1 - 1; rectArray[index].top = r.y1;
rectArray[index].right = r.x2 + 1; rectArray[index].right = r.x2 + 1;
rectArray[index].bottom = r.y2 + 1; rectArray[index].bottom = r.y2 + 1;
} else { } else {
stringBoundingBox = stringBoundingBox 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); r.x2 + x + 1, r.y2 + y + 1);
} }
} }
-2
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 ] ;
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 ] ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter font_support ] ;
UseFreeTypeHeaders ; UseFreeTypeHeaders ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) drawing_modes ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) drawing_modes ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) font_support ] ;
StaticLibrary libpainter.a : StaticLibrary libpainter.a :
Painter.cpp Painter.cpp