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 }