1 module evael.graphics.Font; 2 3 import std.conv : to; 4 import std.string; 5 import std.experimental.logger; 6 7 import derelict.nanovg.nanovg; 8 9 import evael.graphics.GraphicsDevice; 10 import evael.graphics.Texture; 11 import evael.graphics.shaders.Shader; 12 import evael.graphics.Vertex; 13 import evael.system.Asset; 14 15 import evael.utils.Color; 16 import evael.utils.Math; 17 18 import jsonizer; 19 20 /** 21 * Font. Using NanoVG. 22 */ 23 class Font : IAsset 24 { 25 mixin JsonizeMe; 26 27 /// Font size 28 private ushort m_size; 29 30 private int m_fontId; 31 32 @jsonize("path") private string m_fontName; 33 34 private NVGcontext* m_nvg; 35 36 @nogc @safe 37 public this() pure nothrow 38 { 39 40 } 41 42 /** 43 * Font constructor. 44 * Params: 45 * nvgContext : nanovg context 46 * fontId : font id 47 * fontName : font name 48 */ 49 @nogc @safe 50 public this(NVGcontext* nvgContext, in int fontId, in string fontName) pure nothrow 51 { 52 this.m_nvg = nvgContext; 53 this.m_fontId = fontId; 54 this.m_fontName = fontName; 55 } 56 57 @nogc @safe 58 public void dispose() const pure nothrow 59 { 60 61 } 62 63 /** 64 * Renders text. 65 * Params: 66 * text : 67 * position : 68 * color : 69 * fontSize : 70 * shadow : indicates if a shadow should be rendered 71 * alignment 72 */ 73 public void draw()(in wstring text, in auto ref vec2 position, in auto ref Color color, in int fontSize, in bool shadow = true, 74 in int alignment = NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP) 75 { 76 this.draw(text, position.x, position.y, color, fontSize, shadow, alignment); 77 } 78 79 public void draw()(in string text, in auto ref vec2 position, in auto ref Color color, in int fontSize, in bool shadow = true, 80 in int alignment = NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP) 81 { 82 this.draw(text.toStringz(), position.x, position.y, color, fontSize, shadow, alignment); 83 } 84 85 public void draw()(in string text, in float x, in float y, in auto ref Color color, in int fontSize, in bool shadow = true, 86 in int alignment = NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP) 87 { 88 this.draw(text.toStringz(), x, y, color, fontSize, shadow, alignment); 89 } 90 91 public void draw()(in wstring text, in float x, in float y, in auto ref Color color, in int fontSize, in bool shadow = true, 92 in int alignment = NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP) 93 { 94 this.draw(text.to!string().toStringz(), x, y, color, fontSize, shadow, alignment); 95 } 96 97 public void draw()(immutable(char)* data, in float x, in float y, in auto ref Color color, in int fontSize, in bool shadow = true, 98 in int alignment = NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP) 99 { 100 auto vg = this.m_nvg; 101 102 nvgSave(vg); 103 104 nvgFontFaceId(vg, this.m_fontId); 105 nvgFontSize(vg, fontSize); 106 107 if (shadow) 108 { 109 auto shadowColor = Color.Black; 110 shadowColor.a = color.a; 111 112 nvgFontBlur(vg, 2); 113 nvgFillColor(vg, shadowColor.asNvg()); 114 nvgTextAlign(vg, alignment); 115 nvgText(vg, x + 1.6f, y + 1.6f, data, null); 116 nvgFontBlur(vg, 0.8); 117 } 118 119 nvgFillColor(vg, color.asNvg()); 120 nvgTextAlign(vg, alignment); 121 122 nvgText(vg, x, y, data, null); 123 124 nvgRestore(vg); 125 } 126 127 /** 128 * Returns glyph position in text. 129 * Params: 130 * index : char index 131 * textX : text position 132 * text : text 133 */ 134 public NVGglyphPosition getGlyphPosition(in uint index, in float textX, in wstring text, in int fontSize) 135 { 136 NVGglyphPosition[1024] glyphs; 137 138 nvgSave(this.m_nvg); 139 140 nvgFontSize(this.m_nvg, fontSize); 141 nvgFontFaceId(this.m_nvg, this.m_fontId); 142 nvgFontBlur(this.m_nvg, 2); 143 144 immutable nglyphs = nvgTextGlyphPositions(this.m_nvg, textX, 0, text.to!string().toStringz(), null, glyphs.ptr, 1024); 145 146 nvgRestore(this.m_nvg); 147 148 return glyphs[index]; 149 } 150 151 /** 152 * Computes text width. 153 * Params: 154 * text : text 155 */ 156 public uint getTextWidth(in wstring text) 157 { 158 return 0; 159 } 160 161 /** 162 * Computes text height. 163 * Params: 164 * text : text 165 */ 166 @nogc @safe 167 public uint getTextHeight(in wstring text) pure nothrow 168 { 169 return 0; 170 } 171 172 /** 173 * Measures the specified text string. Parameter bounds should be a pointer to float[4], 174 * if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 175 * Measured values are returned in local coordinate space. 176 */ 177 public float[4] getTextBounds(in wstring text, in float x, in int fontSize) 178 { 179 return this.getTextBounds(text.to!string().toStringz(), x, fontSize); 180 } 181 182 public float[4] getTextBounds(in string text, in float x, in int fontSize) 183 { 184 return this.getTextBounds(text.toStringz(), x, fontSize); 185 } 186 187 public float[4] getTextBounds(immutable(char)* data, in float x, in int fontSize) 188 { 189 float[4] bounds; 190 nvgFontSize(this.m_nvg, fontSize); 191 nvgFontFaceId(this.m_nvg, this.m_fontId); 192 nvgTextAlign(this.m_nvg, NVGalign.NVG_ALIGN_LEFT | NVGalign.NVG_ALIGN_TOP); 193 nvgTextBounds(this.m_nvg, x, 0, data, null, bounds.ptr); 194 return bounds; 195 } 196 197 /** 198 * Loads font. 199 * Params: 200 * fileName : font file 201 */ 202 public static Font load(in string fileName, NVGcontext* nvg) 203 { 204 import evael.utils.Config; 205 import std.file : exists; 206 import std.path : baseName; 207 208 string file = Config.Paths.fonts!string ~ fileName; 209 string fontShortName = fileName; 210 211 if (!file.exists()) 212 { 213 if (!fileName.exists()) 214 { 215 throw new Exception("Invalid font : " ~ fileName); 216 } 217 else 218 { 219 file = fileName; 220 fontShortName = baseName(fileName); 221 } 222 } 223 224 immutable fontId = nvgCreateFont(nvg, fontShortName.toStringz(), file.toStringz()); 225 226 if (fontId == -1) 227 { 228 sharedLog.errorf("Unable to create font %s", fileName); 229 } 230 231 return new Font(nvg, fontId, file); 232 } 233 234 /** 235 * Properties 236 */ 237 @nogc @safe 238 @property pure nothrow 239 { 240 public ushort size() const 241 { 242 return this.m_size; 243 } 244 245 public void size(in ushort value) 246 { 247 this.m_size = value; 248 } 249 250 public string name() const 251 { 252 return this.m_fontName; 253 } 254 255 public int id() const 256 { 257 return this.m_fontId; 258 } 259 } 260 }