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