* 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:
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 -
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user