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 }