diff --git a/src/servers/app/FontCacheEntry.cpp b/src/servers/app/FontCacheEntry.cpp index c2237bbce5..785ba62d28 100644 --- a/src/servers/app/FontCacheEntry.cpp +++ b/src/servers/app/FontCacheEntry.cpp @@ -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); diff --git a/src/servers/app/FontCacheEntry.h b/src/servers/app/FontCacheEntry.h index c2ee178cce..a19e084a34 100644 --- a/src/servers/app/FontCacheEntry.h +++ b/src/servers/app/FontCacheEntry.h @@ -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); diff --git a/src/servers/app/FontEngine.cpp b/src/servers/app/FontEngine.cpp index b739fc5754..63b7ddcd5c 100644 --- a/src/servers/app/FontEngine.cpp +++ b/src/servers/app/FontEngine.cpp @@ -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; diff --git a/src/servers/app/FontEngine.h b/src/servers/app/FontEngine.h index bb57060f2b..45a592051b 100644 --- a/src/servers/app/FontEngine.h +++ b/src/servers/app/FontEngine.h @@ -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 diff --git a/src/servers/app/GlyphLayoutEngine.h b/src/servers/app/GlyphLayoutEngine.h index eac70462d1..76c7343e64 100644 --- a/src/servers/app/GlyphLayoutEngine.h +++ b/src/servers/app/GlyphLayoutEngine.h @@ -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; diff --git a/src/servers/app/ServerApp.cpp b/src/servers/app/ServerApp.cpp index 0f38277769..84d06516bb 100644 --- a/src/servers/app/ServerApp.cpp +++ b/src/servers/app/ServerApp.cpp @@ -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]); } } diff --git a/src/servers/app/ServerFont.cpp b/src/servers/app/ServerFont.cpp index be89ea26f5..116b93f7a0 100644 --- a/src/servers/app/ServerFont.cpp +++ b/src/servers/app/ServerFont.cpp @@ -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); } } diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp b/src/servers/app/drawing/Painter/AGGTextRenderer.cpp similarity index 100% rename from src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp rename to src/servers/app/drawing/Painter/AGGTextRenderer.cpp diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h b/src/servers/app/drawing/Painter/AGGTextRenderer.h similarity index 100% rename from src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h rename to src/servers/app/drawing/Painter/AGGTextRenderer.h diff --git a/src/servers/app/drawing/Painter/Jamfile b/src/servers/app/drawing/Painter/Jamfile index eb060f6dfc..330d90b10f 100644 --- a/src/servers/app/drawing/Painter/Jamfile +++ b/src/servers/app/drawing/Painter/Jamfile @@ -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