freetype support

This commit is contained in:
Vadim Lopatin 2014-03-12 16:47:12 +04:00
parent 993b367913
commit aab292ecad
3 changed files with 137 additions and 28 deletions

View File

@ -129,7 +129,7 @@ class Font : RefCountedObject {
} }
// draw text string to buffer // draw text string to buffer
abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color); abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color);
abstract Glyph * getCharGlyph(dchar ch); abstract Glyph * getCharGlyph(dchar ch, bool withImage = true);
// clear usage flags for all entries // clear usage flags for all entries
abstract void checkpoint(); abstract void checkpoint();
@ -154,7 +154,7 @@ struct FontList {
ref FontRef get(int index) { ref FontRef get(int index) {
return _list[index]; return _list[index];
} }
// returns index of found item, -1 if not found // find by a set of parameters - returns index of found item, -1 if not found
int find(int size, int weight, bool italic, FontFamily family, string face) { int find(int size, int weight, bool italic, FontFamily family, string face) {
for (int i = 0; i < _len; i++) { for (int i = 0; i < _len; i++) {
Font item = _list[i].get; Font item = _list[i].get;
@ -170,6 +170,16 @@ struct FontList {
} }
return -1; return -1;
} }
// find by size only - returns index of found item, -1 if not found
int find(int size) {
for (int i = 0; i < _len; i++) {
Font item = _list[i].get;
if (item.size != size)
continue;
return i;
}
return -1;
}
ref FontRef add(Font item) { ref FontRef add(Font item) {
Log.d("FontList.add() enter"); Log.d("FontList.add() enter");
if (_len >= _list.length) { if (_len >= _list.length) {

View File

@ -39,6 +39,7 @@ private struct FontDef {
} }
private class FontFileItem { private class FontFileItem {
private FontList _activeFonts;
private FT_Library _library; private FT_Library _library;
private FontDef _def; private FontDef _def;
string[] _filenames; string[] _filenames;
@ -56,6 +57,20 @@ private class FontFileItem {
_library = library; _library = library;
_def = def; _def = def;
} }
private FontRef _nullFontRef;
ref FontRef get(int size) {
int index = _activeFonts.find(size);
if (index >= 0)
return _activeFonts.get(index);
FreeTypeFont font = new FreeTypeFont(this, size);
if (!font.create()) {
destroy(font);
return _nullFontRef;
}
return _activeFonts.add(font);
}
} }
private class FreeTypeFontFile { private class FreeTypeFontFile {
@ -206,16 +221,16 @@ private class FreeTypeFontFile {
return ch_glyph_index; return ch_glyph_index;
} }
int myabs(int n) { return n >= 0 ? n : -n; }
/// retrieve glyph information, filling glyph struct; returns false if glyph not found /// retrieve glyph information, filling glyph struct; returns false if glyph not found
bool getGlyphInfo(dchar code, Glyph glyph, dchar def_char) bool getGlyphInfo(dchar code, Glyph glyph, dchar def_char, bool withImage = true)
{ {
//FONT_GUARD //FONT_GUARD
int glyph_index = getCharIndex(code, def_char); int glyph_index = getCharIndex(code, def_char);
int flags = FT_LOAD_DEFAULT; int flags = FT_LOAD_DEFAULT;
const bool _drawMonochrome = false; const bool _drawMonochrome = false;
flags |= (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO); flags |= (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
if (withImage)
flags |= FT_LOAD_RENDER;
//if (_hintingMode == HINTING_MODE_AUTOHINT) //if (_hintingMode == HINTING_MODE_AUTOHINT)
// flags |= FT_LOAD_FORCE_AUTOHINT; // flags |= FT_LOAD_FORCE_AUTOHINT;
//else if (_hintingMode == HINTING_MODE_DISABLED) //else if (_hintingMode == HINTING_MODE_DISABLED)
@ -226,11 +241,25 @@ private class FreeTypeFontFile {
flags ); /* load flags, see below */ flags ); /* load flags, see below */
if ( error ) if ( error )
return false; return false;
glyph.lastUsage = 1;
glyph.blackBoxX = cast(ubyte)(_slot.metrics.width >> 6); glyph.blackBoxX = cast(ubyte)(_slot.metrics.width >> 6);
glyph.blackBoxY = cast(ubyte)(_slot.metrics.height >> 6); glyph.blackBoxY = cast(ubyte)(_slot.metrics.height >> 6);
glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6); glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6);
glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6); glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6);
glyph.width = cast(ubyte)(myabs(_slot.metrics.horiAdvance) >> 6); glyph.width = cast(ubyte)(myabs(_slot.metrics.horiAdvance) >> 6);
if (withImage) {
FT_Bitmap* bitmap = &_slot.bitmap;
ubyte w = cast(ubyte)(bitmap.width);
ubyte h = cast(ubyte)(bitmap.rows);
glyph.blackBoxX = w;
glyph.blackBoxY = h;
int sz = w * cast(int)h;
if (sz > 0) {
glyph.glyph = new ubyte[sz];
for (int i = 0; i < sz; i++)
glyph.glyph[i] = bitmap.buffer[i];
}
}
return true; return true;
} }
@ -287,44 +316,88 @@ class FreeTypeFont : Font {
return 0; return 0;
} }
override Glyph * getCharGlyph(dchar ch) { /// find glyph index for character
uint glyphIndex = getGlyphIndex(ch); bool findGlyph(dchar code, dchar def_char, ref FT_UInt index, ref FreeTypeFontFile file) {
if (!glyphIndex) foreach(FreeTypeFontFile f; _files) {
index = f.getCharIndex(code, def_char);
if (index != 0) {
file = f;
return true;
}
}
return false;
}
private Glyph tmpGlyphInfo;
override Glyph * getCharGlyph(dchar ch, bool withImage = true) {
if (ch > 0xFFFF) // do not support unicode chars above 0xFFFF - due to cache limitations
return null; return null;
Glyph * found = _glyphCache.find(cast(ushort)ch);
if (found !is null)
return found;
FT_UInt index;
FreeTypeFontFile file;
if (!findGlyph(ch, 0, index, file)) {
if (!findGlyph(ch, '?', index, file))
return null; return null;
} }
if (!file.getGlyphInfo(ch, tmpGlyphInfo, 0, withImage))
return null;
if (withImage)
return _glyphCache.put(cast(ushort)ch, &tmpGlyphInfo);
return &tmpGlyphInfo;
}
// draw text string to buffer // draw text string to buffer
override void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color) { override void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color) {
int[] widths; int[] widths;
int charsMeasured = measureText(text, widths, 3000);
int bl = baseline; int bl = baseline;
for (int i = 0; i < charsMeasured; i++) { int xx = 0;
int xx = (i > 0) ? widths[i - 1] : 0; for (int i = 0; i < text.length; i++) {
Glyph * glyph = getCharGlyph(text[i]); Glyph * glyph = getCharGlyph(text[i], true);
if (glyph is null) if (glyph is null)
continue; continue;
if ( glyph.blackBoxX && glyph.blackBoxY ) { if ( glyph.blackBoxX && glyph.blackBoxY ) {
buf.drawGlyph( x + xx + glyph.originX, int x0 = x + xx + glyph.originX;
y + bl - glyph.originY, int y0 = y + bl - glyph.originY;
if (x0 > buf.width)
break; // outside right bound
Rect rc = Rect(x0, y0, x0 + glyph.blackBoxX, y0 + glyph.blackBoxY);
if (buf.applyClipping(rc))
buf.drawGlyph( x0,
y0,
glyph, glyph,
color); color);
} }
xx += glyph.width;
} }
} }
override int measureText(const dchar[] text, ref int[] widths, int maxWidth) { override int measureText(const dchar[] text, ref int[] widths, int maxWidth) {
if (text.length == 0) if (text.length == 0)
return 0; return 0;
dstring utf32text = toUTF32(text); const dchar * pstr = text.ptr;
const dchar * pstr = utf32text.ptr; uint len = cast(uint)text.length;
uint len = cast(uint)utf32text.length; int x = 0;
bool res = false; int charsMeasured = 0;
if (!res) { for (int i = 0; i < len; i++) {
widths[0] = 0; Glyph * glyph = getCharGlyph(text[i], true); // TODO: what is better
return 0; if (glyph is null) {
// if no glyph, use previous width - treat as zero width
widths[i] = i > 0 ? widths[i-1] : 0;
continue;
} }
return 0; int w = x + glyph.width; // using advance
int w2 = x + glyph.originX + glyph.blackBoxX; // using black box
if (w < w2) // choose bigger value
w = w2;
widths[i] = w;
x += glyph.width;
charsMeasured = i + 1;
if (x > maxWidth)
break;
}
return charsMeasured;
} }
bool create() { bool create() {
@ -373,6 +446,27 @@ class FreeTypeFontManager : FontManager {
return null; return null;
} }
private FontFileItem findBestMatch(int weight, bool italic, FontFamily family, string face) {
FontFileItem best = null;
int bestScore = 0;
foreach(FontFileItem item; _fontFiles) {
int score = 0;
if (face is null || face.equal(item.def.face))
score += 200; // face match
if (family == item.def.family)
score += 100; // family match
if (italic == item.def.italic)
score += 50; // italic match
int weightDiff = myabs(weight - item.def.weight);
score += 30 - weightDiff / 30; // weight match
if (score > bestScore) {
bestScore = score;
best = item;
}
}
return best;
}
private FontList _activeFonts; private FontList _activeFonts;
private static FontRef _nullFontRef; private static FontRef _nullFontRef;
@ -395,7 +489,10 @@ class FreeTypeFontManager : FontManager {
/// get font instance with specified parameters /// get font instance with specified parameters
override ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face) { override ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face) {
FontFileItem f = findBestMatch(weight, italic, family, face);
if (f is null)
return _nullFontRef; return _nullFontRef;
return f.get(size);
} }
/// clear usage flags for all entries /// clear usage flags for all entries
@ -443,3 +540,5 @@ class FreeTypeFontManager : FontManager {
} }
} }
private int myabs(int n) { return n >= 0 ? n : -n; }

View File

@ -100,7 +100,7 @@ class Win32Font : Font {
return g[0]; return g[0];
} }
override Glyph * getCharGlyph(dchar ch) { override Glyph * getCharGlyph(dchar ch, bool withImage = true) {
uint glyphIndex = getGlyphIndex(ch); uint glyphIndex = getGlyphIndex(ch);
if (!glyphIndex) if (!glyphIndex)
return null; return null;