From 2222864eed6bc28cff3d837c98d1509dcbcf38bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Thu, 2 Aug 2007 19:10:38 +0000 Subject: [PATCH] * complete overhaul of the font/glyph caching * the previous AGG implementation is superfluous * the new implementation is based on that one, but in a way that allows read/write locking to the list of cache entries (fonts) as well as read/write locking to the cached glyphs per individual font cache entry * new GlyphLayoutEngine.h, which is to be the central place for layouting glyphs along the baseline. It handles the locking for getting the font cache entries. It works by giving it a template class GlyphConsumer which does the actual work. * changed AGGTextRenderer to use the new font cache * changed ServerFont::StringWidth(), and the bounding box stuff to use it * changed DrawingEngine, it doesn't need the global font lock anymore * our BFont thought that GetBoundingBoxesAsGlyphs and GetBoundingBoxesAsString is the same, which of course it isn't, hence the two separate functions... AsGlyphs just gets the bounding box of each glyph in a string, not treating the string as an actual word AsString adds the offset of the glyph in the word to the bounding box * changed ServerProtocol.h accordingly for the different bounding box meaning git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21797 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/interface/Font.h | 2 +- headers/private/app/ServerProtocol.h | 1 + src/kits/interface/Font.cpp | 8 +- src/servers/app/FontCache.cpp | 155 +++ src/servers/app/FontCache.h | 40 + src/servers/app/FontCacheEntry.cpp | 238 +++++ src/servers/app/FontCacheEntry.h | 116 +++ src/servers/app/FontEngine.cpp | 589 +++++++++++ src/servers/app/FontEngine.h | 142 +++ src/servers/app/GlyphLayoutEngine.h | 145 +++ src/servers/app/Jamfile | 7 +- src/servers/app/ProfileMessageSupport.cpp | 1 + src/servers/app/ServerFont.cpp | 223 ++-- src/servers/app/ServerFont.h | 14 +- .../app/drawing/AccelerantHWInterface.cpp | 1 + src/servers/app/drawing/DrawingEngine.cpp | 25 +- src/servers/app/drawing/Painter/Jamfile | 6 - src/servers/app/drawing/Painter/Painter.cpp | 37 +- src/servers/app/drawing/Painter/Painter.h | 4 +- .../Painter/font_support/AGGTextRenderer.cpp | 486 ++++----- .../Painter/font_support/AGGTextRenderer.h | 41 +- .../font_support/agg_font_freetype.cpp | 973 ------------------ .../Painter/font_support/agg_font_freetype.h | 189 ---- src/tests/servers/app/Jamfile | 8 +- 24 files changed, 1841 insertions(+), 1610 deletions(-) create mode 100644 src/servers/app/FontCache.cpp create mode 100644 src/servers/app/FontCache.h create mode 100644 src/servers/app/FontCacheEntry.cpp create mode 100644 src/servers/app/FontCacheEntry.h create mode 100644 src/servers/app/FontEngine.cpp create mode 100644 src/servers/app/FontEngine.h create mode 100644 src/servers/app/GlyphLayoutEngine.h delete mode 100644 src/servers/app/drawing/Painter/font_support/agg_font_freetype.cpp delete mode 100644 src/servers/app/drawing/Painter/font_support/agg_font_freetype.h diff --git a/headers/os/interface/Font.h b/headers/os/interface/Font.h index 42bcda5afe..5448cd4462 100644 --- a/headers/os/interface/Font.h +++ b/headers/os/interface/Font.h @@ -254,7 +254,7 @@ class BFont { void _GetBoundingBoxes(const char charArray[], int32 numChars, font_metric_mode mode, bool string_escapement, escapement_delta *delta, - BRect boundingBoxArray[]) const; + BRect boundingBoxArray[], bool asString) const; }; diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index f8733bf014..029c8a9c77 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -124,6 +124,7 @@ enum { AS_GET_ESCAPEMENTS, AS_GET_ESCAPEMENTS_AS_FLOATS, AS_GET_BOUNDINGBOXES_CHARS, + AS_GET_BOUNDINGBOXES_STRING, AS_GET_BOUNDINGBOXES_STRINGS, AS_GET_HAS_GLYPHS, AS_GET_GLYPH_SHAPES, diff --git a/src/kits/interface/Font.cpp b/src/kits/interface/Font.cpp index b504f399a9..ff9b3b1651 100644 --- a/src/kits/interface/Font.cpp +++ b/src/kits/interface/Font.cpp @@ -1195,7 +1195,7 @@ void BFont::GetBoundingBoxesAsGlyphs(const char charArray[], int32 numChars, font_metric_mode mode, BRect boundingBoxArray[]) const { - _GetBoundingBoxes(charArray, numChars, mode, false, NULL, boundingBoxArray); + _GetBoundingBoxes(charArray, numChars, mode, false, NULL, boundingBoxArray, false); } @@ -1203,13 +1203,13 @@ void BFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars, font_metric_mode mode, escapement_delta *delta, BRect boundingBoxArray[]) const { - _GetBoundingBoxes(charArray, numChars, mode, true, delta, boundingBoxArray); + _GetBoundingBoxes(charArray, numChars, mode, true, delta, boundingBoxArray, true); } void BFont::_GetBoundingBoxes(const char charArray[], int32 numChars, font_metric_mode mode, - bool string_escapement, escapement_delta *delta, BRect boundingBoxArray[]) const + bool string_escapement, escapement_delta *delta, BRect boundingBoxArray[], bool asString) const { if (!charArray || numChars < 1 || !boundingBoxArray) return; @@ -1217,7 +1217,7 @@ BFont::_GetBoundingBoxes(const char charArray[], int32 numChars, font_metric_mod int32 code; BPrivate::AppServerLink link; - link.StartMessage(AS_GET_BOUNDINGBOXES_CHARS); + link.StartMessage(asString ? AS_GET_BOUNDINGBOXES_STRING : AS_GET_BOUNDINGBOXES_CHARS); link.Attach(fFamilyID); link.Attach(fStyleID); link.Attach(fSize); diff --git a/src/servers/app/FontCache.cpp b/src/servers/app/FontCache.cpp new file mode 100644 index 0000000000..b4fda3b417 --- /dev/null +++ b/src/servers/app/FontCache.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "FontCache.h" + +#include +#include +#include + +#include +#include + +#include "AutoLocker.h" + + +using std::nothrow; + + +FontCache +FontCache::sDefaultInstance; + +// #pragma mark - + +// constructor +FontCache::FontCache() + : MultiLocker("FontCache lock") + , fFontCacheEntries() +{ +} + +// destructor +FontCache::~FontCache() +{ + FontMap::Iterator iterator = fFontCacheEntries.GetIterator(); + while (iterator.HasNext()) + iterator.Next().value->RemoveReference(); +} + +// Default +/*static*/ FontCache* +FontCache::Default() +{ + return &sDefaultInstance; +} + +// SetFont +FontCacheEntry* +FontCache::FontCacheEntryFor(const ServerFont& font) +{ + char signature[512]; + FontCacheEntry::GenerateSignature(signature, font); + + AutoReadLocker readLocker(this); + + FontCacheEntry* entry = fFontCacheEntries.Get(signature); + + if (entry) { + // the entry was already there + entry->AddReference(); + return entry; + } + + readLocker.Unlock(); + + AutoWriteLocker locker(this); + if (!locker.IsLocked()) + return NULL; + + // prevent getting screwed by a race condition: + // when we released the readlock above, another thread might have + // gotten the writelock before we have, and might have already + // inserted a cache entry for this font. So we look again if there + // is an entry now, and only then create it if it's still not there, + // all while holding the writelock + entry = fFontCacheEntries.Get(signature); + + if (!entry) { + // remove old entries, keep entries below certain count + _ConstrainEntryCount(); + entry = new (nothrow) FontCacheEntry(); + if (!entry || !entry->Init(font) + || fFontCacheEntries.Put(signature, entry) < B_OK) { + fprintf(stderr, "FontCache::FontCacheEntryFor() - " + "out of memory or no font file\n"); + delete entry; + return NULL; + } + } + + entry->AddReference(); + return entry; +} + +// Recycle +void +FontCache::Recycle(FontCacheEntry* entry) +{ + entry->UpdateUsage(); + entry->RemoveReference(); +} + +static const int32 kMaxEntryCount = 30; + +static inline double +usage_index(uint64 useCount, bigtime_t age) +{ + return 100.0 * useCount / age; +} + +// _ConstrainEntryCount +void +FontCache::_ConstrainEntryCount() +{ + // this function is only ever called with the WriteLock held + if (fFontCacheEntries.Size() < kMaxEntryCount) + return; +//printf("FontCache::_ConstrainEntryCount()\n"); + + FontMap::Iterator iterator = fFontCacheEntries.GetIterator(); + + // NOTE: if kMaxEntryCount has a sane value, there has got to be + // some entries, so using the iterator like that should be ok + FontCacheEntry* leastUsedEntry = iterator.Next().value; + bigtime_t now = system_time(); + bigtime_t age = now - leastUsedEntry->LastUsed(); + uint64 useCount = leastUsedEntry->UsedCount(); + double leastUsageIndex = usage_index(useCount, age); +//printf(" leastUsageIndex: %f\n", leastUsageIndex); + + while (iterator.HasNext()) { + FontCacheEntry* entry = iterator.Next().value; + age = now - entry->LastUsed(); + useCount = entry->UsedCount(); + double usageIndex = usage_index(useCount, age); +//printf(" usageIndex: %f\n", usageIndex); + if (usageIndex < leastUsageIndex) { + leastUsedEntry = entry; + leastUsageIndex = usageIndex; + } + } + + iterator = fFontCacheEntries.GetIterator(); + while (iterator.HasNext()) { + if (iterator.Next().value == leastUsedEntry) { + iterator.Remove(); + leastUsedEntry->RemoveReference(); + break; + } + } +} diff --git a/src/servers/app/FontCache.h b/src/servers/app/FontCache.h new file mode 100644 index 0000000000..45ad1f42f9 --- /dev/null +++ b/src/servers/app/FontCache.h @@ -0,0 +1,40 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef FONT_CACHE_H +#define FONT_CACHE_H + +#include "FontCacheEntry.h" +#include "HashMap.h" +#include "HashString.h" +#include "MultiLocker.h" +#include "ServerFont.h" + + +class FontCache : public MultiLocker { + public: + FontCache(); + virtual ~FontCache(); + + // global instance + static FontCache* Default(); + + FontCacheEntry* FontCacheEntryFor(const ServerFont& font); + void Recycle(FontCacheEntry* entry); + + private: + void _ConstrainEntryCount(); + + static FontCache sDefaultInstance; + + typedef HashMap FontMap; + + FontMap fFontCacheEntries; +}; + +#endif // FONT_CACHE_H diff --git a/src/servers/app/FontCacheEntry.cpp b/src/servers/app/FontCacheEntry.cpp new file mode 100644 index 0000000000..c2237bbce5 --- /dev/null +++ b/src/servers/app/FontCacheEntry.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Maxim Shemanarev + * Stephan Aßmus + */ + +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + + +#include "FontCacheEntry.h" + +#include +#include + +#include + +#include "utf8_functions.h" + +BLocker +FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock"); + + +class FontCacheEntry::GlyphCachePool { + public: + enum block_size_e { block_size = 16384-16 }; + + GlyphCachePool() + : fAllocator(block_size) + { + memset(fGlyphs, 0, sizeof(fGlyphs)); + } + + const GlyphCache* FindGlyph(uint16 glyphCode) const + { + unsigned msb = (glyphCode >> 8) & 0xFF; + if (fGlyphs[msb]) + return fGlyphs[msb][glyphCode & 0xFF]; + return 0; + } + + GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex, + unsigned dataSize, glyph_data_type dataType, const agg::rect_i& bounds, + double advanceX, double advanceY) + { + unsigned msb = (glyphCode >> 8) & 0xFF; + if (fGlyphs[msb] == 0) { + fGlyphs[msb] + = (GlyphCache**)fAllocator.allocate(sizeof(GlyphCache*) * 256, + sizeof(GlyphCache*)); + memset(fGlyphs[msb], 0, sizeof(GlyphCache*) * 256); + } + + unsigned lsb = glyphCode & 0xFF; + if (fGlyphs[msb][lsb]) + return 0; // already exists, do not overwrite + + GlyphCache* glyph + = (GlyphCache*)fAllocator.allocate(sizeof(GlyphCache), + sizeof(double)); + + glyph->glyph_index = glyphIndex; + glyph->data = fAllocator.allocate(dataSize); + glyph->data_size = dataSize; + glyph->data_type = dataType; + glyph->bounds = bounds; + glyph->advance_x = advanceX; + glyph->advance_y = advanceY; + + return fGlyphs[msb][lsb] = glyph; + } + + private: + agg::block_allocator fAllocator; + GlyphCache** fGlyphs[256]; +}; + +// #pragma mark - + +// constructor +FontCacheEntry::FontCacheEntry() + : MultiLocker("FontCacheEntry lock") + , Referenceable() + , fGlyphCache(new GlyphCachePool()) + , fEngine() + , fLastUsedTime(LONGLONG_MIN) + , fUseCounter(0) +{ +} + +// destructor +FontCacheEntry::~FontCacheEntry() +{ +//printf("~FontCacheEntry()\n"); + delete fGlyphCache; +} + +// Init +bool +FontCacheEntry::Init(const ServerFont& font) +{ + glyph_rendering renderingType = glyph_ren_native_gray8; + if (font.Rotation() != 0.0 || font.Shear() != 90.0) + renderingType = glyph_ren_outline; + + // TODO: encoding from font + FT_Encoding charMap = FT_ENCODING_NONE; + bool hinting = true; // TODO: font.Hinting(); + + if (!fEngine.Init(font.Path(), 0, font.Size(), charMap, + renderingType, hinting)) { + fprintf(stderr, "FontCacheEntry::Init() - some error loading font " + "file %s\n", font.Path()); + return false; + } + return true; +} + +// HasGlyphs +bool +FontCacheEntry::HasGlyphs(const char* utf8String, size_t length) const +{ + uint32 charCode; + const char* start = utf8String; + while ((charCode = UTF8ToCharCode(&utf8String))) { + if (!fGlyphCache->FindGlyph(charCode)) + return false; + if (utf8String - start + 1 > length) + break; + } + return true; +} + +// Glyph +const GlyphCache* +FontCacheEntry::Glyph(uint16 glyphCode) +{ + const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode); + if (glyph) { + return glyph; + } else { + if (fEngine.PrepareGlyph(glyphCode)) { + glyph = fGlyphCache->CacheGlyph(glyphCode, + fEngine.GlyphIndex(), fEngine.DataSize(), + fEngine.DataType(), fEngine.Bounds(), + fEngine.AdvanceX(), fEngine.AdvanceY()); + + fEngine.WriteGlyphTo(glyph->data); + + return glyph; + } + } + return 0; +} + +// InitAdaptors +void +FontCacheEntry::InitAdaptors(const GlyphCache* glyph, + double x, double y, GlyphMonoAdapter& monoAdapter, + GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter, + double scale) +{ + if (!glyph) + 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_outline: + pathAdapter.init(glyph->data, glyph->data_size, x, y, scale); + break; + + default: + break; + } +} + +// GetKerning +bool +FontCacheEntry::GetKerning(uint16 glyphCode1, uint16 glyphCode2, + double* x, double* y) +{ + return fEngine.GetKerning(glyphCode1, glyphCode2, x, y); +} + +// GenerateSignature +/*static*/ void +FontCacheEntry::GenerateSignature(char* signature, const ServerFont& font) +{ + glyph_rendering renderingType = glyph_ren_native_gray8; + if (font.Rotation() != 0.0 || font.Shear() != 90.0) + renderingType = glyph_ren_outline; + + // TODO: read more of these from the font + FT_Encoding charMap = FT_ENCODING_NONE; + bool hinting = true; // TODO: font.Hinting(); + + sprintf(signature, "%ld%u%d%d%.1f%d", + font.GetFamilyAndStyle(), charMap, + font.Face(), int(renderingType), font.Size(), hinting); +} + +// UpdateUsage +void +FontCacheEntry::UpdateUsage() +{ + // this is a static lock to prevent usage of too many semaphores, + // but on the other hand, it is not so nice to be using a lock + // here at all + // the hope is that the time is so short to hold this lock, that + // there is not much contention + BAutolock _(sUsageUpdateLock); + + fLastUsedTime = system_time(); + fUseCounter++; +} + diff --git a/src/servers/app/FontCacheEntry.h b/src/servers/app/FontCacheEntry.h new file mode 100644 index 0000000000..c2ee178cce --- /dev/null +++ b/src/servers/app/FontCacheEntry.h @@ -0,0 +1,116 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Maxim Shemanarev + * Stephan Aßmus + */ + +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef FONT_CACHE_ENTRY_H +#define FONT_CACHE_ENTRY_H + + +#include + +#include +#include +#include + +#include "ServerFont.h" +#include "FontEngine.h" +#include "MultiLocker.h" +#include "Referenceable.h" +#include "Transformable.h" + + +struct GlyphCache { + unsigned glyph_index; + uint8* data; + unsigned data_size; + glyph_data_type data_type; + agg::rect_i bounds; + double advance_x; + double advance_y; +}; + +class FontCache; + +class FontCacheEntry : public MultiLocker, public Referenceable { + public: + typedef FontEngine::PathAdapter GlyphPathAdapter; + typedef FontEngine::Gray8Adapter GlyphGray8Adapter; + typedef GlyphGray8Adapter::embedded_scanline GlyphGray8Scanline; + typedef FontEngine::MonoAdapter GlyphMonoAdapter; + typedef GlyphMonoAdapter::embedded_scanline GlyphMonoScanline; + typedef agg::conv_curve CurveConverter; + typedef agg::conv_contour ContourConverter; + + typedef agg::conv_transform + TransformedOutline; + + typedef agg::conv_transform + TransformedContourOutline; + + + FontCacheEntry(); + virtual ~FontCacheEntry(); + + bool Init(const ServerFont& font); + + bool HasGlyphs(const char* utf8String, + size_t glyphCount) const; + + const GlyphCache* Glyph(uint16 glyphCode); + + void InitAdaptors(const GlyphCache* glyph, + double x, double y, + GlyphMonoAdapter& monoAdapter, + GlyphGray8Adapter& gray8Adapter, + GlyphPathAdapter& pathAdapter, + double scale = 1.0); + + bool GetKerning(uint16 glyphCode1, + uint16 glyphCode2, double* x, double* y); + + static void GenerateSignature(char* signature, + const ServerFont& font); + + // private to FontCache class: + void UpdateUsage(); + bigtime_t LastUsed() const + { return fLastUsedTime; } + uint64 UsedCount() const + { return fUseCounter; } + + private: + FontCacheEntry(const FontCacheEntry&); + const FontCacheEntry& operator=(const FontCacheEntry&); + + class GlyphCachePool; + + GlyphCachePool* fGlyphCache; + FontEngine fEngine; + + static BLocker sUsageUpdateLock; + bigtime_t fLastUsedTime; + uint64 fUseCounter; +}; + +#endif // FONT_CACHE_ENTRY_H + diff --git a/src/servers/app/FontEngine.cpp b/src/servers/app/FontEngine.cpp new file mode 100644 index 0000000000..b739fc5754 --- /dev/null +++ b/src/servers/app/FontEngine.cpp @@ -0,0 +1,589 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Maxim Shemanarev + * Stephan Aßmus + */ + +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + + +#include "FontEngine.h" + +#include + +#include +#include + + +static const bool kFlipY = true; + + +// int26p6_to_dbl +static inline double +int26p6_to_dbl(int p) +{ + return double(p) / 64.0; +} + +// dbl_to_int26p6 +static inline int +dbl_to_int26p6(double p) +{ + return int(p * 64.0 + 0.5); +} + + +// decompose_ft_outline +template +bool +decompose_ft_outline(const FT_Outline& outline, bool flip_y, PathStorage& path) +{ + typedef typename PathStorage::value_type value_type; + + FT_Vector v_last; + FT_Vector v_control; + FT_Vector v_start; + double x1, y1, x2, y2, x3, y3; + + FT_Vector* point; + FT_Vector* limit; + char* tags; + + int n; // index of contour in outline + int first; // index of first point in contour + char tag; // current point's state + + first = 0; + + for (n = 0; n < outline.n_contours; n++) { + int last; // index of last point in contour + + last = outline.contours[n]; + limit = outline.points + last; + + v_start = outline.points[first]; + v_last = outline.points[last]; + + v_control = v_start; + + point = outline.points + first; + tags = outline.tags + first; + tag = FT_CURVE_TAG(tags[0]); + + // A contour cannot start with a cubic control point! + if (tag == FT_CURVE_TAG_CUBIC) + return false; + + // check first point to determine origin + if ( tag == FT_CURVE_TAG_CONIC) { + // first point is conic control. Yes, this happens. + if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) { + // start at last point if it is on the curve + v_start = v_last; + limit--; + } else { + // if both first and last points are conic, + // start at their middle and record its position + // for closure + v_start.x = (v_start.x + v_last.x) / 2; + v_start.y = (v_start.y + v_last.y) / 2; + + v_last = v_start; + } + point--; + tags--; + } + + x1 = int26p6_to_dbl(v_start.x); + y1 = int26p6_to_dbl(v_start.y); + if (flip_y) y1 = -y1; + path.move_to(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1))); + + while(point < limit) { + point++; + tags++; + + tag = FT_CURVE_TAG(tags[0]); + switch(tag) { + case FT_CURVE_TAG_ON: { // emit a single line_to + x1 = int26p6_to_dbl(point->x); + y1 = int26p6_to_dbl(point->y); + if (flip_y) y1 = -y1; + path.line_to(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1))); + //path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y)); + continue; + } + + case FT_CURVE_TAG_CONIC: { // consume conic arcs + v_control.x = point->x; + v_control.y = point->y; + + Do_Conic: + if (point < limit) { + FT_Vector vec; + FT_Vector v_middle; + + point++; + tags++; + tag = FT_CURVE_TAG(tags[0]); + + vec.x = point->x; + vec.y = point->y; + + if (tag == FT_CURVE_TAG_ON) { + x1 = int26p6_to_dbl(v_control.x); + y1 = int26p6_to_dbl(v_control.y); + x2 = int26p6_to_dbl(vec.x); + y2 = int26p6_to_dbl(vec.y); + if (flip_y) { y1 = -y1; y2 = -y2; } + path.curve3(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + continue; + } + + if (tag != FT_CURVE_TAG_CONIC) + return false; + + v_middle.x = (v_control.x + vec.x) / 2; + v_middle.y = (v_control.y + vec.y) / 2; + + x1 = int26p6_to_dbl(v_control.x); + y1 = int26p6_to_dbl(v_control.y); + x2 = int26p6_to_dbl(v_middle.x); + y2 = int26p6_to_dbl(v_middle.y); + if (flip_y) { y1 = -y1; y2 = -y2; } + path.curve3(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + + //path.curve3(conv(v_control.x), + // flip_y ? -conv(v_control.y) : conv(v_control.y), + // conv(v_middle.x), + // flip_y ? -conv(v_middle.y) : conv(v_middle.y)); + + v_control = vec; + goto Do_Conic; + } + + x1 = int26p6_to_dbl(v_control.x); + y1 = int26p6_to_dbl(v_control.y); + x2 = int26p6_to_dbl(v_start.x); + y2 = int26p6_to_dbl(v_start.y); + if (flip_y) { y1 = -y1; y2 = -y2; } + path.curve3(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2))); + + //path.curve3(conv(v_control.x), + // flip_y ? -conv(v_control.y) : conv(v_control.y), + // conv(v_start.x), + // flip_y ? -conv(v_start.y) : conv(v_start.y)); + goto Close; + } + + default: { // FT_CURVE_TAG_CUBIC + FT_Vector vec1, vec2; + + if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) + return false; + + vec1.x = point[0].x; + vec1.y = point[0].y; + vec2.x = point[1].x; + vec2.y = point[1].y; + + point += 2; + tags += 2; + + if (point <= limit) { + FT_Vector vec; + + vec.x = point->x; + vec.y = point->y; + + x1 = int26p6_to_dbl(vec1.x); + y1 = int26p6_to_dbl(vec1.y); + x2 = int26p6_to_dbl(vec2.x); + y2 = int26p6_to_dbl(vec2.y); + x3 = int26p6_to_dbl(vec.x); + y3 = int26p6_to_dbl(vec.y); + if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } + path.curve4(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2)), + value_type(dbl_to_int26p6(x3)), + value_type(dbl_to_int26p6(y3))); + + //path.curve4(conv(vec1.x), + // flip_y ? -conv(vec1.y) : conv(vec1.y), + // conv(vec2.x), + // flip_y ? -conv(vec2.y) : conv(vec2.y), + // conv(vec.x), + // flip_y ? -conv(vec.y) : conv(vec.y)); + continue; + } + + x1 = int26p6_to_dbl(vec1.x); + y1 = int26p6_to_dbl(vec1.y); + x2 = int26p6_to_dbl(vec2.x); + y2 = int26p6_to_dbl(vec2.y); + x3 = int26p6_to_dbl(v_start.x); + y3 = int26p6_to_dbl(v_start.y); + if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } + path.curve4(value_type(dbl_to_int26p6(x1)), + value_type(dbl_to_int26p6(y1)), + value_type(dbl_to_int26p6(x2)), + value_type(dbl_to_int26p6(y2)), + value_type(dbl_to_int26p6(x3)), + value_type(dbl_to_int26p6(y3))); + + //path.curve4(conv(vec1.x), + // flip_y ? -conv(vec1.y) : conv(vec1.y), + // conv(vec2.x), + // flip_y ? -conv(vec2.y) : conv(vec2.y), + // conv(v_start.x), + // flip_y ? -conv(v_start.y) : conv(v_start.y)); + goto Close; + } + } + } + + path.close_polygon(); + + Close: + first = last + 1; + } + + return true; +} + +// decompose_ft_bitmap_mono +template +void +decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y, + bool flip_y, Scanline& sl, ScanlineStorage& storage) +{ + int i; + const uint8* buf = (const uint8*)bitmap.buffer; + int pitch = bitmap.pitch; + sl.reset(x, x + bitmap.width); + storage.prepare(); + if (flip_y) { + buf += bitmap.pitch * (bitmap.rows - 1); + y += bitmap.rows; + pitch = -pitch; + } + for (i = 0; i < bitmap.rows; i++) { + sl.reset_spans(); + agg::bitset_iterator bits(buf, 0); + int j; + for (j = 0; j < bitmap.width; j++) { + if (bits.bit()) + sl.add_cell(x + j, agg::cover_full); + ++bits; + } + buf += pitch; + if (sl.num_spans()) { + sl.finalize(y - i - 1); + storage.render(sl); + } + } +} + +// decompose_ft_bitmap_gray8 +template +void +decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y, + bool flip_y, Scanline& sl, ScanlineStorage& storage) +{ + int i, j; + const uint8* buf = (const uint8*)bitmap.buffer; + int pitch = bitmap.pitch; + sl.reset(x, x + bitmap.width); + storage.prepare(); + if (flip_y) { + buf += bitmap.pitch * (bitmap.rows - 1); + y += bitmap.rows; + pitch = -pitch; + } + for (i = 0; i < bitmap.rows; i++) { + sl.reset_spans(); + const uint8* p = buf; + for (j = 0; j < bitmap.width; j++) { + if (*p) + sl.add_cell(x + j, *p); + ++p; + } + buf += pitch; + if (sl.num_spans()) { + sl.finalize(y - i - 1); + storage.render(sl); + } + } +} + + +// #pragma mark - + + +// constructor +FontEngine::FontEngine() + : fLastError(0) + , fLibraryInitialized(false) + , fLibrary(0) + , fFace(NULL) + + , fGlyphRendering(glyph_ren_native_gray8) + , fHinting(true) + + , fGlyphIndex(0) + , fDataSize(0) + , fDataType(glyph_data_invalid) + , fBounds(1, 1, 0, 0) + , fAdvanceX(0.0) + , fAdvanceY(0.0) + + , fPath() + , fCurves(fPath) + , fScanlineAA() + , fScanlineBin() + , fScanlineStorageAA() + , fScanlineStorageBin() +{ + fCurves.approximation_scale(4.0); + + fLastError = FT_Init_FreeType(&fLibrary); + if (fLastError == 0) + fLibraryInitialized = true; +} + +// destructor +FontEngine::~FontEngine() +{ + FT_Done_Face(fFace); + + if (fLibraryInitialized) + FT_Done_FreeType(fLibrary); +} + +// CountFaces +unsigned +FontEngine::CountFaces() const +{ + if (fFace) + return fFace->num_faces; + + return 0; +} + +// PrepareGlyph +bool +FontEngine::PrepareGlyph(unsigned glyph_code) +{ + fGlyphIndex = FT_Get_Char_Index(fFace, glyph_code); + fLastError = FT_Load_Glyph(fFace, fGlyphIndex, + fHinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING); +// fHinting ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_NO_HINTING); + + if (fLastError != 0) + return false; + + switch(fGlyphRendering) { + case glyph_ren_native_mono: + fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_MONO); + if (fLastError == 0) { + decompose_ft_bitmap_mono(fFace->glyph->bitmap, + fFace->glyph->bitmap_left, + kFlipY ? -fFace->glyph->bitmap_top : + fFace->glyph->bitmap_top, + kFlipY, + fScanlineBin, + fScanlineStorageBin); + fBounds.x1 = fScanlineStorageBin.min_x(); + fBounds.y1 = fScanlineStorageBin.min_y(); + fBounds.x2 = fScanlineStorageBin.max_x(); + 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; + + + case glyph_ren_native_gray8: + fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_NORMAL); + if (fLastError == 0) { + decompose_ft_bitmap_gray8(fFace->glyph->bitmap, + fFace->glyph->bitmap_left, + kFlipY ? -fFace->glyph->bitmap_top : + fFace->glyph->bitmap_top, + kFlipY, + fScanlineAA, + fScanlineStorageAA); + fBounds.x1 = fScanlineStorageAA.min_x(); + fBounds.y1 = fScanlineStorageAA.min_y(); + fBounds.x2 = fScanlineStorageAA.max_x(); + 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; + + + case glyph_ren_outline: + fPath.remove_all(); + if (decompose_ft_outline(fFace->glyph->outline, kFlipY, + fPath)) { + + agg::rect_d bnd = fPath.bounding_rect(); + fDataSize = fPath.byte_size(); + fDataType = glyph_data_outline; + fBounds.x1 = int(floor(bnd.x1)); + 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; + } + return false; +} + +// #pragma mark - + +// WriteGlyphTo +void +FontEngine::WriteGlyphTo(uint8* data) const +{ + if (data && fDataSize) { + switch(fDataType) { + case glyph_data_mono: + fScanlineStorageBin.serialize(data); + break; + + case glyph_data_gray8: + fScanlineStorageAA.serialize(data); + break; + + case glyph_data_outline: + fPath.serialize(data); + break; + + case glyph_data_invalid: + default: + break; + } + } +} + +// GetKerning +bool +FontEngine::GetKerning(unsigned first, unsigned second, + double* x, double* y) +{ + if (fFace && first && second && FT_HAS_KERNING(fFace)) { + FT_Vector delta; + FT_Get_Kerning(fFace, first, second, + FT_KERNING_DEFAULT, &delta); + + double dx = int26p6_to_dbl(delta.x); + double dy = int26p6_to_dbl(delta.y); + + *x += dx; + *y += dy; + + return true; + } + return false; +} + + +// #pragma mark - + + +bool +FontEngine::Init(const char* fontFilePath, unsigned faceIndex, double size, + FT_Encoding charMap, glyph_rendering ren_type, bool hinting, + const char* fontFileBuffer, const long fontFileBufferSize) +{ + if (!fLibraryInitialized) + return false; + + fHinting = hinting; + + fLastError = 0; + + FT_Done_Face(fFace); + if (fontFileBuffer && fontFileBufferSize) { + fLastError = FT_New_Memory_Face(fLibrary, + (const FT_Byte*)fontFileBuffer, + fontFileBufferSize, + faceIndex, + &fFace); + } else { + fLastError = FT_New_Face(fLibrary, + fontFilePath, + faceIndex, + &fFace); + } + + if (fLastError != 0) + return false; + + switch(ren_type) { + case glyph_ren_native_mono: + fGlyphRendering = glyph_ren_native_mono; + break; + + case glyph_ren_native_gray8: + fGlyphRendering = glyph_ren_native_gray8; + break; + + case glyph_ren_outline: + if (FT_IS_SCALABLE(fFace)) + fGlyphRendering = glyph_ren_outline; + else + fGlyphRendering = glyph_ren_native_gray8; + break; + } + + FT_Set_Pixel_Sizes(fFace, + unsigned(size * 64.0) >> 6, // pixel_width + unsigned(size * 64.0) >> 6); // pixel_height + + if (charMap != FT_ENCODING_NONE) + fLastError = FT_Select_Charmap(fFace, charMap); + + return fLastError == 0; +} + diff --git a/src/servers/app/FontEngine.h b/src/servers/app/FontEngine.h new file mode 100644 index 0000000000..bb57060f2b --- /dev/null +++ b/src/servers/app/FontEngine.h @@ -0,0 +1,142 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Maxim Shemanarev + * Stephan Aßmus + */ + +//---------------------------------------------------------------------------- +// Anti-Grain Geometry - Version 2.4 +// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +//---------------------------------------------------------------------------- +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://www.antigrain.com +//---------------------------------------------------------------------------- + +#ifndef FONT_ENGINE_H +#define FONT_ENGINE_H + +#include + +#include +#include FT_FREETYPE_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +enum glyph_rendering { + glyph_ren_native_mono, + glyph_ren_native_gray8, + glyph_ren_outline, +}; + + +enum glyph_data_type { + glyph_data_invalid = 0, + glyph_data_mono = 1, + glyph_data_gray8 = 2, + glyph_data_outline = 3 +}; + + +class FontEngine { + public: + typedef agg::serialized_scanlines_adaptor_aa Gray8Adapter; + typedef agg::serialized_scanlines_adaptor_bin MonoAdapter; + typedef agg::scanline_storage_aa8 ScanlineStorageAA; + typedef agg::scanline_storage_bin ScanlineStorageBin; + typedef agg::serialized_integer_path_adaptor PathAdapter; + + FontEngine(); + virtual ~FontEngine(); + + bool Init(const char* fontFilePath, + unsigned face_index, double size, + FT_Encoding char_map, + glyph_rendering ren_type, + bool hinting, + const char* fontFileBuffer = NULL, + const long fontFileBufferSize = 0); + + int LastError() const + { return fLastError; } + unsigned CountFaces() const; + bool Hinting() const + { return fHinting; } + + + bool PrepareGlyph(unsigned glyphCode); + + unsigned GlyphIndex() const + { return fGlyphIndex; } + unsigned DataSize() const + { return fDataSize; } + glyph_data_type DataType() const + { return fDataType; } + const agg::rect_i& Bounds() const + { return fBounds; } + double AdvanceX() const + { return fAdvanceX; } + double AdvanceY() const + { return fAdvanceY; } + + void WriteGlyphTo(uint8* data) const; + + + bool GetKerning(unsigned first, unsigned second, + double* x, double* y); + + private: + // disallowed stuff: + FontEngine(const FontEngine&); + const FontEngine& operator=(const FontEngine&); + + int fLastError; + bool fLibraryInitialized; + FT_Library fLibrary; // handle to library + FT_Face fFace; // FreeType font face handle + + glyph_rendering fGlyphRendering; + bool fHinting; + + // members needed to generate individual glyphs according + // to glyph rendering type + unsigned fGlyphIndex; + unsigned fDataSize; + glyph_data_type fDataType; + agg::rect_i fBounds; + double fAdvanceX; + double fAdvanceY; + + // these members are for caching memory allocations + // when rendering glyphs + typedef agg::path_storage_integer PathStorageType; + typedef agg::conv_curve CurveConverterType; + + PathStorageType fPath; + CurveConverterType fCurves; + agg::scanline_u8 fScanlineAA; + agg::scanline_bin fScanlineBin; + + ScanlineStorageAA fScanlineStorageAA; + ScanlineStorageBin fScanlineStorageBin; +}; + + +#endif // FONT_ENGINE_H diff --git a/src/servers/app/GlyphLayoutEngine.h b/src/servers/app/GlyphLayoutEngine.h new file mode 100644 index 0000000000..eac70462d1 --- /dev/null +++ b/src/servers/app/GlyphLayoutEngine.h @@ -0,0 +1,145 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef GLYPH_LAYOUT_ENGINE_H +#define GLYPH_LAYOUT_ENGINE_H + +#include "utf8_functions.h" + +#include "FontCache.h" +#include "FontCacheEntry.h" +#include "ServerFont.h" + + +class GlyphLayoutEngine { + public: + + template + static bool LayoutGlyphs(GlyphConsumer& consumer, + const ServerFont& font, + const char* utf8String, + int32 length, + const escapement_delta* delta = NULL, + bool kerning = true, + uint8 spacing = B_BITMAP_SPACING); + + private: + GlyphLayoutEngine(); + virtual ~GlyphLayoutEngine(); + + static bool _IsWhiteSpace(uint32 glyph); +}; + + +// _IsWhiteSpace +inline bool +GlyphLayoutEngine::_IsWhiteSpace(uint32 charCode) +{ + switch (charCode) { + case 0x0009: /* tab */ + case 0x000b: /* vertical tab */ + case 0x000c: /* form feed */ + case 0x0020: /* space */ + case 0x00a0: /* non breaking space */ + case 0x000a: /* line feed */ + case 0x000d: /* carriage return */ + case 0x2028: /* line separator */ + case 0x2029: /* paragraph separator */ + return true; + } + + return false; +} + +// LayoutGlyphs +template +inline bool +GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, + const ServerFont& font, + const char* utf8String, int32 length, + const escapement_delta* delta, bool kerning, uint8 spacing) +{ + FontCache* cache = FontCache::Default(); + FontCacheEntry* entry = cache->FontCacheEntryFor(font); + + if (!entry || !entry->ReadLock()) { + cache->Recycle(entry); + return false; + } + + bool needsWriteLock + = !entry->HasGlyphs(utf8String, length); + + if (needsWriteLock) { + entry->ReadUnlock(); + if (!entry->WriteLock()) { + cache->Recycle(entry); + return false; + } + } + + consumer.Start(); + + double x = 0.0; + double y = 0.0; + + double advanceX = 0.0; + double advanceY = 0.0; + + uint32 lastCharCode = 0; + uint32 charCode; + int32 index = 0; + const char* start = utf8String; + while ((charCode = UTF8ToCharCode(&utf8String))) { + + const GlyphCache* glyph = entry->Glyph(charCode); + if (glyph == NULL) { + fprintf(stderr, "failed to load glyph for 0x%04lx (%c)\n", charCode, + isprint(charCode) ? (char)charCode : '-'); + + consumer.ConsumeEmptyGlyph(index, charCode, x, y); + continue; + } + + if (kerning) + entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); + + x += advanceX; + y += advanceY; + + if (delta) + x += _IsWhiteSpace(charCode) ? delta->space : delta->nonspace; + + if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, y)) + break; + + // increment pen position + advanceX = glyph->advance_x; + advanceY = glyph->advance_y; + + lastCharCode = charCode; + index++; + if (utf8String - start + 1 > length) + break; + } + + x += advanceX; + y += advanceY; + consumer.Finish(x, y); + + if (needsWriteLock) + entry->WriteUnlock(); + else + entry->ReadUnlock(); + + cache->Recycle(entry); + return true; +} + + +#endif // GLYPH_LAYOUT_ENGINE_H diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 9560ffc90f..13517a225d 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -1,6 +1,6 @@ SubDir HAIKU_TOP src servers app ; -UseLibraryHeaders png zlib ; +UseLibraryHeaders agg png zlib ; UsePrivateHeaders app graphics input interface kernel shared storage ; UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ; @@ -26,6 +26,9 @@ Server app_server : DrawState.cpp EventDispatcher.cpp EventStream.cpp + FontCache.cpp + FontCacheEntry.cpp + FontEngine.cpp FontFamily.cpp FontManager.cpp FontStyle.cpp @@ -62,7 +65,7 @@ Server app_server : : libtranslation.so libz.so libpng.so libbe.so libasdrawing.a libpainter.a libagg.a libfreetype.so - libtextencoding.so + libtextencoding.so libshared.a : app_server.rdef ; diff --git a/src/servers/app/ProfileMessageSupport.cpp b/src/servers/app/ProfileMessageSupport.cpp index d766d55434..33f42f18cc 100644 --- a/src/servers/app/ProfileMessageSupport.cpp +++ b/src/servers/app/ProfileMessageSupport.cpp @@ -112,6 +112,7 @@ string_for_message_code(uint32 code, BString& string) case AS_GET_ESCAPEMENTS: string = "AS_GET_ESCAPEMENTS"; break; case AS_GET_ESCAPEMENTS_AS_FLOATS: string = "AS_GET_ESCAPEMENTS_AS_FLOATS"; break; case AS_GET_BOUNDINGBOXES_CHARS: string = "AS_GET_BOUNDINGBOXES_CHARS"; break; + case AS_GET_BOUNDINGBOXES_STRING: string = "AS_GET_BOUNDINGBOXES_STRING"; break; case AS_GET_BOUNDINGBOXES_STRINGS: string = "AS_GET_BOUNDINGBOXES_STRINGS"; break; case AS_GET_HAS_GLYPHS: string = "AS_GET_HAS_GLYPHS"; break; case AS_GET_GLYPH_SHAPES: string = "AS_GET_GLYPH_SHAPES"; break; diff --git a/src/servers/app/ServerFont.cpp b/src/servers/app/ServerFont.cpp index 7f176677f5..be89ea26f5 100644 --- a/src/servers/app/ServerFont.cpp +++ b/src/servers/app/ServerFont.cpp @@ -1,17 +1,19 @@ /* - * Copyright 2001-2006, Haiku. + * Copyright 2001-2007, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Jérôme Duval, jerome.duval@free.fr * Michael Lotz + * Stephan Aßmus */ #include "ServerFont.h" #include "Angle.h" +#include "GlyphLayoutEngine.h" #include "FontManager.h" #include "truncate_string.h" #include "utf8_functions.h" @@ -24,6 +26,8 @@ #include #include +#include + #include #include @@ -495,45 +499,110 @@ ServerFont::GetEscapements(const char charArray[], int32 numChars, } -status_t -ServerFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars, - BRect rectArray[], bool stringEscapement, font_metric_mode mode, - escapement_delta delta) -{ - // TODO: The mode is never used - if (!charArray || numChars <= 0 || !rectArray) - return B_BAD_DATA; +class BoundingBoxConsumer { + public: + BoundingBoxConsumer(Transformable& transform, BRect* rectArray, + bool asString) + : rectArray(rectArray) + , stringBoundingBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN) + , fAsString(asString) + , fCurves(fPathAdaptor) + , fContour(fCurves) + , fTransformedOutline(fCurves, transform) + , fTransformedContourOutline(fContour, transform) + , fTransform(transform) + { + } + void Start() {} + void Finish(double x, double y) {} + void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} + bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, + FontCacheEntry* entry, double x, double y) + { + if (glyph->data_type != glyph_data_outline) { + const agg::rect_i& r = glyph->bounds; + if (fAsString) { + rectArray[index].left = r.x1 + x; + rectArray[index].top = r.y1 + y - 1; + 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].right = r.x2 + 1; + rectArray[index].bottom = r.y2 + 1; + } else { + stringBoundingBox = stringBoundingBox + | BRect(r.x1 + x, r.y1 + y - 1, + r.x2 + x + 1, r.y2 + y + 1); + } + } + } else { + if (fAsString) { + entry->InitAdaptors(glyph, x, y, + fMonoAdaptor, fGray8Adaptor, fPathAdaptor); + } else { + entry->InitAdaptors(glyph, 0, 0, + fMonoAdaptor, fGray8Adaptor, fPathAdaptor); + } + double left = 0.0; + double top = 0.0; + double right = -1.0; + double bottom = -1.0; + uint32 pathID[1]; + pathID[0] = 0; + // TODO: use fContour if falseboldwidth is > 0 + agg::bounding_rect(fTransformedOutline, pathID, 0, 1, + &left, &top, &right, &bottom); - FT_Face face = GetTransformedFace(true, true); - if (!face) - return B_ERROR; - - const char *string = charArray; - for (int i = 0; i < numChars; i++) { - uint32 charCode = UTF8ToCharCode(&string); - if (stringEscapement) { - if (i > 0) - rectArray[i].OffsetBy(is_white_space(charCode) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0); - - rectArray[i].OffsetBy(is_white_space(charCode) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0); + if (rectArray) { + rectArray[index] = BRect(left, top, right, bottom); + } else { + stringBoundingBox = stringBoundingBox + | BRect(left, top, right, bottom); + } } - - FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP); - if (i < numChars - 1) { - rectArray[i + 1].left = rectArray[i + 1].right = rectArray[i].left - + face->glyph->metrics.horiAdvance / 64.0; - } - - rectArray[i].left += float(face->glyph->metrics.horiBearingX) / 64.0; - rectArray[i].right += float(face->glyph->metrics.horiBearingX - + face->glyph->metrics.width) / 64.0; - rectArray[i].top = -float(face->glyph->metrics.horiBearingY) / 64.0; - rectArray[i].bottom = float(face->glyph->metrics.height - - face->glyph->metrics.horiBearingY) / 64.0; + return true; } - PutTransformedFace(face); - return B_OK; + BRect* rectArray; + BRect stringBoundingBox; + + private: + bool fAsString; + FontCacheEntry::GlyphPathAdapter fPathAdaptor; + FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; + FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; + + FontCacheEntry::CurveConverter fCurves; + FontCacheEntry::ContourConverter fContour; + + FontCacheEntry::TransformedOutline fTransformedOutline; + FontCacheEntry::TransformedContourOutline fTransformedContourOutline; + + Transformable& fTransform; +}; + + +status_t +ServerFont::GetBoundingBoxes(const char* string, int32 numChars, + BRect rectArray[], bool stringEscapement, font_metric_mode mode, + escapement_delta delta, bool asString) +{ + // TODO: The font_metric_mode is not used + if (!string || numChars <= 0 || !rectArray) + return B_BAD_DATA; + + bool kerning = true; // TODO make this a property? + + Transformable transform(EmbeddedTransformation()); + + BoundingBoxConsumer consumer(transform, rectArray, asString); + if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars, + stringEscapement ? &delta : NULL, kerning, fSpacing)) + return B_OK; + return B_ERROR; } @@ -541,63 +610,60 @@ status_t ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], int32 numStrings, BRect rectArray[], font_metric_mode mode, escapement_delta deltaArray[]) { - // TODO: The mode is never used + // TODO: The font_metric_mode is never used if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) return B_BAD_DATA; - FT_Face face = GetTransformedFace(true, true); - if (!face) - return B_ERROR; + bool kerning = true; // TODO make this a property? + + Transformable transform(EmbeddedTransformation()); for (int32 i = 0; i < numStrings; i++) { int32 numChars = lengthArray[i]; - const char *string = charArray[i]; + const char* string = charArray[i]; escapement_delta delta = deltaArray[i]; - rectArray[i].left = 0.0; - for (int32 j = 0; j < numChars; j++) { - uint32 charCode = UTF8ToCharCode(&string); - FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP); + BoundingBoxConsumer consumer(transform, NULL, true); + if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars, + &delta, kerning, fSpacing)) + return B_ERROR; - // TODO: In my testing the width doesn't seem quite right (a - // little too long), though I need to do more comparisions with BeOS - rectArray[i].right += (face->glyph->advance.x >> 6); - rectArray[i].right += is_white_space(charCode) ? delta.space : delta.nonspace; - - float top = -(face->glyph->metrics.horiBearingY >> 6); - if (top < rectArray[i].top) - rectArray[i].top = top; - float bottom = (face->glyph->metrics.height - - face->glyph->metrics.horiBearingY) >> 6; - if (bottom > rectArray[i].bottom) - rectArray[i].bottom = bottom; - } + rectArray[i] = consumer.stringBoundingBox; } - PutTransformedFace(face); return B_OK; } +class StringWidthConsumer { + public: + StringWidthConsumer() : width(0.0) {} + void Start() {} + void Finish(double x, double y) { width = x; } + void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} + bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, + FontCacheEntry* entry, double x, double y) + { return true; } + + float width; +}; + + float -ServerFont::StringWidth(const char *_string, int32 numChars) const +ServerFont::StringWidth(const char *string, int32 numChars, + const escapement_delta* deltaArray) const { - if (!_string || numChars <= 0) + if (!string || numChars <= 0) return 0.0; - FT_Face face = GetTransformedFace(false, false); - if (!face) + bool kerning = true; // TODO make this a property? + + StringWidthConsumer consumer; + if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numChars, + deltaArray, kerning, fSpacing)) return 0.0; - float width = 0.0; - const char *string = _string; - for (int i = 0; i < numChars; i++) { - FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); - width += face->glyph->advance.x / 64.0; - } - - PutTransformedFace(face); - return width; + return consumer.width; } @@ -655,3 +721,16 @@ ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const delete[] result; } + +Transformable +ServerFont::EmbeddedTransformation() const +{ + // TODO: cache this? + Transformable transform; + + transform.ShearBy(B_ORIGIN, (90.0 - fShear) * PI / 180.0, 0.0); + transform.RotateBy(B_ORIGIN, -fRotation * PI / 180.0); + + return transform; +} + diff --git a/src/servers/app/ServerFont.h b/src/servers/app/ServerFont.h index ba7a25ca80..01c8e0939f 100644 --- a/src/servers/app/ServerFont.h +++ b/src/servers/app/ServerFont.h @@ -1,11 +1,12 @@ /* - * Copyright 2001-2005, Haiku. + * Copyright 2001-2007, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Jérôme Duval, jerome.duval@free.fr * Axel Dörfler, axeld@pinc-software.de + * Stephan Aßmus */ #ifndef SERVER_FONT_H #define SERVER_FONT_H @@ -15,6 +16,7 @@ #include #include "FontFamily.h" +#include "Transformable.h" class BShape; class BString; @@ -135,11 +137,12 @@ class ServerFont { int32 numChars, escapement_delta delta, float widthArray[]) const; - status_t GetBoundingBoxesAsString(const char charArray[], + status_t GetBoundingBoxes(const char charArray[], int32 numChars, BRect rectArray[], bool stringEscapement, font_metric_mode mode, - escapement_delta delta); + escapement_delta delta, + bool asString); status_t GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], int32 numStrings, @@ -147,7 +150,8 @@ class ServerFont { escapement_delta deltaArray[]); float StringWidth(const char *string, - int32 numChars) const; + int32 numChars, + const escapement_delta* delta = NULL) const; bool Lock() const { return fStyle->Lock(); } void Unlock() const { fStyle->Unlock(); } @@ -162,6 +166,8 @@ class ServerFont { uint32 mode, float width) const; + Transformable EmbeddedTransformation() const; + protected: friend class FontStyle; diff --git a/src/servers/app/drawing/AccelerantHWInterface.cpp b/src/servers/app/drawing/AccelerantHWInterface.cpp index 298b03bb87..e4d2b77969 100644 --- a/src/servers/app/drawing/AccelerantHWInterface.cpp +++ b/src/servers/app/drawing/AccelerantHWInterface.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index dcfe82f449..d8aa7c3b04 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -13,8 +13,8 @@ #include #include -#include "AGGTextRenderer.h" #include "DrawState.h" +#include "GlyphLayoutEngine.h" #include "Painter.h" #include "PNGDump.h" #include "ServerBitmap.h" @@ -1035,8 +1035,6 @@ DrawingEngine::DrawString(const char* string, int32 length, { CRASH_IF_NOT_LOCKED - FontLocker locker(&fPainter->Font()); - BPoint penLocation = pt; //bigtime_t now = system_time(); @@ -1071,30 +1069,15 @@ float DrawingEngine::StringWidth(const char* string, int32 length, escapement_delta* delta) { - FontLocker locker(&fPainter->Font()); - - float width = 0.0; -// NOTE: For now it is enough to block on the -// font style lock, this already prevents multiple -// threads from executing this code and avoids a -// deadlock in case another thread holds the font -// lock already and then tries to lock the drawing -// engine after it is already locked here (race condition) - width = fPainter->StringWidth(string, length, delta); - return width; + return fPainter->StringWidth(string, length, delta); } // StringWidth float DrawingEngine::StringWidth(const char* string, int32 length, - const ServerFont& font, escapement_delta* delta) + const ServerFont& font, escapement_delta* delta) { - FontLocker locker(&font); - - AGGTextRenderer* renderer = AGGTextRenderer::Default(); - renderer->SetFont(font); - - return renderer->StringWidth(string, length, delta); + return font.StringWidth(string, length, delta); } // #pragma mark - diff --git a/src/servers/app/drawing/Painter/Jamfile b/src/servers/app/drawing/Painter/Jamfile index 25257df2f9..eb060f6dfc 100644 --- a/src/servers/app/drawing/Painter/Jamfile +++ b/src/servers/app/drawing/Painter/Jamfile @@ -21,11 +21,5 @@ StaticLibrary libpainter.a : # drawing_modes PixelFormat.cpp - # font_support - # is contained within libagg.a, - # but we need a modified version that - # uses the FontManager - agg_font_freetype.cpp - AGGTextRenderer.cpp ; diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index e7abced024..6cece22e34 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -35,7 +35,6 @@ #include "DrawState.h" -#include "AGGTextRenderer.h" #include "DrawingMode.h" #include "PatternHandler.h" #include "RenderingBuffer.h" @@ -79,7 +78,7 @@ Painter::Painter() fMiterLimit(B_DEFAULT_MITER_LIMIT), fPatternHandler(), - fTextRenderer(AGGTextRenderer::Default()) + fTextRenderer() { fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false); @@ -137,7 +136,7 @@ Painter::SetDrawState(const DrawState* data, bool updateFont, if (updateFont) SetFont(data->Font()); - fTextRenderer->SetAntialiasing(!(data->ForceFontAliasing() || data->Font().Flags() & B_DISABLE_ANTIALIASING)); + fTextRenderer.SetAntialiasing(!(data->ForceFontAliasing() || data->Font().Flags() & B_DISABLE_ANTIALIASING)); fSubpixelPrecise = data->SubPixelPrecise(); @@ -992,16 +991,10 @@ Painter::DrawString(const char* utf8String, uint32 length, // instance of the text renderer is used by everyone) _UpdateFont(); - bounds = fTextRenderer->RenderString(utf8String, - length, - &fRenderer, - &fRendererBin, - fUnpackedScanline, - baseLine, - fClippingRegion->Frame(), - false, - &fPenLocation, - delta); + bounds = fTextRenderer.RenderString(utf8String, length, + &fRenderer, &fRendererBin, fUnpackedScanline, + baseLine, fClippingRegion->Frame(), false, + &fPenLocation, delta); SetPattern(oldPattern); @@ -1024,13 +1017,9 @@ Painter::BoundingBox(const char* utf8String, uint32 length, _UpdateFont(); static BRect dummy; - return fTextRenderer->RenderString(utf8String, - length, - &fRenderer, - &fRendererBin, - fUnpackedScanline, - baseLine, dummy, true, penLocation, - delta); + return fTextRenderer.RenderString(utf8String, length, + &fRenderer, &fRendererBin, fUnpackedScanline, + baseLine, dummy, true, penLocation, delta); } // StringWidth @@ -1038,11 +1027,7 @@ float Painter::StringWidth(const char* utf8String, uint32 length, const escapement_delta* delta) { - // make sure the text renderer is using our font (the global - // instance of the text renderer is used by everyone) - _UpdateFont(); - - return fTextRenderer->StringWidth(utf8String, length, delta); + return fFont.StringWidth(utf8String, length, delta); } // #pragma mark - @@ -1150,7 +1135,7 @@ Painter::_Clipped(const BRect& rect) const void Painter::_UpdateFont() const { - fTextRenderer->SetFont(fFont); + fTextRenderer.SetFont(fFont); } // _UpdateDrawingMode diff --git a/src/servers/app/drawing/Painter/Painter.h b/src/servers/app/drawing/Painter/Painter.h index 24149f7508..fd69012e43 100644 --- a/src/servers/app/drawing/Painter/Painter.h +++ b/src/servers/app/drawing/Painter/Painter.h @@ -9,6 +9,7 @@ #ifndef PAINTER_H #define PAINTER_H +#include "AGGTextRenderer.h" #include "FontManager.h" #include "PatternHandler.h" #include "RGBColor.h" @@ -23,7 +24,6 @@ #include -class AGGTextRenderer; class BBitmap; class BRegion; class DrawState; @@ -289,7 +289,7 @@ mutable agg::conv_curve fCurve; // a class handling rendering and caching of glyphs // it is setup to load from a specific Freetype supported // font file which it gets from ServerFont - AGGTextRenderer* fTextRenderer; +mutable AGGTextRenderer fTextRenderer; }; // SetHighColor diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp index 08f83a1886..45f8b1020c 100644 --- a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp +++ b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.cpp @@ -6,29 +6,17 @@ #include "AGGTextRenderer.h" -#include "FontManager.h" -#include "ServerFont.h" -#include "utf8_functions.h" - #include #include #include #include #include -#include -#include -#include -#include -#include - #include #include #include #include -#define FLIP_Y false - #define SHOW_GLYPH_BOUNDS 0 #if SHOW_GLYPH_BOUNDS @@ -36,82 +24,41 @@ # include #endif -// rect_to_int -inline void -rect_to_int(BRect r, - int32& left, int32& top, int32& right, int32& bottom) -{ - left = (int32)floorf(r.left); - top = (int32)floorf(r.top); - right = (int32)ceilf(r.right); - bottom = (int32)ceilf(r.bottom); -} +#include "GlyphLayoutEngine.h" +#include "IntRect.h" -inline bool -is_white_space(uint32 charCode) -{ - switch (charCode) { - case 0x0009: /* tab */ - case 0x000b: /* vertical tab */ - case 0x000c: /* form feed */ - case 0x0020: /* space */ - case 0x00a0: /* non breaking space */ - case 0x000a: /* line feed */ - case 0x000d: /* carriage return */ - case 0x2028: /* line separator */ - case 0x2029: /* paragraph separator */ - return true; - } - - return false; -} - -#define DEFAULT_UNI_CODE_BUFFER_SIZE 2048 - -// init default instance -AGGTextRenderer -AGGTextRenderer::sDefaultInstance; - // constructor AGGTextRenderer::AGGTextRenderer() - : fFontEngine(gFreeTypeLibrary), - fFontCache(fFontEngine), + : fPathAdaptor() + , fGray8Adaptor() + , fGray8Scanline() + , fMonoAdaptor() + , fMonoScanline() - fCurves(fFontCache.path_adaptor()), - fContour(fCurves), + , fCurves(fPathAdaptor) + , fContour(fCurves) - fUnicodeBuffer((char*)malloc(DEFAULT_UNI_CODE_BUFFER_SIZE)), - fUnicodeBufferSize(DEFAULT_UNI_CODE_BUFFER_SIZE), - - fHinted(true), - fAntialias(true), - fKerning(true), - fEmbeddedTransformation() + , fHinted(true) + , fAntialias(true) + , fKerning(true) + , fEmbeddedTransformation() { fCurves.approximation_scale(2.0); fContour.auto_detect_orientation(false); - fFontEngine.flip_y(FLIP_Y); } // destructor AGGTextRenderer::~AGGTextRenderer() { - Unset(); - free(fUnicodeBuffer); -} - -// Default -/*static*/ AGGTextRenderer* -AGGTextRenderer::Default() -{ - return &sDefaultInstance; } // SetFont -bool +void AGGTextRenderer::SetFont(const ServerFont &font) { + fFont = font; + // construct an embedded transformation (rotate & shear) fEmbeddedTransformation.Reset(); fEmbeddedTransformation.ShearBy(B_ORIGIN, @@ -119,17 +66,7 @@ AGGTextRenderer::SetFont(const ServerFont &font) fEmbeddedTransformation.RotateBy(B_ORIGIN, -font.Rotation() * PI / 180.0); - agg::glyph_rendering glyphType = - fHinted && fEmbeddedTransformation.IsIdentity() - && font.FalseBoldWidth() == 0.0 ? - agg::glyph_ren_native_gray8 : - agg::glyph_ren_outline; - - fFontEngine.load_font(font, glyphType, font.Size()); - fContour.width(font.FalseBoldWidth() * 2.0); - - return true; } // SetHinting @@ -137,7 +74,7 @@ void AGGTextRenderer::SetHinting(bool hinting) { fHinted = hinting; - fFontEngine.hinting(fEmbeddedTransformation.IsIdentity() && fHinted); +// fFontEngine.hinting(fEmbeddedTransformation.IsIdentity() && fHinted); } // SetAntialiasing @@ -153,14 +90,187 @@ AGGTextRenderer::SetAntialiasing(bool antialiasing) } } -// Unset -void -AGGTextRenderer::Unset() -{ - // TODO ? release some kind of reference count on the ServerFont? -} +typedef agg::conv_transform + conv_font_trans_type; + +typedef agg::conv_transform + conv_font_contour_trans_type; + +class AGGTextRenderer::StringRenderer { + public: + StringRenderer(const IntRect& clippingFrame, bool dryRun, + renderer_type& solidRenderer, + renderer_bin_type& binRenderer, + scanline_unpacked_type& scanline, + FontCacheEntry::TransformedOutline& transformedGlyph, + FontCacheEntry::TransformedContourOutline& transformedContour, + const Transformable& transform, + const BPoint& transformOffset, + BPoint* nextCharPos, + AGGTextRenderer& renderer) + + : fTransform(transform) + , fTransformOffset(transformOffset) + , fClippingFrame(clippingFrame) + , fDryRun(dryRun) + , fBounds(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN) + , fNextCharPos(nextCharPos) + , fVector(false) + + , fSolidRenderer(solidRenderer) + , fBinRenderer(binRenderer) + , fScanline(scanline) + + , fTransformedGlyph(transformedGlyph) + , fTransformedContour(transformedContour) + + , fRenderer(renderer) + { + } + + void Start() + { + fRenderer.fRasterizer.reset(); + } + void Finish(double x, double y) + { + if (fVector) { + agg::render_scanlines(fRenderer.fRasterizer, fScanline, + fSolidRenderer); + } + + if (fNextCharPos) { + fNextCharPos->x = x; + fNextCharPos->y = y; + fTransform.Transform(fNextCharPos); + } + } + + void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) + { + } + + bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, + FontCacheEntry* entry, double x, double y) + { + // "glyphBounds" is the bounds of the glyph transformed + // by the x y location of the glyph along the base line, + // it is therefor yet "untransformed". + const agg::rect_i& r = glyph->bounds; + IntRect glyphBounds(r.x1 + x, r.y1 + y - 1, + r.x2 + x + 1, r.y2 + y + 1); + // NOTE: "-1"/"+1" converts the glyph bounding box from pixel + // indices to pixel area coordinates + + // track bounding box + if (glyphBounds.IsValid()) + fBounds = fBounds | glyphBounds; + + // render the glyph if this is not a dry run + if (!fDryRun) { + // init the fontmanager's embedded adaptors + // NOTE: The initialization for the "location" of + // the glyph is different depending on wether we + // deal with non-(rotated/sheared) text, in which + // case we have a native FT bitmap. For rotated or + // sheared text, we use AGG vector outlines and + // a transformation pipeline, which will be applied + // _after_ we retrieve the outline, and that's why + // we simply pass x and y, which are untransformed. + + // "glyphBounds" is now transformed into screen coords + // in order to stop drawing when we are already outside + // of the clipping frame + if (glyph->data_type != glyph_data_outline) { + // we cannot use the transformation pipeline + double transformedX = x + fTransformOffset.x; + double transformedY = y + fTransformOffset.y; + entry->InitAdaptors(glyph, transformedX, transformedY, + fRenderer.fMonoAdaptor, + fRenderer.fGray8Adaptor, + fRenderer.fPathAdaptor); + + glyphBounds.OffsetBy(fTransformOffset); + } else { + entry->InitAdaptors(glyph, x, y, + fRenderer.fMonoAdaptor, + fRenderer.fGray8Adaptor, + fRenderer.fPathAdaptor); + + float falseBoldWidth = fRenderer.fContour.width(); + if (falseBoldWidth != 0.0) + glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth); + // TODO: not correct! this is later used for clipping, + // but it doesn't get the rect right + glyphBounds = fTransform.TransformBounds(glyphBounds); + } + + if (fClippingFrame.Intersects(glyphBounds)) { + switch (glyph->data_type) { + case glyph_data_mono: + agg::render_scanlines(fRenderer.fMonoAdaptor, + fRenderer.fMonoScanline, fBinRenderer); + break; + + case glyph_data_gray8: + agg::render_scanlines(fRenderer.fGray8Adaptor, + fRenderer.fGray8Scanline, fSolidRenderer); + break; + + case glyph_data_outline: { + fVector = true; + if (fRenderer.fContour.width() == 0.0) { + fRenderer.fRasterizer.add_path(fTransformedGlyph); + } else { + fRenderer.fRasterizer.add_path(fTransformedContour); + } +#if SHOW_GLYPH_BOUNDS + agg::path_storage p; + p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5); + p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5); + p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5); + p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5); + p.close_polygon(); + agg::conv_stroke ps(p); + ps.width(1.0); + fRenderer.fRasterizer.add_path(ps); +#endif + + break; + } + default: + break; + } + } + } + return true; + } + + IntRect Bounds() const + { + return fBounds; + } + + private: + const Transformable& fTransform; + const BPoint& fTransformOffset; + const IntRect& fClippingFrame; + bool fDryRun; + IntRect fBounds; + BPoint* fNextCharPos; + bool fVector; + + renderer_type& fSolidRenderer; + renderer_bin_type& fBinRenderer; + scanline_unpacked_type& fScanline; + FontCacheEntry::TransformedOutline& fTransformedGlyph; + FontCacheEntry::TransformedContourOutline& fTransformedContour; + AGGTextRenderer& fRenderer; +}; + +// RenderString BRect AGGTextRenderer::RenderString(const char* string, uint32 length, @@ -175,11 +285,6 @@ AGGTextRenderer::RenderString(const char* string, { //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun); - // "bounds" will track the bounding box arround all glyphs that are actually drawn - // it will be calculated in untransformed coordinates within the loop and then - // it is transformed to the real location at the exit of the function. - BRect bounds(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN); - Transformable transform(fEmbeddedTransformation); transform.TranslateBy(baseLine); @@ -188,199 +293,22 @@ AGGTextRenderer::RenderString(const char* string, // use a transformation behind the curves // (only if glyph->data_type == agg::glyph_data_outline) // in the pipeline for the rasterizer - typedef agg::conv_transform - conv_font_trans_type; - conv_font_trans_type transformedOutline(fCurves, transform); - - typedef agg::conv_transform - conv_font_contour_trans_type; - conv_font_contour_trans_type transformedContourOutline(fContour, transform); - float falseBoldWidth = fContour.width(); - - double x = 0.0; - double y0 = 0.0; - double y = y0; - - double advanceX = 0.0; - double advanceY = 0.0; - bool firstLoop = true; + FontCacheEntry::TransformedOutline + transformedOutline(fCurves, transform); + FontCacheEntry::TransformedContourOutline + transformedContourOutline(fContour, transform); // for when we bypass the transformation pipeline BPoint transformOffset(0.0, 0.0); transform.Transform(&transformOffset); - fFontCache.reset(); + StringRenderer renderer(clippingFrame, dryRun, + *solidRenderer, *binRenderer, scanline, + transformedOutline, transformedContourOutline, + transform, transformOffset, nextCharPos, *this); - uint32 charCode; - while ((charCode = UTF8ToCharCode(&string)) > 0) { - // line break (not supported by R5) - /*if (charCode == '\n') { - y0 += LineOffset(); - x = 0.0; - y = y0; - advanceX = 0.0; - advanceY = 0.0; - continue; - }*/ + GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, delta, + fKerning, B_BITMAP_SPACING); - const agg::glyph_cache* glyph = fFontCache.glyph(charCode); - if (glyph == NULL) { - fprintf(stderr, "failed to load glyph for 0x%04lx (%c)\n", charCode, - isprint(charCode) ? (char)charCode : '-'); - - continue; - } - - if (!firstLoop && fKerning) - fFontCache.add_kerning(&advanceX, &advanceY); - - x += advanceX; - y += advanceY; - - if (delta) - x += is_white_space(charCode) ? delta->space : delta->nonspace; - - // "glyphBounds" is the bounds of the glyph transformed - // by the x y location of the glyph along the base line, - // it is therefor yet "untransformed". - const agg::rect_i& r = glyph->bounds; - BRect glyphBounds(r.x1 + x, r.y1 + y - 1, r.x2 + x + 1, r.y2 + y + 1); - // NOTE: "-1"/"+1" converts the glyph bounding box from pixel - // indices to pixel area coordinates - - // track bounding box - if (glyphBounds.IsValid()) - bounds = bounds | glyphBounds; - - // render the glyph if this is not a dry run - if (!dryRun) { - // init the fontmanager's embedded adaptors - // NOTE: The initialization for the "location" of - // the glyph is different depending on wether we - // deal with non-(rotated/sheared) text, in which - // case we have a native FT bitmap. For rotated or - // sheared text, we use AGG vector outlines and - // a transformation pipeline, which will be applied - // _after_ we retrieve the outline, and that's why - // we simply pass x and y, which are untransformed. - - // "glyphBounds" is now transformed into screen coords - // in order to stop drawing when we are already outside - // of the clipping frame - if (glyph->data_type != agg::glyph_data_outline) { - // we cannot use the transformation pipeline - double transformedX = x + transformOffset.x; - double transformedY = y + transformOffset.y; - fFontCache.init_embedded_adaptors(glyph, - transformedX, transformedY); - glyphBounds.OffsetBy(transformOffset); - } else { - fFontCache.init_embedded_adaptors(glyph, x, y); - if (falseBoldWidth != 0.0) - glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth); - glyphBounds = transform.TransformBounds(glyphBounds); - } - - if (clippingFrame.Intersects(glyphBounds)) { - switch (glyph->data_type) { - case agg::glyph_data_mono: - agg::render_scanlines(fFontCache.mono_adaptor(), - fFontCache.mono_scanline(), *binRenderer); - break; - - case agg::glyph_data_gray8: - agg::render_scanlines(fFontCache.gray8_adaptor(), - fFontCache.gray8_scanline(), *solidRenderer); - break; - - case agg::glyph_data_outline: { - fRasterizer.reset(); - - if (fContour.width() == 0.0) { - fRasterizer.add_path(transformedOutline); - } else { - fRasterizer.add_path(transformedContourOutline); - } -#if SHOW_GLYPH_BOUNDS - agg::path_storage p; - p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5); - p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5); - p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5); - p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5); - p.close_polygon(); - agg::conv_stroke ps(p); - ps.width(1.0); - fRasterizer.add_path(ps); -#endif - - agg::render_scanlines(fRasterizer, scanline, - *solidRenderer); - break; - } - default: - break; - } - } - } - - // increment pen position - advanceX = glyph->advance_x; - advanceY = glyph->advance_y; - - firstLoop = false; - } - - // put pen location behind rendered text - // (at the baseline of the virtual next glyph) - if (nextCharPos) { - nextCharPos->x = x + advanceX; - nextCharPos->y = y + advanceY; - - transform.Transform(nextCharPos); - } - - return transform.TransformBounds(bounds); + return transform.TransformBounds(renderer.Bounds()); } - - -double -AGGTextRenderer::StringWidth(const char* string, uint32 length, - const escapement_delta* delta) -{ - // NOTE: The implementation does not take font rotation (or shear) - // into account. Just like on R5. Should it ever be desirable to - // "fix" this, simply use (before "return width;"): - // - // BPoint end(width, 0.0); - // fEmbeddedTransformation.Transform(&end); - // width = fabs(end.x); - // - // Note that shear will not have any influence on the baseline though. - - double width = 0.0; - uint32 charCode; - double y = 0.0; - const agg::glyph_cache* glyph; - bool firstLoop = true; - - fFontCache.reset(); - - while ((charCode = UTF8ToCharCode(&string)) > 0) { - glyph = fFontCache.glyph(charCode); - if (glyph) { - if (!firstLoop && fKerning) - fFontCache.add_kerning(&width, &y); - width += glyph->advance_x; - - if (delta) { - width += is_white_space(charCode) ? - delta->space : delta->nonspace; - } - } - - firstLoop = false; - }; - - return width; -} - diff --git a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h index ad5bfa34b5..0bcc8466c1 100644 --- a/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h +++ b/src/servers/app/drawing/Painter/font_support/AGGTextRenderer.h @@ -5,9 +5,9 @@ #ifndef AGG_TEXT_RENDERER_H #define AGG_TEXT_RENDERER_H -#include "agg_font_freetype.h" #include "defines.h" +#include "FontCacheEntry.h" #include "ServerFont.h" #include "Transformable.h" @@ -21,16 +21,7 @@ class AGGTextRenderer { AGGTextRenderer(); virtual ~AGGTextRenderer(); - // NOTE: every Painter instance is using the same - // AGGTextRenderer instance, and the only thing that - // protects locking is the fact that every use of a - // ServerFont goes through a global lock... this will - // have to be changed. Maybe every ServerFont should - // have it's own AGGTextRenderer or something - static AGGTextRenderer* Default(); - - bool SetFont(const ServerFont &font); - void Unset(); + void SetFont(const ServerFont &font); void SetHinting(bool hinting); bool Hinting() const @@ -55,33 +46,27 @@ class AGGTextRenderer { BPoint* nextCharPos = NULL, const escapement_delta* delta = NULL); - double StringWidth(const char* utf8String, - uint32 length, - const escapement_delta* delta = NULL); - private: - typedef agg::font_engine_freetype_int32 font_engine_type; - typedef agg::font_cache_manager font_cache_type; - typedef agg::conv_curve - conv_font_curve_type; - typedef agg::conv_contour conv_font_contour_type; - - font_engine_type fFontEngine; - font_cache_type fFontCache; + class StringRenderer; + friend class StringRenderer; // Pipeline to process the vectors glyph paths (curves + contour) - conv_font_curve_type fCurves; - conv_font_contour_type fContour; + FontCacheEntry::GlyphPathAdapter fPathAdaptor; + FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; + FontCacheEntry::GlyphGray8Scanline fGray8Scanline; + FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; + FontCacheEntry::GlyphMonoScanline fMonoScanline; + + FontCacheEntry::CurveConverter fCurves; + FontCacheEntry::ContourConverter fContour; rasterizer_type fRasterizer; // NOTE: the object has it's own rasterizer object // since it might be using a different gamma setting // to support non-anti-aliased text rendering - char* fUnicodeBuffer; - int32 fUnicodeBufferSize; - + ServerFont fFont; bool fHinted; // is glyph hinting active? bool fAntialias; bool fKerning; diff --git a/src/servers/app/drawing/Painter/font_support/agg_font_freetype.cpp b/src/servers/app/drawing/Painter/font_support/agg_font_freetype.cpp deleted file mode 100644 index 0d67c695ea..0000000000 --- a/src/servers/app/drawing/Painter/font_support/agg_font_freetype.cpp +++ /dev/null @@ -1,973 +0,0 @@ -//---------------------------------------------------------------------------- -// Copyright 2005 Michael Lotz -// -// Anti-Grain Geometry - Version 2.2 -// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://www.antigrain.com -//---------------------------------------------------------------------------- -// modified to use family and style id as glyph cache lookup - - -#include "agg_font_freetype.h" -#include "agg_bitset_iterator.h" -#include "agg_renderer_scanline.h" - -#include "ServerFont.h" -#include "FontFamily.h" - -#include - - -namespace agg { - //------------------------------------------------------------------------------ - // - // This code implements the AUTODIN II polynomial - // The variable corresponding to the macro argument "crc" should - // be an unsigned long. - // Oroginal code by Spencer Garrett - // - - // generated using the AUTODIN II polynomial - // x^32 + x^26 + x^23 + x^22 + x^16 + - // x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - // - //------------------------------------------------------------------------------ - - static const unsigned crc32tab[256] = - { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - }; - - - //------------------------------------------------------------------------------ - -// static unsigned calc_crc32(const unsigned char* buf, unsigned size) -// { -// unsigned crc = (unsigned)~0; -// const unsigned char* p; -// unsigned len = 0; -// unsigned nr = size; -// -// for (len += nr, p = buf; nr--; ++p) -// { -// crc = (crc >> 8) ^ crc32tab[(crc ^ *p) & 0xff]; -// } -// return ~crc; -// } - - - //------------------------------------------------------------------------ - inline double conv_coord_64(int v) - { - return double(v) / 64.0; - } - - - //------------------------------------------------------------------------ - inline int conv_coord_none(int v) - { - return v; - } - - - //------------------------------------------------------------------------ - template - bool decompose_ft_outline(const FT_Outline& outline, - bool flip_y, - PathStorage& path, - ConvCoord conv) - { - FT_Vector v_last; - FT_Vector v_control; - FT_Vector v_start; - - FT_Vector* point; - FT_Vector* limit; - char* tags; - - int n; // index of contour in outline - int first; // index of first point in contour - char tag; // current point's state - - first = 0; - - for(n = 0; n < outline.n_contours; n++) - { - int last; // index of last point in contour - - last = outline.contours[n]; - limit = outline.points + last; - - v_start = outline.points[first]; - v_last = outline.points[last]; - - v_control = v_start; - - point = outline.points + first; - tags = outline.tags + first; - tag = FT_CURVE_TAG(tags[0]); - - // A contour cannot start with a cubic control point! - if(tag == FT_CURVE_TAG_CUBIC) return false; - - // check first point to determine origin - if( tag == FT_CURVE_TAG_CONIC) - { - // first point is conic control. Yes, this happens. - if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) - { - // start at last point if it is on the curve - v_start = v_last; - limit--; - } - else - { - // if both first and last points are conic, - // start at their middle and record its position - // for closure - v_start.x = (v_start.x + v_last.x) / 2; - v_start.y = (v_start.y + v_last.y) / 2; - - v_last = v_start; - } - point--; - tags--; - } - - path.move_to(conv(v_start.x), flip_y ? -conv(v_start.y) : conv(v_start.y)); - - while(point < limit) - { - point++; - tags++; - - tag = FT_CURVE_TAG(tags[0]); - switch(tag) - { - case FT_CURVE_TAG_ON: // emit a single line_to - { - path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y)); - continue; - } - - case FT_CURVE_TAG_CONIC: // consume conic arcs - { - v_control.x = point->x; - v_control.y = point->y; - - Do_Conic: - if(point < limit) - { - FT_Vector vec; - FT_Vector v_middle; - - point++; - tags++; - tag = FT_CURVE_TAG(tags[0]); - - vec.x = point->x; - vec.y = point->y; - - if(tag == FT_CURVE_TAG_ON) - { - path.curve3(conv(v_control.x), - flip_y ? -conv(v_control.y) : conv(v_control.y), - conv(vec.x), - flip_y ? -conv(vec.y) : conv(vec.y)); - continue; - } - - if(tag != FT_CURVE_TAG_CONIC) return false; - - v_middle.x = (v_control.x + vec.x) / 2; - v_middle.y = (v_control.y + vec.y) / 2; - - path.curve3(conv(v_control.x), - flip_y ? -conv(v_control.y) : conv(v_control.y), - conv(v_middle.x), - flip_y ? -conv(v_middle.y) : conv(v_middle.y)); - - v_control = vec; - goto Do_Conic; - } - path.curve3(conv(v_control.x), - flip_y ? -conv(v_control.y) : conv(v_control.y), - conv(v_start.x), - flip_y ? -conv(v_start.y) : conv(v_start.y)); - goto Close; - } - - default: // FT_CURVE_TAG_CUBIC - { - FT_Vector vec1, vec2; - - if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) - { - return false; - } - - vec1.x = point[0].x; - vec1.y = point[0].y; - vec2.x = point[1].x; - vec2.y = point[1].y; - - point += 2; - tags += 2; - - if(point <= limit) - { - FT_Vector vec; - - vec.x = point->x; - vec.y = point->y; - - path.curve4(conv(vec1.x), - flip_y ? -conv(vec1.y) : conv(vec1.y), - conv(vec2.x), - flip_y ? -conv(vec2.y) : conv(vec2.y), - conv(vec.x), - flip_y ? -conv(vec.y) : conv(vec.y)); - continue; - } - - path.curve4(conv(vec1.x), - flip_y ? -conv(vec1.y) : conv(vec1.y), - conv(vec2.x), - flip_y ? -conv(vec2.y) : conv(vec2.y), - conv(v_start.x), - flip_y ? -conv(v_start.y) : conv(v_start.y)); - goto Close; - } - } - } - - path.close_polygon(); - - Close: - first = last + 1; - } - - return true; - } - - - - //------------------------------------------------------------------------ - template - void decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, - int x, int y, - bool flip_y, - Scanline& sl, - ScanlineStorage& storage) - { - int i; - const int8u* buf = (const int8u*)bitmap.buffer; - int pitch = bitmap.pitch; - sl.reset(x, x + bitmap.width); - storage.prepare(); - if(flip_y) - { - buf += bitmap.pitch * (bitmap.rows - 1); - y += bitmap.rows; - pitch = -pitch; - } - for(i = 0; i < bitmap.rows; i++) - { - sl.reset_spans(); - bitset_iterator bits(buf, 0); - int j; - for(j = 0; j < bitmap.width; j++) - { - if(bits.bit()) sl.add_cell(x + j, cover_full); - ++bits; - } - buf += pitch; - if(sl.num_spans()) - { - sl.finalize(y - i - 1); - storage.render(sl); - } - } - } - - - - //------------------------------------------------------------------------ - template - void decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, - int x, int y, - bool flip_y, - Rasterizer& ras, - Scanline& sl, - ScanlineStorage& storage) - { - int i, j; - const int8u* buf = (const int8u*)bitmap.buffer; - int pitch = bitmap.pitch; - sl.reset(x, x + bitmap.width); - storage.prepare(); - if(flip_y) - { - buf += bitmap.pitch * (bitmap.rows - 1); - y += bitmap.rows; - pitch = -pitch; - } - for(i = 0; i < bitmap.rows; i++) - { - sl.reset_spans(); - const int8u* p = buf; - for(j = 0; j < bitmap.width; j++) - { - if(*p) sl.add_cell(x + j, ras.apply_gamma(*p)); - ++p; - } - buf += pitch; - if(sl.num_spans()) - { - sl.finalize(y - i - 1); - storage.render(sl); - } - } - } - - - - - - - - - - - - - - //------------------------------------------------------------------------ - font_engine_freetype_base::~font_engine_freetype_base() - { -// delete [] m_face_ids; - delete [] m_signature; - } - - - //------------------------------------------------------------------------ - font_engine_freetype_base::font_engine_freetype_base(bool flag32, FT_Library library, - unsigned max_faces) : - m_flag32(flag32), - m_change_stamp(0), - m_last_error(0), - m_cur_id(0), - m_face_index(0), - m_char_map(FT_ENCODING_NONE), - m_signature(new char [256+256-16]), - m_size(0), - m_hinting(true), - m_flip_y(false), - m_library(library), -// m_face_ids(new unsigned [max_faces]), -// m_num_faces(0), -// m_max_faces(max_faces), - m_cur_face(0), - m_resolution(0), - m_glyph_rendering(glyph_ren_native_gray8), - m_glyph_index(0), - m_data_size(0), - m_data_type(glyph_data_invalid), - m_bounds(1,1,0,0), - m_advance_x(0.0), - m_advance_y(0.0), - - m_path16(), - m_path32(), - m_curves16(m_path16), - m_curves32(m_path32), - m_scanline_aa(), - m_scanline_bin(), - m_scanlines_aa(), - m_scanlines_bin(), - m_rasterizer() - { - m_matrix.xx = 0x10000L; - m_matrix.xy = 0; - m_matrix.yx = 0; - m_matrix.yy = 0x10000L; - m_curves16.approximation_scale(4.0); - m_curves32.approximation_scale(4.0); - m_last_error = 0; - } - - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::resolution(unsigned dpi) - { - m_resolution = dpi; - update_char_size(); - } - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::load_font(const ServerFont &font, - glyph_rendering ren_type, - double size) - { - if (m_cur_face == font.GetFTFace() - && m_cur_id == font.GetFamilyAndStyle() - && m_glyph_rendering == ren_type - && m_size == unsigned(size * 64.0)) { - // make sure the freetype lib is in sync too - FT_Set_Pixel_Sizes(m_cur_face, - m_size >> 6, // pixel_width - m_size >> 6); // pixel_height - return true; - } - - m_cur_face = font.GetFTFace(); - m_cur_id = font.GetFamilyAndStyle(); - - m_size = unsigned(size * 64.0); - - switch(ren_type) { - case glyph_ren_native_mono: - m_glyph_rendering = glyph_ren_native_mono; - break; - case glyph_ren_native_gray8: - m_hinting = true; - m_glyph_rendering = glyph_ren_native_gray8; - break; - case glyph_ren_outline: - if(FT_IS_SCALABLE(m_cur_face)) - m_glyph_rendering = glyph_ren_outline; - else - m_glyph_rendering = glyph_ren_native_gray8; - break; - case glyph_ren_agg_mono: - if(FT_IS_SCALABLE(m_cur_face)) - m_glyph_rendering = glyph_ren_agg_mono; - else - m_glyph_rendering = glyph_ren_native_mono; - break; - case glyph_ren_agg_gray8: - if(FT_IS_SCALABLE(m_cur_face)) - m_glyph_rendering = glyph_ren_agg_gray8; - else - m_glyph_rendering = glyph_ren_native_gray8; - break; - } - -// NOTE: Freetype (embedded) transformation not used -// (and it would result in update_signature() being called twice -// update_transform(); - update_char_size(); - return true; - } - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::attach(const char* file_name) - { - if(m_cur_face) - { - m_last_error = FT_Attach_File(m_cur_face, file_name); - return m_last_error == 0; - } - return false; - } - - //------------------------------------------------------------------------ - unsigned font_engine_freetype_base::num_faces() const - { - if(m_cur_face) - { - return m_cur_face->num_faces; - } - return 0; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::char_map(FT_Encoding char_map) - { - if(m_cur_face) - { - m_last_error = FT_Select_Charmap(m_cur_face, m_char_map); - if(m_last_error == 0) - { - update_signature(); - return true; - } - } - return false; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::size(double size) - { - m_size = int(size * 64.0); - if(m_cur_face) - { - update_char_size(); - return true; - } - return false; - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_transform() - { - FT_Matrix mtx = m_matrix; - - if(m_flip_y) - { - mtx.xy = -mtx.xy; - mtx.yy = -mtx.yy; - } - - if(m_cur_face) - { - FT_Vector pen; - pen.x = 0; - pen.y = 0; - FT_Set_Transform(m_cur_face, &mtx, &pen); - update_signature(); - } - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::transform(const trans_affine& mtx) - { - double m[6]; - mtx.store_to(m); - - m_matrix.xx = long( m[0] * 0x10000L); - m_matrix.xy = long(-m[1] * 0x10000L); - m_matrix.yx = long(-m[2] * 0x10000L); - m_matrix.yy = long( m[3] * 0x10000L); - update_transform(); - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::transform(double xx, double xy, - double yx, double yy) - { - m_matrix.xx = long( xx * 0x10000L); - m_matrix.xy = long(-xy * 0x10000L); - m_matrix.yx = long(-yx * 0x10000L); - m_matrix.yy = long( yy * 0x10000L); - update_transform(); - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::hinting(bool h) - { - if(m_hinting != h) - { - m_hinting = h; - if(m_cur_face) - { - update_signature(); - } - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::flip_y(bool f) - { - m_flip_y = f; - if(m_cur_face) - { - update_transform(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_signature() - { - if(m_cur_face) - { -// NOTE: gamma is fixed for now, so no need to switch -// font cache based on gamma -// unsigned gamma_hash = 0; -// if(m_glyph_rendering == glyph_ren_native_gray8 || -// m_glyph_rendering == glyph_ren_agg_mono || -// m_glyph_rendering == glyph_ren_agg_gray8) -// { -// unsigned char gamma_table[rasterizer_scanline_aa<>::aa_num]; -// unsigned i; -// for(i = 0; i < rasterizer_scanline_aa<>::aa_num; ++i) -// { -// gamma_table[i] = m_rasterizer.apply_gamma(i); -// } -// gamma_hash = calc_crc32(gamma_table, sizeof(gamma_table)); -// } -// -// sprintf(m_signature, -// "%u,%u,%d,%d,%d:%dx%d,%d,%d,%d,%d,%d,%d,%08X", -// m_cur_id, -// m_char_map, -// m_face_index, -// int(m_glyph_rendering), -// m_resolution, -// m_height, -// m_width, -// int(m_matrix.xx), -// int(m_matrix.xy), -// int(m_matrix.yx), -// int(m_matrix.yy), -// int(m_hinting), -// int(m_flip_y), -// gamma_hash); - sprintf(m_signature, - "%u,%u,%d,%d,%d,%d", - m_cur_id, - m_char_map, - m_face_index, - int(m_glyph_rendering), - m_size, - int(m_hinting)); - ++m_change_stamp; - } - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_char_size() - { - if(m_cur_face) - { - if(m_resolution) - { - FT_Set_Char_Size(m_cur_face, - m_size, // char_width in 1/64th of points - m_size, // char_height in 1/64th of points - m_resolution, // horizontal device resolution - m_resolution); // vertical device resolution - } - else - { - FT_Set_Pixel_Sizes(m_cur_face, - m_size >> 6, // pixel_width - m_size >> 6); // pixel_height - } - update_signature(); - } - } - - - - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::prepare_glyph(unsigned glyph_code) - { -// bool flip = false; - bool flip = true; - - - m_glyph_index = FT_Get_Char_Index(m_cur_face, glyph_code); - m_last_error = FT_Load_Glyph(m_cur_face, - m_glyph_index, - m_hinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING); -// m_hinting ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_NO_HINTING); - if(m_last_error == 0) - { - switch(m_glyph_rendering) - { - case glyph_ren_native_mono: - m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_MONO); - if(m_last_error == 0) - { - decompose_ft_bitmap_mono(m_cur_face->glyph->bitmap, - m_cur_face->glyph->bitmap_left, - flip ? -m_cur_face->glyph->bitmap_top : - m_cur_face->glyph->bitmap_top, - flip, - m_scanline_bin, - m_scanlines_bin); - m_bounds.x1 = m_scanlines_bin.min_x(); - m_bounds.y1 = m_scanlines_bin.min_y(); - m_bounds.x2 = m_scanlines_bin.max_x(); - m_bounds.y2 = m_scanlines_bin.max_y(); - m_data_size = m_scanlines_bin.byte_size(); - m_data_type = glyph_data_mono; - m_advance_x = double(m_cur_face->glyph->advance.x) / 64.0; - m_advance_y = double(m_cur_face->glyph->advance.y) / 64.0; - return true; - } - break; - - - case glyph_ren_native_gray8: - m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_NORMAL); - if(m_last_error == 0) - { - decompose_ft_bitmap_gray8(m_cur_face->glyph->bitmap, - m_cur_face->glyph->bitmap_left, - flip ? -m_cur_face->glyph->bitmap_top : - m_cur_face->glyph->bitmap_top, - flip, - m_rasterizer, - m_scanline_aa, - m_scanlines_aa); - m_bounds.x1 = m_scanlines_aa.min_x(); - m_bounds.y1 = m_scanlines_aa.min_y(); - m_bounds.x2 = m_scanlines_aa.max_x(); - m_bounds.y2 = m_scanlines_aa.max_y(); - m_data_size = m_scanlines_aa.byte_size(); - m_data_type = glyph_data_gray8; - m_advance_x = double(m_cur_face->glyph->advance.x) / 64.0; - m_advance_y = double(m_cur_face->glyph->advance.y) / 64.0; - return true; - } - break; - - - case glyph_ren_outline: - if(m_last_error == 0) - { - if(m_flag32) - { - m_path32.remove_all(); - if(decompose_ft_outline(m_cur_face->glyph->outline, - flip, - m_path32, - conv_coord_none)) - { - rect_d bnd = m_path32.bounding_rect(); - m_data_size = m_path32.byte_size(); - m_data_type = glyph_data_outline; - m_bounds.x1 = int(floor(bnd.x1)); - m_bounds.y1 = int(floor(bnd.y1)); - m_bounds.x2 = int(ceil(bnd.x2)); - m_bounds.y2 = int(ceil(bnd.y2)); - m_advance_x = double(m_cur_face->glyph->advance.x) / 64.0; - m_advance_y = double(m_cur_face->glyph->advance.y) / 64.0; - return true; - } - } - else - { - m_path16.remove_all(); - if(decompose_ft_outline(m_cur_face->glyph->outline, - flip, - m_path16, - conv_coord_none)) - { - rect_d bnd = m_path16.bounding_rect(); - m_data_size = m_path16.byte_size(); - m_data_type = glyph_data_outline; - m_bounds.x1 = int(floor(bnd.x1)); - m_bounds.y1 = int(floor(bnd.y1)); - m_bounds.x2 = int(ceil(bnd.x2)); - m_bounds.y2 = int(ceil(bnd.y2)); - m_advance_x = double(m_cur_face->glyph->advance.x) / 64.0; - m_advance_y = double(m_cur_face->glyph->advance.y) / 64.0; - return true; - } - } - } - return false; - - case glyph_ren_agg_mono: - if(m_last_error == 0) - { - m_rasterizer.reset(); - if(m_flag32) - { - m_path32.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - flip, - m_path32, - conv_coord_none); - m_rasterizer.add_path(m_curves32); - } - else - { - m_path16.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - flip, - m_path16, - conv_coord_none); - m_rasterizer.add_path(m_curves16); - } - m_scanlines_bin.prepare(); // Remove all - render_scanlines(m_rasterizer, m_scanline_bin, m_scanlines_bin); - m_bounds.x1 = m_scanlines_bin.min_x(); - m_bounds.y1 = m_scanlines_bin.min_y(); - m_bounds.x2 = m_scanlines_bin.max_x() + 1; - m_bounds.y2 = m_scanlines_bin.max_y() + 1; - m_data_size = m_scanlines_bin.byte_size(); - m_data_type = glyph_data_mono; - m_advance_x = double(m_cur_face->glyph->advance.x) / 64.0; - m_advance_y = double(m_cur_face->glyph->advance.y) / 64.0; - return true; - } - return false; - - - case glyph_ren_agg_gray8: - if(m_last_error == 0) - { - m_rasterizer.reset(); - if(m_flag32) - { - m_path32.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - flip, - m_path32, - conv_coord_none); - m_rasterizer.add_path(m_curves32); - } - else - { - m_path16.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - flip, - m_path16, - conv_coord_none); - m_rasterizer.add_path(m_curves16); - } - m_scanlines_aa.prepare(); // Remove all - render_scanlines(m_rasterizer, m_scanline_aa, m_scanlines_aa); - m_bounds.x1 = m_scanlines_aa.min_x(); - m_bounds.y1 = m_scanlines_aa.min_y(); - m_bounds.x2 = m_scanlines_aa.max_x() + 1; - m_bounds.y2 = m_scanlines_aa.max_y() + 1; - m_data_size = m_scanlines_aa.byte_size(); - m_data_type = glyph_data_gray8; - m_advance_x = double(m_cur_face->glyph->advance.x) / 64.0; - m_advance_y = double(m_cur_face->glyph->advance.y) / 64.0; - return true; - } - return false; - } - } - return false; - } - - - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::write_glyph_to(int8u* data) const - { - if(data && m_data_size) - { - switch(m_data_type) - { - case glyph_data_mono: m_scanlines_bin.serialize(data); break; - case glyph_data_gray8: m_scanlines_aa.serialize(data); break; - case glyph_data_outline: - if(m_flag32) - { - m_path32.serialize(data); - } - else - { - m_path16.serialize(data); - } - break; - case glyph_data_invalid: break; - } - } - } - - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::add_kerning(unsigned first, unsigned second, - double* x, double* y) - { - if(m_cur_face && first && second && FT_HAS_KERNING(m_cur_face)) - { - FT_Vector delta; - FT_Get_Kerning(m_cur_face, first, second, - FT_KERNING_DEFAULT, &delta); - FT_Vector_Transform(&delta, &m_matrix); - *x += double(delta.x) / 64.0; - *y += double(delta.y) / 64.0; - return true; - } - return false; - } - - - -} - - diff --git a/src/servers/app/drawing/Painter/font_support/agg_font_freetype.h b/src/servers/app/drawing/Painter/font_support/agg_font_freetype.h deleted file mode 100644 index 95c2e395fa..0000000000 --- a/src/servers/app/drawing/Painter/font_support/agg_font_freetype.h +++ /dev/null @@ -1,189 +0,0 @@ -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.2 -// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://www.antigrain.com -//---------------------------------------------------------------------------- -// -// See implementation agg_font_freetype.cpp -// -//---------------------------------------------------------------------------- - -#ifndef AGG_FONT_FREETYPE_INCLUDED -#define AGG_FONT_FREETYPE_INCLUDED - -#include -#include FT_FREETYPE_H - - -#include "agg_scanline_storage_aa.h" -#include "agg_scanline_storage_bin.h" -#include "agg_scanline_u.h" -#include "agg_scanline_bin.h" -#include "agg_path_storage_integer.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_conv_curve.h" -#include "agg_trans_affine.h" -#include "agg_font_cache_manager.h" - -class ServerFont; - -namespace agg -{ - - - //-----------------------------------------------font_engine_freetype_base - class font_engine_freetype_base - { - public: - //-------------------------------------------------------------------- - typedef serialized_scanlines_adaptor_aa gray8_adaptor_type; - typedef serialized_scanlines_adaptor_bin mono_adaptor_type; - typedef scanline_storage_aa8 scanlines_aa_type; - typedef scanline_storage_bin scanlines_bin_type; - - //-------------------------------------------------------------------- - ~font_engine_freetype_base(); - font_engine_freetype_base(bool flag32, FT_Library library, unsigned max_faces = 32); - - // Set font parameters - //-------------------------------------------------------------------- - void resolution(unsigned dpi); - bool load_font(const ServerFont &font, glyph_rendering ren_type, - double size); - bool attach(const char* file_name); - bool char_map(FT_Encoding map); - bool size(double size); - void transform(const trans_affine& mtx); - void transform(double xx, double xy, double yx, double yy); - void hinting(bool h); - void flip_y(bool f); - - // Set Gamma - //-------------------------------------------------------------------- - template void gamma(const GammaF& f) - { - m_rasterizer.gamma(f); - } - - // Accessors - //-------------------------------------------------------------------- - int last_error() const { return m_last_error; } - unsigned resolution() const { return m_resolution; } - unsigned cur_id() const { return m_cur_id; } - unsigned num_faces() const; - FT_Encoding char_map() const { return m_char_map; } - double size() const { return double(m_size) / 64.0; } - bool hinting() const { return m_hinting; } - bool flip_y() const { return m_flip_y; } - - - // Interface mandatory to implement for font_cache_manager - //-------------------------------------------------------------------- - const char* font_signature() const { return m_signature; } - int change_stamp() const { return m_change_stamp; } - - bool prepare_glyph(unsigned glyph_code); - unsigned glyph_index() const { return m_glyph_index; } - unsigned data_size() const { return m_data_size; } - glyph_data_type data_type() const { return m_data_type; } - const rect_i& bounds() const { return m_bounds; } - double advance_x() const { return m_advance_x; } - double advance_y() const { return m_advance_y; } - void write_glyph_to(int8u* data) const; - bool add_kerning(unsigned first, unsigned second, - double* x, double* y); - - private: - font_engine_freetype_base(const font_engine_freetype_base&); - const font_engine_freetype_base& operator = (const font_engine_freetype_base&); - - void update_char_size(); - void update_signature(); - void update_transform(); - - bool m_flag32; - int m_change_stamp; - int m_last_error; - unsigned m_cur_id; - unsigned m_face_index; - FT_Encoding m_char_map; - char* m_signature; - unsigned m_size; - FT_Matrix m_matrix; - bool m_hinting; - bool m_flip_y; - bool m_library_initialized; - FT_Library m_library; // handle to library - FT_Face m_cur_face; // handle to the current face object - int m_resolution; - glyph_rendering m_glyph_rendering; - unsigned m_glyph_index; - unsigned m_data_size; - glyph_data_type m_data_type; - rect_i m_bounds; - double m_advance_x; - double m_advance_y; - - path_storage_integer m_path16; - path_storage_integer m_path32; - conv_curve > m_curves16; - conv_curve > m_curves32; - scanline_u8 m_scanline_aa; - scanline_bin m_scanline_bin; - scanlines_aa_type m_scanlines_aa; - scanlines_bin_type m_scanlines_bin; - rasterizer_scanline_aa<> m_rasterizer; - }; - - - - - //------------------------------------------------font_engine_freetype_int16 - // This class uses values of type int16 (10.6 format) for the vector cache. - // The vector cache is compact, but when rendering glyphs of height - // more that 200 there integer overflow can occur. - // - class font_engine_freetype_int16 : public font_engine_freetype_base - { - public: - typedef serialized_integer_path_adaptor path_adaptor_type; - typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type; - typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type; - typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type; - typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type; - - font_engine_freetype_int16(FT_Library library, unsigned max_faces = 32) : - font_engine_freetype_base(false, library, max_faces) {} - }; - - //------------------------------------------------font_engine_freetype_int32 - // This class uses values of type int32 (26.6 format) for the vector cache. - // The vector cache is twice larger than in font_engine_freetype_int16, - // but it allows you to render glyphs of very large sizes. - // - class font_engine_freetype_int32 : public font_engine_freetype_base - { - public: - typedef serialized_integer_path_adaptor path_adaptor_type; - typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type; - typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type; - typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type; - typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type; - - font_engine_freetype_int32(FT_Library library, unsigned max_faces = 32) : - font_engine_freetype_base(true, library, max_faces) {} - }; - - -} - -#endif diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index 4411f41941..c3cab9c4fc 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -63,6 +63,9 @@ SharedLibrary libhaikuappserver.so : CursorSet.cpp DesktopSettings.cpp DrawState.cpp + FontCache.cpp + FontCacheEntry.cpp + FontEngine.cpp FontFamily.cpp FontManager.cpp FontStyle.cpp @@ -83,7 +86,7 @@ SharedLibrary libhaikuappserver.so : # trace.c # libraries - : be libtextencoding.so libfreetype.so + : be libpainter.a libtextencoding.so libfreetype.so libshared.a ; AddResources haiku_app_server : app_server.rdef ; @@ -136,8 +139,7 @@ Server haiku_app_server : # libraries : - z libpng.so libhaikuappserver.so - libpainter.a be + z libpng.so libhaikuappserver.so libpainter.a be libhwinterface.so libhwinterfaceimpl.so libagg.a libfreetype.so libtextencoding.so ;