1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  * 
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  * 
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  * 
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  * 
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  * 
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.Graphics.Font;
25 
26 private {
27 	debug import std.stdio : writeln;
28 	import std.file : exists;
29 	import std.conv : to;
30 	import std..string : toStringz;
31 	import std.exception : enforce;
32 	
33 	import derelict.sdl2.ttf;
34 	
35 	import Dgame.Internal.Shared;
36 	import Dgame.Internal.Log;
37 }
38 
39 /**
40  * Font is the low-level class for loading and manipulating character fonts.
41  * This class is meant to be used by Dgame.Graphics.Text.
42  *
43  * Author: rschuett
44  */
45 struct Font {
46 	/**
47 	 * Font styles
48 	 */
49 	enum Style : ubyte {
50 		Bold      = TTF_STYLE_BOLD,			/** Makes the text bold */
51 		Italic    = TTF_STYLE_ITALIC,		/** Makes the text italic */
52 		Underline = TTF_STYLE_UNDERLINE,	/** Underline the text */
53 		Crossed   = TTF_STYLE_STRIKETHROUGH, /** Cross the text */
54 		Normal    = TTF_STYLE_NORMAL		/** Normal text without any style. */
55 	}
56 	
57 	/**
58 	 * Font Hints
59 	 */
60 	enum Hint : ubyte {
61 		Normal = TTF_HINTING_NORMAL, /** Normal (default) Hint */
62 		Light  = TTF_HINTING_LIGHT,  /** */
63 		Mono   = TTF_HINTING_MONO,   /** */
64 		None   = TTF_HINTING_NONE    /** No Hint */
65 	}
66 	
67 	/**
68 	 * Font mode
69 	 */
70 	enum Mode : ubyte {
71 		Solid,  /** Solid mode is dirty but fast. */
72 		Shaded, /** Blended is optimized but still fast. */
73 		Blended /** Nicest but slowest mode. */
74 	}
75 	
76 private:
77 	shared_ptr!(TTF_Font) _target;
78 	string _filename;
79 	
80 	ubyte _fontSize;
81 	Mode _mode;
82 	Style _style;
83 	Hint _hint;
84 	
85 public:
86 	/**
87 	 * CTor
88 	 */
89 	this(string filename, ubyte size, Mode mode = Mode.Solid,
90 	     Style style = Style.Normal)
91 	{
92 		this._mode  = mode;
93 		this._style = style;
94 		
95 		this.loadFromFile(filename, size);
96 	}
97 	
98 	/**
99 	 * Postblit
100 	 */
101 	debug(Dgame)
102 	this(this) {
103 		debug Log.info("Font Postblit");
104 	}
105 	
106 	/**
107 	 * DTor
108 	 */
109 	debug(Dgame)
110 	~this() {
111 		debug Log.info("Close Font");
112 	}
113 	
114 	/**
115 	 * Close and release the current font <b>and all</b> which are linked to this Font.
116 	 */
117 	void free() {
118 		this._target.terminate();
119 	}
120 	
121 	/**
122 	 * Load the font from a file.
123 	 * If the second parameter isn't 0, the current font size will be replaced with that.
124 	 * If both are 0, an exception is thrown.
125 	 */
126 	void loadFromFile(string fontFile, ubyte fontSize = 0) {
127 		this._filename = fontFile;
128 		
129 		this._fontSize = fontSize == 0 ? this._fontSize : fontSize;
130 		enforce(this._fontSize != 0, "No valid size for this font.");
131 		
132 		if (!exists(fontFile))
133 			Log.error("Font File %s does not exists.", fontFile);
134 		
135 		TTF_Font* font = TTF_OpenFont(toStringz(fontFile), this._fontSize);
136 		if (font is null) {
137 			Log.error("Could not load font %s. TTF Error: %s.",
138 			          fontFile, to!(string)(TTF_GetError()));
139 		}
140 		
141 		this._target = make_shared(font, (TTF_Font* ttf) => (TTF_CloseFont(ttf)));
142 	}
143 	
144 	/**
145 	 * Returns the current filename, if any
146 	 */
147 	@property
148 	string filename() const pure nothrow {
149 		return this._filename;
150 	}
151 	
152 	/**
153 	 * Set the font style.
154 	 *
155 	 * See: Font.Style enum
156 	 */
157 	void setStyle(Style style) {
158 		TTF_SetFontStyle(this._target, style);
159 	}
160 	
161 	/**
162 	 * Returns the current font style.
163 	 *
164 	 * See: Font.Style enum
165 	 */
166 	Style getStyle() const {
167 		return cast(Style) TTF_GetFontStyle(this._target);
168 	}
169 	
170 	/**
171 	 * Set the font mode.
172 	 *
173 	 * See: Font.Mode enum
174 	 */
175 	void setMode(Mode mode) pure nothrow {
176 		this._mode = mode;
177 	}
178 	
179 	/**
180 	 * Returns the current font mode.
181 	 *
182 	 * See: Font.Mode enum
183 	 */
184 	Mode getMode() const pure nothrow {
185 		return this._mode;
186 	}
187 	
188 	/**
189 	 * Set a hint for the Font
190 	 * 
191 	 * See: Hint enum
192 	 */
193 	void setHint(Hint hint) {
194 		this._hint = hint;
195 		TTF_SetFontHinting(this._target, hint);
196 	}
197 	
198 	/**
199 	 * Returns the current hint
200 	 */
201 	Hint getHint() const pure nothrow {
202 		return this._hint;
203 	}
204 	
205 	/**
206 	 * Returns the current font size.
207 	 */
208 	ubyte getSize() const pure nothrow {
209 		return this._fontSize;
210 	}
211 	
212 	/**
213 	 * Returns a TTFthis._target pointer.
214 	 */
215 	@property
216 	inout(TTF_Font)* ptr() inout pure nothrow {
217 		return this._target.ptr;
218 	}
219 	
220 	/**
221 	 * Returns the current use count
222 	 */
223 	int useCount() const pure nothrow {
224 		return this._target.usage;
225 	}
226 } unittest {
227 	writeln("<Font unittest>");
228 	
229 	{
230 		Font f1 = Font("../../samples/font/arial.ttf", 14);
231 		
232 		assert(f1.useCount() == 1, to!string(f1.useCount()));
233 		{
234 			Font f2 = f1;
235 			
236 			assert(f1.useCount() == 2, to!string(f1.useCount()));
237 			assert(f2.useCount() == 2, to!string(f2.useCount()));
238 			
239 			f2 = f1;
240 			
241 			assert(f1.useCount() == 2, to!string(f1.useCount()));
242 			assert(f2.useCount() == 2, to!string(f2.useCount()));
243 			
244 			{
245 				Font f3 = f2;
246 				
247 				assert(f1.useCount() == 3, to!string(f1.useCount()));
248 				assert(f2.useCount() == 3, to!string(f2.useCount()));
249 				assert(f3.useCount() == 3, to!string(f3.useCount()));
250 			}
251 			
252 			assert(f1.useCount() == 2, to!string(f1.useCount()));
253 			assert(f2.useCount() == 2, to!string(f2.useCount()));
254 		}
255 		assert(f1.useCount() == 1, to!string(f1.useCount()));
256 	}
257 	
258 	writeln("</Font unittest>");
259 }