From b8f8f8f64bbd24e57c29be56bc1c2de1150f724a Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 4 Mar 2014 12:03:25 +0400 Subject: [PATCH] supported getGlyph() for win32 font --- src/dlangui/graphics/fonts.d | 79 ++++++++++++++++- src/dlangui/platforms/windows/winapp.d | 116 +++++++++++++++++++++++++ winmain.d | 2 + 3 files changed, 194 insertions(+), 3 deletions(-) diff --git a/src/dlangui/graphics/fonts.d b/src/dlangui/graphics/fonts.d index 2205ae33..784139b0 100644 --- a/src/dlangui/graphics/fonts.d +++ b/src/dlangui/graphics/fonts.d @@ -1,7 +1,7 @@ module dlangui.graphics.fonts; import dlangui.core.types; -enum FontFamily : int { +public enum FontFamily : int { SansSerif, Serif, Fantasy, @@ -9,7 +9,78 @@ enum FontFamily : int { MonoSpace } -class Font : RefCountedObject { +public struct Glyph +{ + public ubyte blackBoxX; ///< 0: width of glyph + public ubyte blackBoxY; ///< 1: height of glyph black box + public byte originX; ///< 2: X origin for glyph + public byte originY; ///< 3: Y origin for glyph + public ushort glyphIndex; ///< 4: bytes in glyph array + public ubyte width; ///< 6: full width of glyph + public ubyte lastUsage; + public ubyte[] glyph; ///< 7: glyph data, arbitrary size +} + +public struct GlyphCache +{ + Glyph[] _data; + uint _len; + + // find glyph in cache + public Glyph * find(ushort glyphIndex) { + for (uint i = 0; i < _len; i++) { + Glyph * item = &_data[i]; + if (item.glyphIndex == glyphIndex) { + item.lastUsage = 1; + return item; + } + } + return null; + } + + public Glyph * put(ushort glyphIndex, Glyph * glyph) { + if (_len >= _data.length) { + uint newsize = (_len < 32) ? 32 : _len * 2; + _data.length = newsize; + } + _data[_len++] = *glyph; + Glyph * res = &_data[_len - 1]; + res.lastUsage = 1; + return res; + } + + // clear usage flags for all entries + public void checkpoint() { + for (uint src = 0; src < _len; src++) { + _data[src].lastUsage = 0; + } + } + + // removes entries not used after last call of checkpoint() or cleanup() + public void cleanup() { + uint dst = 0; + for (uint src = 0; src < _len; src++) { + if (_data[src].lastUsage != 0) { + _data[src].lastUsage = 0; + if (src != dst) { + _data[dst++] = _data[src]; + } + } + } + _len = dst; + } + + // removes all entries + public void clear() { + _data = null; + _len = 0; + } + public ~this() { + clear(); + } +} + +public class Font : RefCountedObject { abstract public @property int size(); abstract public @property int height(); abstract public @property int weight(); @@ -18,13 +89,15 @@ class Font : RefCountedObject { abstract public @property string face(); abstract public @property FontFamily family(); abstract public @property bool isNull(); + // measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars. abstract public int measureText(const dchar[] text, ref int[] widths, int maxWidth); + abstract public Glyph * getCharGlyph(dchar ch); public void clear() {} public ~this() { clear(); } } alias FontRef = Ref!Font; -class FontManager { +public class FontManager { static __gshared FontManager _instance; public static @property void instance(FontManager manager) { _instance = manager; diff --git a/src/dlangui/platforms/windows/winapp.d b/src/dlangui/platforms/windows/winapp.d index 9029c811..c5cc49ec 100644 --- a/src/dlangui/platforms/windows/winapp.d +++ b/src/dlangui/platforms/windows/winapp.d @@ -42,6 +42,8 @@ class Win32Font : Font { FontFamily _family; LOGFONTA _logfont; Win32ColorDrawBuf _drawbuf; + GlyphCache _glyphCache; + public this() { } public override void clear() { @@ -58,6 +60,120 @@ class Win32Font : Font { _drawbuf = null; } } + + uint getGlyphIndex(dchar code) + { + if (_drawbuf is null) + return 0; + wchar[2] s; + wchar[2] g; + s[0] = cast(wchar)code; + s[1] = 0; + g[0] = 0; + GCP_RESULTSW gcp; + gcp.lStructSize = GCP_RESULTSW.sizeof; + gcp.lpOutString = null; + gcp.lpOrder = null; + gcp.lpDx = null; + gcp.lpCaretPos = null; + gcp.lpClass = null; + gcp.lpGlyphs = g.ptr; + gcp.nGlyphs = 2; + gcp.nMaxFit = 2; + + DWORD res = GetCharacterPlacementW( + _drawbuf.dc, s.ptr, 1, + 1000, + &gcp, + 0 + ); + if (!res) + return 0; + return g[0]; + } + + public override Glyph * getCharGlyph(dchar ch) { + uint glyphIndex = getGlyphIndex(ch); + if (!glyphIndex) + return null; + if (glyphIndex >= 0xFFFF) + return null; + Glyph * found = _glyphCache.find(cast(ushort)glyphIndex); + if (found !is null) + return found; + GLYPHMETRICS metrics; + + MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} }; + uint res; + res = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, + GGO_METRICS, + &metrics, + 0, + null, + &identity ); + if (res==GDI_ERROR) + return null; + int gs = GetGlyphOutlineW( _drawbuf.dc, cast(wchar)ch, + GGO_GRAY8_BITMAP, //GGO_METRICS + &metrics, + 0, + NULL, + &identity ); + if (gs >= 0x10000 || gs < 0) + return null; + + Glyph g; + g.blackBoxX = cast(ubyte)metrics.gmBlackBoxX; + 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.glyphIndex = cast(ushort)glyphIndex; + + if (g.blackBoxX>0 && g.blackBoxY>0) + { + 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; + } + } + else + { + // empty glyph + for (int i = g.blackBoxX * g.blackBoxY - 1; i >= 0; i--) + g.glyph[i] = 0; + } + } + // found! + return _glyphCache.put(cast(ushort)glyphIndex, &g); + } + public override int measureText(const dchar[] text, ref int[] widths, int maxWidth) { if (_hfont is null || _drawbuf is null || text.length == 0) return 0; diff --git a/winmain.d b/winmain.d index b6b8b2a0..923930d3 100644 --- a/winmain.d +++ b/winmain.d @@ -23,5 +23,7 @@ extern (C) int UIAppMain() { assert(charsMeasured > 0); int w = widths[charsMeasured - 1]; Log.d("Measured string: ", charsMeasured, " chars, width=", w); + Glyph * g = font.getCharGlyph('A'); + Log.d("Char A glyph: ", g.blackBoxX, "x", g.blackBoxY); return Platform.instance().enterMessageLoop(); }