1 module grape.font; 2 3 import derelict.sdl2.sdl; 4 import derelict.sdl2.ttf; 5 import derelict.opengl3.gl3; 6 7 import std.stdio; 8 import std.string; 9 import std.exception : enforce; 10 import std.algorithm; 11 import std.array; 12 import std.conv; 13 import std.file; 14 15 import grape.buffer; 16 import grape.shader; 17 import grape.window; 18 import grape.surface; 19 import grape.renderer; 20 21 static immutable auto FontSizeList = [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 22 15, 16, 17, 18, 20, 22, 24, 26, 23 28, 32, 36, 40, 48, 56, 64, 72 ]; 24 25 /* Cannot compile but I'm not sure... 26 static immutable int[25] FontSizeList = [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 27 15, 16, 17, 18, 20, 22, 24, 26, 28 28, 32, 36, 40, 48, 56, 64, 72 ]; 29 */ 30 31 private final class FontUnit { 32 public: 33 this(in string file, in int size) { 34 _font = TTF_OpenFont(toStringz(file), size); 35 enforce(_font !is null, "TTF_OpenFont() failed"); 36 } 37 38 ~this() { 39 debug(tor) writeln("FontUnit dtor"); 40 TTF_CloseFont(_font); 41 } 42 43 //alias _font this; 44 @property { 45 TTF_Font* unit() { 46 return _font; 47 } 48 } 49 50 private: 51 TTF_Font* _font; 52 } 53 54 /** 55 * Fontを管理するクラス 56 * 57 * FontRendererがあるのでFontをロードする機能くらいしか使いません。 58 */ 59 final class Font { 60 public: 61 this() { 62 if (!_initialized) { 63 _initialized = true; 64 DerelictSDL2ttf.load(); 65 if (TTF_Init() == -1) 66 throw new Exception("TTF_Init() failed"); 67 } 68 69 _texture = new Texture; 70 _surf = new Surface; 71 _instance ~= this; 72 } 73 74 /** 75 * Fontの初期化 76 * 77 * 引数にTTFフォントのファイル名を渡すと読み込みます。 78 */ 79 this(in string file) { 80 this(); 81 load(file); 82 } 83 84 ~this() { 85 debug(tor) writeln("Font dtor"); 86 foreach (font; _units) destroy(font); 87 } 88 89 static ~this() { 90 debug(tor) writeln("Font static dtor"); 91 if (_initialized) { 92 foreach (v; _instance) destroy(v); 93 TTF_Quit(); 94 } 95 } 96 97 /** 98 * Fontの読み込み 99 * 100 * file: TTFフォントのファイル名 101 */ 102 void load(in string file) { 103 enforce(exists(file), file ~ " does not exist"); 104 foreach (size; FontSizeList) 105 _units[size] = new FontUnit(file, size); 106 } 107 108 /** 109 * 文字列テクスチャの作成 110 * 111 * 基本的にユーザーは使いません。 112 * 受け取ったtextのテクスチャを作成します。 113 * size: 文字の大きさ 114 * text: 文字列 115 * color: 文字列の色 116 */ 117 void create_texture(in int size, in string text, in SDL_Color color) { 118 _surf.create({ return TTF_RenderUTF8_Solid(_units[size].unit, toStringz(text), color); }); 119 _surf.convert(SurfaceFormat.abgr8888); 120 //_surf.convert(SurfaceFormat.rgba8888); 121 _texture.create(_surf); 122 } 123 124 @property { 125 /** 126 * 文字列テクスチャを返す 127 * 128 * 基本的にユーザーは使いません。 129 */ 130 Texture texture() { 131 return _texture; 132 } 133 } 134 135 private: 136 FontUnit[int] _units; 137 Texture _texture; 138 Surface _surf; 139 static Font[] _instance; 140 static bool _initialized = false; 141 } 142 143 /** 144 * Fontを描画するクラス 145 * 146 * Fontの描画は全てこのクラスが行います。 147 */ 148 class FontRenderer : Old_Renderer { 149 public: 150 this() { 151 string[] locNames = ["pos", "texCoord"]; 152 int[] strides = [ 3, 2 ]; 153 mixin FontShaderSource; 154 init(FontShader, locNames, strides, DrawMode.Triangles); 155 156 _program.use(); 157 init_vbo(); 158 init_ibo(); 159 set_uniform("tex", 0, "1i"); 160 161 debug(tor) writeln("FontHdr ctor"); 162 } 163 164 this(Font font) { 165 this(); 166 set_font(font); 167 } 168 169 /** 170 * 描画するフォントのセット 171 * 172 * render関数を呼ぶ前に必ず呼ばれる必要があります。 173 * font: 描画するフォント 174 * 175 * TODO: 176 * コンストラクタでやるか 177 */ 178 void set_font(Font font) { 179 _font = font; 180 } 181 182 /** 183 * 描画文字色の設定 184 * 185 * 0~255までの値を引数にとります。 186 * r: 赤 187 * g: 緑 188 * b: 青 189 * 190 * TODO: 191 * 引数の値範囲チェック 192 */ 193 void set_color(in ubyte r, in ubyte g, in ubyte b) { 194 _color = SDL_Color(r, g, b); 195 } 196 197 override void render() {} 198 199 /** 200 * 描画関数 201 * 202 * x: 描画する左上のx座標 203 * y: 描画する左上のy座標 204 * text: 描画する文字列 205 * size: 描画するフォントの大きさ 206 * 207 * TODO: 208 * Rendererのrenderを使ってない 209 */ 210 //void draw(float x, float y, string text, int size = _font.keys[0]) { // TODO 211 void render(in float x, in float y, in string text, in int size) { 212 enforce(!find(FontSizeList, size).array.empty, "Called wrong size of the font. These are available FontSizeList.\n" ~ FontSizeList.to!string); 213 _program.use(); 214 215 _font.create_texture(size, text, _color); 216 217 float[12] pos = set_pos(x, y); 218 set_vbo(pos, _texCoord); 219 _font.texture.texture_scope({ 220 glDepthFunc(GL_ALWAYS); // TODO 221 _ibo.draw(_drawMode); 222 glDepthFunc(GL_LESS); 223 }); 224 } 225 226 private: 227 void init_vbo() { 228 _texCoord = [ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 ]; 229 } 230 231 void init_ibo() { 232 int[] index = [ 0, 1, 2, 0, 2, 3 ]; 233 _ibo = new IBO; 234 _ibo.create(index); 235 } 236 237 float[12] set_pos(in float x, in float y) { 238 auto startX = x / (WINDOW_WIDTH/2.0); 239 auto startY = y / (WINDOW_HEIGHT/2.0); 240 auto w = _font.texture.w / (WINDOW_WIDTH/2.0); 241 auto h = _font.texture.h / (WINDOW_HEIGHT/2.0); 242 243 return [ startX, startY, 0.0, 244 startX+w, startY, 0.0, 245 startX+w, startY-h, 0.0, 246 startX, startY-h, 0.0 ]; 247 } 248 249 Font _font; 250 SDL_Color _color; 251 float[] _texCoord; 252 } 253