improve freetype fonts support - #461

This commit is contained in:
Vadim Lopatin 2017-09-27 14:52:29 +03:00
parent 51cf849a1f
commit e990cb10cb
4 changed files with 104 additions and 29 deletions

View File

@ -348,11 +348,17 @@ enum SubpixelRenderingMode : ubyte {
align(1)
struct Glyph
{
static if (ENABLE_OPENGL) {
/// 0: unique id of glyph (for drawing in hardware accelerated scenes)
uint id;
}
/// 0: width of glyph black box
ushort blackBoxX;
@property ushort correctedBlackBoxX() { return subpixelMode ? blackBoxX / 3 : blackBoxX; }
/// 2: height of glyph black box
ubyte blackBoxY;
/// 3: X origin for glyph
@ -361,17 +367,15 @@ struct Glyph
byte originY;
/// 5: full width of glyph
ubyte width;
/// 6: subpixel rendering mode - if !=SubpixelRenderingMode.None, glyph data contains 3 bytes per pixel instead of 1
ubyte widthPixels;
/// 6: full width of glyph scaled * 64
ushort widthScaled;
/// 8: subpixel rendering mode - if !=SubpixelRenderingMode.None, glyph data contains 3 bytes per pixel instead of 1
SubpixelRenderingMode subpixelMode;
/// 7: usage flag, to handle cleanup of unused glyphs
/// 9: usage flag, to handle cleanup of unused glyphs
ubyte lastUsage;
static if (ENABLE_OPENGL) {
/// 8: unique id of glyph (for drawing in hardware accelerated scenes)
uint id;
}
///< 12: glyph data, arbitrary size (blackBoxX * blackBoxY)
///< 10: glyph data, arbitrary size (blackBoxX * blackBoxY)
ubyte[] glyph;
}

View File

@ -186,7 +186,22 @@ class Font : RefCountedObject {
/// returns character width
int charWidth(dchar ch) {
Glyph * g = getCharGlyph(ch);
return !g ? 0 : g.width;
return !g ? 0 : g.widthPixels;
}
protected bool _allowKerning;
/// override to enable kerning support
@property bool allowKerning() {
return false;
}
/// override to enable kerning support
@property void allowKerning(bool allow) {
_allowKerning = allow;
}
/// override to implement kerning offset calculation
int getKerningOffset(dchar prevChar, dchar currentChar) {
return 0;
}
/*******************************************************************************************
@ -211,6 +226,7 @@ class Font : RefCountedObject {
uint len = cast(uint)text.length;
if (widths.length < len)
widths.length = len + 1;
bool useKerning = allowKerning;
int x = 0;
int charsMeasured = 0;
int * pwidths = widths.ptr;
@ -218,6 +234,7 @@ class Font : RefCountedObject {
tabOffset = tabOffset % tabWidth;
if (tabOffset < 0)
tabOffset += tabWidth;
dchar prevChar = 0;
foreach(int i; 0 .. len) {
//auto measureStart = std.datetime.Clock.currAppTick;
dchar ch = pstr[i];
@ -229,9 +246,11 @@ class Font : RefCountedObject {
pwidths[i] = tabPosition;
charsMeasured = i + 1;
x = tabPosition;
prevChar = 0;
continue;
} else if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) {
pwidths[i] = x;
prevChar = 0;
continue; // skip '&' in hot key when measuring
}
Glyph * glyph = getCharGlyph(pstr[i], true); // TODO: what is better
@ -242,17 +261,23 @@ class Font : RefCountedObject {
if (glyph is null) {
// if no glyph, use previous width - treat as zero width
pwidths[i] = x;
prevChar = 0;
continue;
}
int w = x + glyph.width; // using advance
int w2 = x + glyph.originX + glyph.correctedBlackBoxX; // using black box
if (w < w2) // choose bigger value
w = w2;
int kerningDelta = useKerning && prevChar ? getKerningOffset(ch, prevChar) : 0;
int width = ((glyph.widthScaled + kerningDelta + 63) >> 6);
if (width < glyph.originX + glyph.correctedBlackBoxX)
width = glyph.originX + glyph.correctedBlackBoxX;
int w = x + width; // using advance
//int w2 = x + glyph.originX + glyph.correctedBlackBoxX; // using black box
//if (w < w2) // choose bigger value
// w = w2;
pwidths[i] = w;
x += glyph.width;
x += width;
charsMeasured = i + 1;
if (x > maxWidth)
break;
prevChar = ch;
}
return charsMeasured;
}

View File

@ -118,6 +118,8 @@ class FreeTypeFontFile {
private int _weight;
private bool _italic;
private bool _allowKerning = true;
/// filename
@property string filename() { return _filename; }
// properties as detected after opening of file
@ -186,7 +188,7 @@ class FreeTypeFontFile {
clear();
return false;
}
_height = cast(int)(_face.size.metrics.height >> 6);
_height = cast(int)((_face.size.metrics.height + 63) >> 6);
_size = size;
_baseline = _height + cast(int)(_face.size.metrics.descender >> 6);
_weight = _face.style_flags & FT_STYLE_FLAG_BOLD ? FontWeight.Bold : FontWeight.Normal;
@ -218,6 +220,11 @@ class FreeTypeFontFile {
return ch_glyph_index;
}
/// allow kerning
@property bool allowKerning() {
return FT_HAS_KERNING( _face );
}
/// retrieve glyph information, filling glyph struct; returns false if glyph not found
bool getGlyphInfo(dchar code, ref Glyph glyph, dchar def_char, bool withImage = true)
{
@ -240,11 +247,12 @@ class FreeTypeFontFile {
if ( error )
return false;
glyph.lastUsage = 1;
glyph.blackBoxX = cast(ushort)(_slot.metrics.width >> 6);
glyph.blackBoxY = cast(ubyte)(_slot.metrics.height >> 6);
glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6);
glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6);
glyph.width = cast(ubyte)(myabs(cast(int)(_slot.metrics.horiAdvance)) >> 6);
glyph.blackBoxX = cast(ushort)((_slot.metrics.width + 32) >> 6);
glyph.blackBoxY = cast(ubyte)((_slot.metrics.height + 32) >> 6);
glyph.originX = cast(byte)((_slot.metrics.horiBearingX + 32) >> 6);
glyph.originY = cast(byte)((_slot.metrics.horiBearingY + 32) >> 6);
glyph.widthScaled = cast(ushort)(myabs(cast(int)(_slot.metrics.horiAdvance)));
glyph.widthPixels = cast(ubyte)(myabs(cast(int)(_slot.metrics.horiAdvance + 32)) >> 6);
glyph.subpixelMode = subpixel;
//glyph.glyphIndex = cast(ushort)code;
if (withImage) {
@ -301,6 +309,19 @@ class FreeTypeFontFile {
_face = null;
}
int getKerningOffset(FT_UInt prevCharIndex, FT_UInt nextCharIndex) {
const FT_KERNING_DEFAULT = 0;
FT_Vector delta;
int error = FT_Get_Kerning( _face, /* handle to face object */
prevCharIndex, /* left glyph index */
nextCharIndex, /* right glyph index */
FT_KERNING_DEFAULT, /* kerning mode */
&delta); /* target vector */
const RSHIFT = 0;
if ( !error )
return (delta.x) >> RSHIFT;
return 0;
}
}
/**
@ -318,6 +339,7 @@ class FreeTypeFont : Font {
_fontItem = item;
_size = size;
_height = size;
_allowKerning = true;
debug ++_instanceCount;
debug(resalloc) Log.d("Created font, count=", _instanceCount);
}
@ -357,6 +379,28 @@ class FreeTypeFont : Font {
return false;
}
/// override to allow kerning
override @property bool allowKerning() {
return _allowKerning;
}
/// override to implement kerning offset calculation
override int getKerningOffset(dchar prevChar, dchar currentChar) {
if (!_allowKerning || !prevChar || !currentChar)
return 0;
FT_UInt index1;
FreeTypeFontFile file1;
if (!findGlyph(prevChar, 0, index1, file1))
return 0;
FT_UInt index2;
FreeTypeFontFile file2;
if (!findGlyph(currentChar, 0, index2, file2))
return 0;
if (file1 !is file2)
return 0;
return file1.getKerningOffset(index1, index2);
}
override Glyph * getCharGlyph(dchar ch, bool withImage = true) {
if (ch > 0xFFFF) // do not support unicode chars above 0xFFFF - due to cache limitations
return null;
@ -429,7 +473,7 @@ private derelict.util.exception.ShouldThrow missingSymFunc( string symName ) {
static import derelict.util.exception;
foreach(s; ["FT_New_Face", "FT_Attach_File", "FT_Set_Pixel_Sizes",
"FT_Get_Char_Index", "FT_Load_Glyph", "FT_Done_Face",
"FT_Init_FreeType", "FT_Done_FreeType"]) {
"FT_Init_FreeType", "FT_Done_FreeType", "FT_Get_Kerning"]) {
if (symName.equal(s)) // Symbol is used
return derelict.util.exception.ShouldThrow.Yes;
}
@ -532,12 +576,12 @@ class FreeTypeFontManager : FontManager {
Log.v("DerelictFT: Loading FreeType library");
if (!DerelictFT) {
Log.w("DerelictFT is null. Compiler bug? Applying workaround to fix it.");
version(Android) {
//DerelictFT = new DerelictFTLoader("libft2.so");
DerelictFT = new DerelictFTLoader;
} else {
DerelictFT = new DerelictFTLoader;
}
version(Android) {
//DerelictFT = new DerelictFTLoader("libft2.so");
DerelictFT = new DerelictFTLoader;
} else {
DerelictFT = new DerelictFTLoader;
}
}
DerelictFT.missingSymbolCallback = &missingSymFunc;
Log.v("DerelictFT: Missing symbols callback is registered");

View File

@ -344,7 +344,8 @@ class Win32Font : Font {
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
g.originX = cast(byte)((metrics.gmptGlyphOrigin.x + 0) / 3);
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
g.width = cast(ubyte)((metrics.gmCellIncX + 2) / 3);
g.widthPixels = cast(ubyte)((metrics.gmCellIncX + 2) / 3);
g.widthScaled = g.widthPixels << 6;
g.subpixelMode = FontManager.subpixelRenderingMode;
//Log.d(" *3 : blackBoxX=", metrics.gmBlackBoxX, " \tblackBoxY=", metrics.gmBlackBoxY, " \torigin.x=", metrics.gmptGlyphOrigin.x, " \torigin.y=", metrics.gmptGlyphOrigin.y, " \tgmCellIncX=", metrics.gmCellIncX);
//Log.d(" /3 : blackBoxX=", g.blackBoxX, " \tblackBoxY=", g.blackBoxY, " \torigin.x=", g.originX, " \torigin.y=", g.originY, "\tgmCellIncX=", g.width);
@ -353,7 +354,8 @@ class Win32Font : Font {
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
g.originX = cast(byte)metrics.gmptGlyphOrigin.x;
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
g.width = cast(ubyte)metrics.gmCellIncX;
g.widthPixels = cast(ubyte)metrics.gmCellIncX;
g.widthScaled = g.widthPixels << 6;
}
if (g.blackBoxX > 0 && g.blackBoxY > 0) {