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)
|
align(1)
|
||||||
struct Glyph
|
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
|
/// 0: width of glyph black box
|
||||||
ushort blackBoxX;
|
ushort blackBoxX;
|
||||||
|
|
||||||
@property ushort correctedBlackBoxX() { return subpixelMode ? blackBoxX / 3 : blackBoxX; }
|
@property ushort correctedBlackBoxX() { return subpixelMode ? blackBoxX / 3 : blackBoxX; }
|
||||||
|
|
||||||
|
|
||||||
/// 2: height of glyph black box
|
/// 2: height of glyph black box
|
||||||
ubyte blackBoxY;
|
ubyte blackBoxY;
|
||||||
/// 3: X origin for glyph
|
/// 3: X origin for glyph
|
||||||
|
@ -361,17 +367,15 @@ struct Glyph
|
||||||
byte originY;
|
byte originY;
|
||||||
|
|
||||||
/// 5: full width of glyph
|
/// 5: full width of glyph
|
||||||
ubyte width;
|
ubyte widthPixels;
|
||||||
/// 6: subpixel rendering mode - if !=SubpixelRenderingMode.None, glyph data contains 3 bytes per pixel instead of 1
|
/// 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;
|
SubpixelRenderingMode subpixelMode;
|
||||||
/// 7: usage flag, to handle cleanup of unused glyphs
|
/// 9: usage flag, to handle cleanup of unused glyphs
|
||||||
ubyte lastUsage;
|
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;
|
ubyte[] glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,22 @@ class Font : RefCountedObject {
|
||||||
/// returns character width
|
/// returns character width
|
||||||
int charWidth(dchar ch) {
|
int charWidth(dchar ch) {
|
||||||
Glyph * g = getCharGlyph(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;
|
uint len = cast(uint)text.length;
|
||||||
if (widths.length < len)
|
if (widths.length < len)
|
||||||
widths.length = len + 1;
|
widths.length = len + 1;
|
||||||
|
bool useKerning = allowKerning;
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int charsMeasured = 0;
|
int charsMeasured = 0;
|
||||||
int * pwidths = widths.ptr;
|
int * pwidths = widths.ptr;
|
||||||
|
@ -218,6 +234,7 @@ class Font : RefCountedObject {
|
||||||
tabOffset = tabOffset % tabWidth;
|
tabOffset = tabOffset % tabWidth;
|
||||||
if (tabOffset < 0)
|
if (tabOffset < 0)
|
||||||
tabOffset += tabWidth;
|
tabOffset += tabWidth;
|
||||||
|
dchar prevChar = 0;
|
||||||
foreach(int i; 0 .. len) {
|
foreach(int i; 0 .. len) {
|
||||||
//auto measureStart = std.datetime.Clock.currAppTick;
|
//auto measureStart = std.datetime.Clock.currAppTick;
|
||||||
dchar ch = pstr[i];
|
dchar ch = pstr[i];
|
||||||
|
@ -229,9 +246,11 @@ class Font : RefCountedObject {
|
||||||
pwidths[i] = tabPosition;
|
pwidths[i] = tabPosition;
|
||||||
charsMeasured = i + 1;
|
charsMeasured = i + 1;
|
||||||
x = tabPosition;
|
x = tabPosition;
|
||||||
|
prevChar = 0;
|
||||||
continue;
|
continue;
|
||||||
} else if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) {
|
} else if (ch == '&' && (textFlags & (TextFlag.UnderlineHotKeys | TextFlag.HotKeys | TextFlag.UnderlineHotKeysWhenAltPressed))) {
|
||||||
pwidths[i] = x;
|
pwidths[i] = x;
|
||||||
|
prevChar = 0;
|
||||||
continue; // skip '&' in hot key when measuring
|
continue; // skip '&' in hot key when measuring
|
||||||
}
|
}
|
||||||
Glyph * glyph = getCharGlyph(pstr[i], true); // TODO: what is better
|
Glyph * glyph = getCharGlyph(pstr[i], true); // TODO: what is better
|
||||||
|
@ -242,17 +261,23 @@ class Font : RefCountedObject {
|
||||||
if (glyph is null) {
|
if (glyph is null) {
|
||||||
// if no glyph, use previous width - treat as zero width
|
// if no glyph, use previous width - treat as zero width
|
||||||
pwidths[i] = x;
|
pwidths[i] = x;
|
||||||
|
prevChar = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int w = x + glyph.width; // using advance
|
int kerningDelta = useKerning && prevChar ? getKerningOffset(ch, prevChar) : 0;
|
||||||
int w2 = x + glyph.originX + glyph.correctedBlackBoxX; // using black box
|
int width = ((glyph.widthScaled + kerningDelta + 63) >> 6);
|
||||||
if (w < w2) // choose bigger value
|
if (width < glyph.originX + glyph.correctedBlackBoxX)
|
||||||
w = w2;
|
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;
|
pwidths[i] = w;
|
||||||
x += glyph.width;
|
x += width;
|
||||||
charsMeasured = i + 1;
|
charsMeasured = i + 1;
|
||||||
if (x > maxWidth)
|
if (x > maxWidth)
|
||||||
break;
|
break;
|
||||||
|
prevChar = ch;
|
||||||
}
|
}
|
||||||
return charsMeasured;
|
return charsMeasured;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,8 @@ class FreeTypeFontFile {
|
||||||
private int _weight;
|
private int _weight;
|
||||||
private bool _italic;
|
private bool _italic;
|
||||||
|
|
||||||
|
private bool _allowKerning = true;
|
||||||
|
|
||||||
/// filename
|
/// filename
|
||||||
@property string filename() { return _filename; }
|
@property string filename() { return _filename; }
|
||||||
// properties as detected after opening of file
|
// properties as detected after opening of file
|
||||||
|
@ -186,7 +188,7 @@ class FreeTypeFontFile {
|
||||||
clear();
|
clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_height = cast(int)(_face.size.metrics.height >> 6);
|
_height = cast(int)((_face.size.metrics.height + 63) >> 6);
|
||||||
_size = size;
|
_size = size;
|
||||||
_baseline = _height + cast(int)(_face.size.metrics.descender >> 6);
|
_baseline = _height + cast(int)(_face.size.metrics.descender >> 6);
|
||||||
_weight = _face.style_flags & FT_STYLE_FLAG_BOLD ? FontWeight.Bold : FontWeight.Normal;
|
_weight = _face.style_flags & FT_STYLE_FLAG_BOLD ? FontWeight.Bold : FontWeight.Normal;
|
||||||
|
@ -218,6 +220,11 @@ class FreeTypeFontFile {
|
||||||
return ch_glyph_index;
|
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
|
/// 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)
|
bool getGlyphInfo(dchar code, ref Glyph glyph, dchar def_char, bool withImage = true)
|
||||||
{
|
{
|
||||||
|
@ -240,11 +247,12 @@ class FreeTypeFontFile {
|
||||||
if ( error )
|
if ( error )
|
||||||
return false;
|
return false;
|
||||||
glyph.lastUsage = 1;
|
glyph.lastUsage = 1;
|
||||||
glyph.blackBoxX = cast(ushort)(_slot.metrics.width >> 6);
|
glyph.blackBoxX = cast(ushort)((_slot.metrics.width + 32) >> 6);
|
||||||
glyph.blackBoxY = cast(ubyte)(_slot.metrics.height >> 6);
|
glyph.blackBoxY = cast(ubyte)((_slot.metrics.height + 32) >> 6);
|
||||||
glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6);
|
glyph.originX = cast(byte)((_slot.metrics.horiBearingX + 32) >> 6);
|
||||||
glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6);
|
glyph.originY = cast(byte)((_slot.metrics.horiBearingY + 32) >> 6);
|
||||||
glyph.width = cast(ubyte)(myabs(cast(int)(_slot.metrics.horiAdvance)) >> 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.subpixelMode = subpixel;
|
||||||
//glyph.glyphIndex = cast(ushort)code;
|
//glyph.glyphIndex = cast(ushort)code;
|
||||||
if (withImage) {
|
if (withImage) {
|
||||||
|
@ -301,6 +309,19 @@ class FreeTypeFontFile {
|
||||||
_face = null;
|
_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;
|
_fontItem = item;
|
||||||
_size = size;
|
_size = size;
|
||||||
_height = size;
|
_height = size;
|
||||||
|
_allowKerning = true;
|
||||||
debug ++_instanceCount;
|
debug ++_instanceCount;
|
||||||
debug(resalloc) Log.d("Created font, count=", _instanceCount);
|
debug(resalloc) Log.d("Created font, count=", _instanceCount);
|
||||||
}
|
}
|
||||||
|
@ -357,6 +379,28 @@ class FreeTypeFont : Font {
|
||||||
return false;
|
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) {
|
override Glyph * getCharGlyph(dchar ch, bool withImage = true) {
|
||||||
if (ch > 0xFFFF) // do not support unicode chars above 0xFFFF - due to cache limitations
|
if (ch > 0xFFFF) // do not support unicode chars above 0xFFFF - due to cache limitations
|
||||||
return null;
|
return null;
|
||||||
|
@ -429,7 +473,7 @@ private derelict.util.exception.ShouldThrow missingSymFunc( string symName ) {
|
||||||
static import derelict.util.exception;
|
static import derelict.util.exception;
|
||||||
foreach(s; ["FT_New_Face", "FT_Attach_File", "FT_Set_Pixel_Sizes",
|
foreach(s; ["FT_New_Face", "FT_Attach_File", "FT_Set_Pixel_Sizes",
|
||||||
"FT_Get_Char_Index", "FT_Load_Glyph", "FT_Done_Face",
|
"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
|
if (symName.equal(s)) // Symbol is used
|
||||||
return derelict.util.exception.ShouldThrow.Yes;
|
return derelict.util.exception.ShouldThrow.Yes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,7 +344,8 @@ class Win32Font : Font {
|
||||||
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
|
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
|
||||||
g.originX = cast(byte)((metrics.gmptGlyphOrigin.x + 0) / 3);
|
g.originX = cast(byte)((metrics.gmptGlyphOrigin.x + 0) / 3);
|
||||||
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
|
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;
|
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=", 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);
|
//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.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
|
||||||
g.originX = cast(byte)metrics.gmptGlyphOrigin.x;
|
g.originX = cast(byte)metrics.gmptGlyphOrigin.x;
|
||||||
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
|
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) {
|
if (g.blackBoxX > 0 && g.blackBoxY > 0) {
|
||||||
|
|
Loading…
Reference in New Issue