* 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:
parent
25c969f5a8
commit
25a7061652
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user