mirror of https://github.com/buggins/dlangui.git
improve freetype fonts support - #461
This commit is contained in:
parent
51cf849a1f
commit
e990cb10cb
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue