From 269b1736d373ec2a6536986f9c5460d03a304655 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Mon, 19 Jan 2015 09:58:54 +0300 Subject: [PATCH] support font antialiasing and hinting settings --- src/dlangui/graphics/fonts.d | 40 ++++++++ src/dlangui/graphics/ftfonts.d | 36 +++++-- src/dlangui/platforms/windows/win32fonts.d | 111 +++++++++++++++------ src/dlangui/platforms/windows/winapp.d | 10 +- 4 files changed, 156 insertions(+), 41 deletions(-) diff --git a/src/dlangui/graphics/fonts.d b/src/dlangui/graphics/fonts.d index a1ddf708..ca119969 100644 --- a/src/dlangui/graphics/fonts.d +++ b/src/dlangui/graphics/fonts.d @@ -134,6 +134,12 @@ class Font : RefCountedObject { /// returns true if font object is not yet initialized / loaded abstract @property bool isNull(); + /// return true if antialiasing is enabled, false if not enabled + @property bool antialiased() { + return size >= FontManager.instance.minAnitialiasedFontSize; + } + + private int _fixedFontDetection = -1; /// returns true if font has fixed pitch (all characters have equal width) @@ -415,10 +421,24 @@ struct FontList { } } +/// default min font size for antialiased fonts (e.g. if 16 is set, for 16+ sizes antialiasing will be used, for sizes <=15 - antialiasing will be off) +const int DEF_MIN_ANTIALIASED_FONT_SIZE = 0; // 0 means always use antialiasing + +/// Hinting mode (currently supported for FreeType only) +enum HintingMode : int { + /// based on information from font (using bytecode interpreter) + Normal, + /// force autohinting algorithm even if font contains hint data + AutoHint, + /// disable hinting completely + Disabled +} /// Access points to fonts. class FontManager { protected static __gshared FontManager _instance; + protected static __gshared int _minAnitialiasedFontSize = DEF_MIN_ANTIALIASED_FONT_SIZE; + protected static __gshared HintingMode _hintingMode = HintingMode.Normal; /// sets new font manager singleton instance static @property void instance(FontManager manager) { @@ -443,6 +463,26 @@ class FontManager { /// removes entries not used after last call of checkpoint() or cleanup() abstract void cleanup(); + /// get min font size for antialiased fonts + @property int minAnitialiasedFontSize() { + return _minAnitialiasedFontSize; + } + + /// set new min font size for antialiased fonts + @property void minAnitialiasedFontSize(int size) { + _minAnitialiasedFontSize = size; + } + + /// get current hinting mode + @property HintingMode hintingMode() { + return _hintingMode; + } + + /// set hinting mode + @property void hintingMode(HintingMode mode) { + _hintingMode = mode; + } + ~this() { Log.d("Destroying font manager"); } diff --git a/src/dlangui/graphics/ftfonts.d b/src/dlangui/graphics/ftfonts.d index 715db7a4..e9aed16d 100644 --- a/src/dlangui/graphics/ftfonts.d +++ b/src/dlangui/graphics/ftfonts.d @@ -246,14 +246,14 @@ private class FreeTypeFontFile { //FONT_GUARD int glyph_index = getCharIndex(code, def_char); int flags = FT_LOAD_DEFAULT; - const bool _drawMonochrome = false; + const bool _drawMonochrome = _size < FontManager.instance.minAnitialiasedFontSize; flags |= (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO); if (withImage) flags |= FT_LOAD_RENDER; - //if (_hintingMode == HINTING_MODE_AUTOHINT) - // flags |= FT_LOAD_FORCE_AUTOHINT; - //else if (_hintingMode == HINTING_MODE_DISABLED) - // flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; + if (FontManager.instance.hintingMode == HintingMode.AutoHint) + flags |= FT_LOAD_FORCE_AUTOHINT; + else if (FontManager.instance.hintingMode == HintingMode.Disabled) + flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; int error = FT_Load_Glyph( _face, /* handle to face object */ glyph_index, /* glyph index */ @@ -276,8 +276,30 @@ private class FreeTypeFontFile { 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]; + if (_drawMonochrome) { + // monochrome bitmap + ubyte mask = 0x80; + ubyte * ptr = bitmap.buffer; + ubyte * dst = glyph.glyph.ptr; + for ( int y=0; y>= 1; + if ( !mask && x != w-1) { + mask = 0x80; + row++; + } + } + ptr += bitmap.pitch; + } + + } else { + // antialiased + for (int i = 0; i < sz; i++) + glyph.glyph[i] = bitmap.buffer[i]; + } } version (USE_OPENGL) { glyph.id = nextGlyphId(); diff --git a/src/dlangui/platforms/windows/win32fonts.d b/src/dlangui/platforms/windows/win32fonts.d index 6fb383fe..bbc9886e 100644 --- a/src/dlangui/platforms/windows/win32fonts.d +++ b/src/dlangui/platforms/windows/win32fonts.d @@ -142,12 +142,24 @@ class Win32Font : Font { &identity ); if (res == GDI_ERROR) return null; - int gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, - GGO_GRAY8_BITMAP, //GGO_METRICS - &metrics, - 0, - NULL, - &identity ); + int gs = 0; + // calculate bitmap size + if (antialiased) { + gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, + GGO_GRAY8_BITMAP, + &metrics, + 0, + NULL, + &identity ); + } else { + gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, + GGO_BITMAP, + &metrics, + 0, + NULL, + &identity ); + } + if (gs >= 0x10000 || gs < 0) return null; @@ -167,33 +179,65 @@ class Win32Font : Font { g.glyph = new ubyte[g.blackBoxX * g.blackBoxY]; if (gs>0) { - ubyte[] glyph = new ubyte[gs]; - res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, - GGO_GRAY8_BITMAP, //GGO_METRICS - &metrics, - gs, - glyph.ptr, - &identity ); - if (res==GDI_ERROR) - { - return null; - } - int glyph_row_size = (g.blackBoxX + 3) / 4 * 4; - ubyte * src = glyph.ptr; - ubyte * dst = g.glyph.ptr; - for (int y = 0; y < g.blackBoxY; y++) - { - for (int x = 0; x < g.blackBoxX; x++) - { - ubyte b = src[x]; - if (b>=64) - b = 63; - b = (b<<2) & 0xFC; - dst[x] = b; - } - src += glyph_row_size; - dst += g.blackBoxX; - } + if (antialiased) { + // antialiased glyph + ubyte[] glyph = new ubyte[gs]; + res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, + GGO_GRAY8_BITMAP, //GGO_METRICS + &metrics, + gs, + glyph.ptr, + &identity ); + if (res==GDI_ERROR) + { + return null; + } + int glyph_row_size = (g.blackBoxX + 3) / 4 * 4; + ubyte * src = glyph.ptr; + ubyte * dst = g.glyph.ptr; + for (int y = 0; y < g.blackBoxY; y++) + { + for (int x = 0; x < g.blackBoxX; x++) + { + ubyte b = src[x]; + if (b>=64) + b = 63; + b = (b<<2) & 0xFC; + dst[x] = b; + } + src += glyph_row_size; + dst += g.blackBoxX; + } + } else { + // bitmap glyph + ubyte[] glyph = new ubyte[gs]; + res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, + GGO_BITMAP, //GGO_METRICS + &metrics, + gs, + glyph.ptr, + &identity ); + if (res==GDI_ERROR) + { + return null; + } + int glyph_row_bytes = ((g.blackBoxX + 7) / 8); + int glyph_row_size = (glyph_row_bytes + 3) / 4 * 4; + ubyte * src = glyph.ptr; + ubyte * dst = g.glyph.ptr; + for (int y = 0; y < g.blackBoxY; y++) + { + for (int x = 0; x < g.blackBoxX; x++) + { + int offset = x >> 3; + int shift = 7 - (x & 7); + ubyte b = ((src[offset] >> shift) & 1) ? 255 : 0; + dst[x] = b; + } + src += glyph_row_size; + dst += g.blackBoxX; + } + } } else { @@ -217,6 +261,7 @@ class Win32Font : Font { lf.lfFaceName[def.face.length] = 0; lf.lfHeight = size; //-size; lf.lfItalic = italic; + lf.lfWeight = weight; lf.lfOutPrecision = OUT_OUTLINE_PRECIS; //OUT_TT_ONLY_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; //lf.lfQuality = NONANTIALIASED_QUALITY; //ANTIALIASED_QUALITY; diff --git a/src/dlangui/platforms/windows/winapp.d b/src/dlangui/platforms/windows/winapp.d index fed7942c..158f5fd0 100644 --- a/src/dlangui/platforms/windows/winapp.d +++ b/src/dlangui/platforms/windows/winapp.d @@ -859,7 +859,7 @@ int myWinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int iCmdSho /// testing freetype font manager - static if (false) { + version(USE_FREETYPE) { import dlangui.graphics.ftfonts; import win32.shlobj; FreeTypeFontManager ftfontMan = new FreeTypeFontManager(); @@ -889,6 +889,14 @@ int myWinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int iCmdSho ftfontMan.registerFont(fontsPath ~ "timesbd.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Bold); ftfontMan.registerFont(fontsPath ~ "timesbi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Bold); ftfontMan.registerFont(fontsPath ~ "timesi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Normal); + ftfontMan.registerFont(fontsPath ~ "consola.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Normal); + ftfontMan.registerFont(fontsPath ~ "consolab.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Bold); + ftfontMan.registerFont(fontsPath ~ "consolai.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Normal); + ftfontMan.registerFont(fontsPath ~ "consolaz.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Bold); + ftfontMan.registerFont(fontsPath ~ "verdana.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Normal); + ftfontMan.registerFont(fontsPath ~ "verdanab.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Bold); + ftfontMan.registerFont(fontsPath ~ "verdanai.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Normal); + ftfontMan.registerFont(fontsPath ~ "verdanaz.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Bold); FontManager.instance = ftfontMan; }