* 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
This commit is contained in:
Stephan Aßmus
2007-08-02 19:10:38 +00:00
parent 693c7a1eaa
commit 2222864eed
24 changed files with 1841 additions and 1610 deletions
+1 -1
View File
@@ -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;
};
+1
View File
@@ -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,
+4 -4
View File
@@ -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<uint16>(fFamilyID);
link.Attach<uint16>(fStyleID);
link.Attach<float>(fSize);
+155
View File
@@ -0,0 +1,155 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "FontCache.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <Entry.h>
#include <Path.h>
#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;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#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<HashString, FontCacheEntry*> FontMap;
FontMap fFontCacheEntries;
};
#endif // FONT_CACHE_H
+238
View File
@@ -0,0 +1,238 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Maxim Shemanarev <[email protected]>
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// 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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#include "FontCacheEntry.h"
#include <string.h>
#include <agg_array.h>
#include <Autolock.h>
#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++;
}
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Maxim Shemanarev <[email protected]>
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// 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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef FONT_CACHE_ENTRY_H
#define FONT_CACHE_ENTRY_H
#include <Locker.h>
#include <agg_conv_curve.h>
#include <agg_conv_contour.h>
#include <agg_conv_transform.h>
#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<GlyphPathAdapter> CurveConverter;
typedef agg::conv_contour<CurveConverter> ContourConverter;
typedef agg::conv_transform<CurveConverter, Transformable>
TransformedOutline;
typedef agg::conv_transform<ContourConverter, Transformable>
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
+589
View File
@@ -0,0 +1,589 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Maxim Shemanarev <[email protected]>
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// 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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#include "FontEngine.h"
#include <stdio.h>
#include <agg_bitset_iterator.h>
#include <agg_renderer_scanline.h>
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<class PathStorage>
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<class Scanline, class ScanlineStorage>
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<class Scanline, class ScanlineStorage>
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;
}
+142
View File
@@ -0,0 +1,142 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Maxim Shemanarev <[email protected]>
* Stephan Aßmus <[email protected]>
*/
//----------------------------------------------------------------------------
// 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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef FONT_ENGINE_H
#define FONT_ENGINE_H
#include <SupportDefs.h>
#include <ft2build.h>
#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>
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<uint8> 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<int32, 6> 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<int32, 6> PathStorageType;
typedef agg::conv_curve<PathStorageType> CurveConverterType;
PathStorageType fPath;
CurveConverterType fCurves;
agg::scanline_u8 fScanlineAA;
agg::scanline_bin fScanlineBin;
ScanlineStorageAA fScanlineStorageAA;
ScanlineStorageBin fScanlineStorageBin;
};
#endif // FONT_ENGINE_H
+145
View File
@@ -0,0 +1,145 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#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<class GlyphConsumer>
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<class GlyphConsumer>
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
+5 -2
View File
@@ -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
;
@@ -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;
+151 -72
View File
@@ -1,17 +1,19 @@
/*
* Copyright 2001-2006, Haiku.
* Copyright 2001-2007, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* DarkWyrm <[email protected]>
* Jérôme Duval, [email protected]
* Michael Lotz <[email protected]>
* Stephan Aßmus <[email protected]>
*/
#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 <String.h>
#include <UTF8.h>
#include <agg_bounding_rect.h>
#include <stdio.h>
#include <string.h>
@@ -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;
}
+10 -4
View File
@@ -1,11 +1,12 @@
/*
* Copyright 2001-2005, Haiku.
* Copyright 2001-2007, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* DarkWyrm <bpmagic@columbus.rr.com>
* Jérôme Duval, jerome.duval@free.fr
* Axel Dörfler, axeld@pinc-software.de
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef SERVER_FONT_H
#define SERVER_FONT_H
@@ -15,6 +16,7 @@
#include <Rect.h>
#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;
@@ -29,6 +29,7 @@
#include <FindDirectory.h>
#include <graphic_driver.h>
#include <image.h>
#include <String.h>
#include <dirent.h>
#include <new>
+4 -21
View File
@@ -13,8 +13,8 @@
#include <algo.h>
#include <stack.h>
#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 -
-6
View File
@@ -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
;
+11 -26
View File
@@ -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
+2 -2
View File
@@ -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 <Rect.h>
class AGGTextRenderer;
class BBitmap;
class BRegion;
class DrawState;
@@ -289,7 +289,7 @@ mutable agg::conv_curve<agg::path_storage> 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
@@ -6,29 +6,17 @@
#include "AGGTextRenderer.h"
#include "FontManager.h"
#include "ServerFont.h"
#include "utf8_functions.h"
#include <agg_basics.h>
#include <agg_bounding_rect.h>
#include <agg_conv_segmentator.h>
#include <agg_conv_transform.h>
#include <agg_trans_affine.h>
#include <Bitmap.h>
#include <ByteOrder.h>
#include <Entry.h>
#include <Message.h>
#include <UTF8.h>
#include <math.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#define FLIP_Y false
#define SHOW_GLYPH_BOUNDS 0
#if SHOW_GLYPH_BOUNDS
@@ -36,82 +24,41 @@
# include <agg_path_storage.h>
#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<FontCacheEntry::CurveConverter, Transformable>
conv_font_trans_type;
typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable>
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<agg::path_storage> 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_curve_type, agg::trans_affine>
conv_font_trans_type;
conv_font_trans_type transformedOutline(fCurves, transform);
typedef agg::conv_transform<conv_font_contour_type, agg::trans_affine>
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<agg::path_storage> 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;
}
@@ -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_engine_type> font_cache_type;
typedef agg::conv_curve<font_cache_type::path_adaptor_type>
conv_font_curve_type;
typedef agg::conv_contour<conv_font_curve_type> 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;
@@ -1,973 +0,0 @@
//----------------------------------------------------------------------------
// Copyright 2005 Michael Lotz <[email protected]>
//
// 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: [email protected]
// [email protected]
// 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 <stdio.h>
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 <[email protected]>
//
// 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<class PathStorage, class ConvCoord>
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<class Scanline, class ScanlineStorage>
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<class Rasterizer, class Scanline, class ScanlineStorage>
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;
}
}
@@ -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: [email protected]
// [email protected]
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// See implementation agg_font_freetype.cpp
//
//----------------------------------------------------------------------------
#ifndef AGG_FONT_FREETYPE_INCLUDED
#define AGG_FONT_FREETYPE_INCLUDED
#include <ft2build.h>
#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<int8u> 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<class GammaF> 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<int16, 6> m_path16;
path_storage_integer<int32, 6> m_path32;
conv_curve<path_storage_integer<int16, 6> > m_curves16;
conv_curve<path_storage_integer<int32, 6> > 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<int16, 6> 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<int32, 6> 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
+5 -3
View File
@@ -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
;