1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.ui.ftfont;
29 
30 import std.stdio;
31 
32 import std..string;
33 import std.ascii;
34 import std.utf;
35 import std.file;
36 
37 import dlib.core.memory;
38 import dlib.core.stream;
39 import dlib.container.dict;
40 import dlib.text.utf8;
41 import dlib.math.vector;
42 import dlib.image.color;
43 
44 import derelict.opengl;
45 import derelict.freetype.ft;
46 
47 import dagon.core.ownership;
48 import dagon.ui.font;
49 import dagon.graphics.rc;
50 
51 struct Glyph
52 {
53     bool valid;
54     GLuint textureId = 0;
55     FT_Glyph ftGlyph = null;
56     int width = 0;
57     int height = 0;
58     FT_Pos advanceX = 0;
59 }
60 
61 int nextPowerOfTwo(int a)
62 {
63     int rval = 1;
64     while(rval < a)
65         rval <<= 1;
66     return rval;
67 }
68 
69 final class FreeTypeFont: Font
70 {
71     FT_Face ftFace;
72     FT_Library ftLibrary;
73     Dict!(Glyph, dchar) glyphs;
74     
75     Vector2f[4] vertices;
76     Vector2f[4] texcoords;
77     uint[3][2] indices;
78     
79     GLuint vao = 0;
80     GLuint vbo = 0;
81     GLuint tbo = 0;
82     GLuint eao = 0;
83     
84     bool canRender = false;
85     
86     GLuint shaderProgram;
87     GLuint vertexShader;
88     GLuint fragmentShader;
89     
90     GLint modelViewMatrixLoc;
91     GLint projectionMatrixLoc;
92     
93     GLint glyphPositionLoc;
94     GLint glyphScaleLoc;
95     GLint glyphTexcoordScaleLoc;
96     
97     GLint glyphTextureLoc;
98     GLint glyphColorLoc;
99     
100     string vsText = 
101     q{
102         #version 330 core
103         
104         uniform mat4 modelViewMatrix;
105         uniform mat4 projectionMatrix;
106         
107         uniform vec2 glyphPosition;
108         uniform vec2 glyphScale;
109         uniform vec2 glyphTexcoordScale;
110         
111         layout (location = 0) in vec2 va_Vertex;
112         layout (location = 1) in vec2 va_Texcoord;
113 
114         out vec2 texCoord;
115         
116         void main()
117         {
118             texCoord = va_Texcoord * glyphTexcoordScale;
119             gl_Position = projectionMatrix * modelViewMatrix * vec4(glyphPosition + va_Vertex * glyphScale, 0.0, 1.0);
120         }
121     };
122     
123     string fsText =
124     q{
125         #version 330 core
126         
127         uniform sampler2D glyphTexture;
128         uniform vec4 glyphColor;
129 
130         in vec2 texCoord;
131         out vec4 frag_color;
132         
133         void main()
134         {
135             vec4 t = texture(glyphTexture, texCoord);
136             frag_color = vec4(t.rrr, t.g) * glyphColor;
137         }
138     };
139 
140     this(uint height, Owner o)
141     {
142         super(o);
143         this.height = height;
144 
145         if (FT_Init_FreeType(&ftLibrary))
146             throw new Exception("FT_Init_FreeType failed");
147             
148         vertices[0] = Vector2f(0, 1);
149         vertices[1] = Vector2f(0, 0);
150         vertices[2] = Vector2f(1, 0);
151         vertices[3] = Vector2f(1, 1);
152         
153         texcoords[0] = Vector2f(0, 1);
154         texcoords[1] = Vector2f(0, 0);
155         texcoords[2] = Vector2f(1, 0);
156         texcoords[3] = Vector2f(1, 1);
157         
158         indices[0][0] = 0;
159         indices[0][1] = 1;
160         indices[0][2] = 2;
161         
162         indices[1][0] = 0;
163         indices[1][1] = 2;
164         indices[1][2] = 3;
165     }
166 
167     void createFromFile(string filename)
168     {
169         if (!exists(filename))
170             throw new Exception("Cannot find font file " ~ filename);
171 
172         if (FT_New_Face(ftLibrary, toStringz(filename), 0, &ftFace))
173             throw new Exception("FT_New_Face failed (there is probably a problem with your font file)");
174 
175         FT_Set_Char_Size(ftFace, cast(int)height << 6, cast(int)height << 6, 96, 96);
176         glyphs = New!(Dict!(Glyph, dchar));
177     }
178 
179     void createFromMemory(ubyte[] buffer)
180     {
181         if (FT_New_Memory_Face(ftLibrary, buffer.ptr, cast(uint)buffer.length, 0, &ftFace))
182             throw new Exception("FT_New_Face failed (there is probably a problem with your font file)");
183 
184         FT_Set_Char_Size(ftFace, cast(int)height << 6, cast(int)height << 6, 96, 96);
185         glyphs = New!(Dict!(Glyph, dchar));
186     }
187     
188     void prepareVAO()
189     {
190         if (canRender)
191             return;
192     
193         glGenBuffers(1, &vbo);
194         glBindBuffer(GL_ARRAY_BUFFER, vbo);
195         glBufferData(GL_ARRAY_BUFFER, vertices.length * float.sizeof * 2, vertices.ptr, GL_STATIC_DRAW); 
196 
197         glGenBuffers(1, &tbo);
198         glBindBuffer(GL_ARRAY_BUFFER, tbo);
199         glBufferData(GL_ARRAY_BUFFER, texcoords.length * float.sizeof * 2, texcoords.ptr, GL_STATIC_DRAW);
200 
201         glGenBuffers(1, &eao);
202         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
203         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof * 3, indices.ptr, GL_STATIC_DRAW);
204 
205         glGenVertexArrays(1, &vao);
206         glBindVertexArray(vao);
207         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
208     
209         glEnableVertexAttribArray(0);
210         glBindBuffer(GL_ARRAY_BUFFER, vbo);
211         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null);
212     
213         glEnableVertexAttribArray(1);
214         glBindBuffer(GL_ARRAY_BUFFER, tbo);
215         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null);
216         
217         //glBindBuffer(GL_ARRAY_BUFFER, 0);
218         //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
219 
220         glBindVertexArray(0);
221         
222         const(char*)pvs = vsText.ptr;
223         const(char*)pfs = fsText.ptr;
224         
225         char[1000] infobuffer = 0;
226         int infobufferlen = 0;
227 
228         vertexShader = glCreateShader(GL_VERTEX_SHADER);
229         glShaderSource(vertexShader, 1, &pvs, null);
230         glCompileShader(vertexShader);
231         GLint success = 0;
232         glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
233         if (!success)
234         {
235             GLint logSize = 0;
236             glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logSize);
237             glGetShaderInfoLog(vertexShader, 999, &logSize, infobuffer.ptr);
238             writeln("Error in vertex shader:");
239             writeln(infobuffer[0..logSize]);
240         }
241 
242         fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
243         glShaderSource(fragmentShader, 1, &pfs, null);
244         glCompileShader(fragmentShader);
245         success = 0;
246         glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
247         if (!success)
248         {
249             GLint logSize = 0;
250             glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logSize);
251             glGetShaderInfoLog(fragmentShader, 999, &logSize, infobuffer.ptr);
252             writeln("Error in fragment shader:");
253             writeln(infobuffer[0..logSize]);
254         }
255 
256         shaderProgram = glCreateProgram();
257         glAttachShader(shaderProgram, vertexShader);
258         glAttachShader(shaderProgram, fragmentShader);
259         glLinkProgram(shaderProgram);
260         
261         modelViewMatrixLoc = glGetUniformLocation(shaderProgram, "modelViewMatrix");
262         projectionMatrixLoc = glGetUniformLocation(shaderProgram, "projectionMatrix");
263         
264         glyphPositionLoc = glGetUniformLocation(shaderProgram, "glyphPosition");
265         glyphScaleLoc = glGetUniformLocation(shaderProgram, "glyphScale");
266         glyphTexcoordScaleLoc = glGetUniformLocation(shaderProgram, "glyphTexcoordScale");
267         glyphTextureLoc = glGetUniformLocation(shaderProgram, "glyphTexture");
268         glyphColorLoc = glGetUniformLocation(shaderProgram, "glyphColor");
269         
270         canRender = true;
271     }
272 
273     void preloadASCII()
274     {
275         enum ASCII_CHARS = 128;
276         foreach(i; 0..ASCII_CHARS)
277         {
278             GLuint tex;
279             glGenTextures(1, &tex);
280             loadGlyph(i, tex);
281         }
282     }
283 
284     ~this()
285     {
286         if (canRender)
287         {
288             glDeleteVertexArrays(1, &vao);
289             glDeleteBuffers(1, &vbo);
290             glDeleteBuffers(1, &tbo);
291             glDeleteBuffers(1, &eao);
292         }
293     
294         foreach(i, glyph; glyphs)
295             glDeleteTextures(1, &glyph.textureId);
296         Delete(glyphs);
297     }
298 
299     uint loadGlyph(dchar code, GLuint texId)
300     {
301         FT_Glyph glyph;
302 
303         uint charIndex = FT_Get_Char_Index(ftFace, code);
304 
305         if (charIndex == 0)
306         {
307             //TODO: if character wasn't found in font file
308         }
309 
310         auto res = FT_Load_Glyph(ftFace, charIndex, FT_LOAD_DEFAULT);
311 
312         if (res)
313             throw new Exception(format("FT_Load_Glyph failed with code %s", res));
314 
315         if (FT_Get_Glyph(ftFace.glyph, &glyph))
316             throw new Exception("FT_Get_Glyph failed");
317 
318         FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, null, 1);
319         FT_BitmapGlyph bitmapGlyph = cast(FT_BitmapGlyph)glyph;
320 
321         FT_Bitmap bitmap = bitmapGlyph.bitmap;
322 
323         int width = nextPowerOfTwo(bitmap.width);
324         int height = nextPowerOfTwo(bitmap.rows);
325 
326         GLubyte[] img = New!(GLubyte[])(2 * width * height);
327 
328         foreach(j; 0..height)
329         foreach(i; 0..width)
330         {
331             img[2 * (i + j * width)] = 255;
332             img[2 * (i + j * width) + 1] =
333                 (i >= bitmap.width || j >= bitmap.rows)?
334                  0 : bitmap.buffer[i + bitmap.width * j];
335         }
336 
337         glBindTexture(GL_TEXTURE_2D, texId);
338         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
339         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
341         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
342 
343         glTexImage2D(GL_TEXTURE_2D,
344             0, GL_RG8, width, height,
345             0, GL_RG, GL_UNSIGNED_BYTE, img.ptr);
346 
347         Delete(img);
348 
349         Glyph g = Glyph(true, texId, glyph, width, height, ftFace.glyph.advance.x);
350         glyphs[code] = g;
351 
352         return charIndex;
353     }
354 
355     dchar loadChar(dchar code)
356     {
357         GLuint tex;
358         glGenTextures(1, &tex);
359         loadGlyph(code, tex);
360         return code;
361     }
362 
363     float renderGlyph(dchar code, float shift)
364     {
365         Glyph glyph;
366         if (code in glyphs)
367             glyph = glyphs[code];
368         else
369             glyph = glyphs[loadChar(code)];
370 
371         //if (!glyph.valid)
372         //    return 0.0f;
373 
374         FT_BitmapGlyph bitmapGlyph = cast(FT_BitmapGlyph)(glyph.ftGlyph);
375         FT_Bitmap bitmap = bitmapGlyph.bitmap;
376 
377         glBindTexture(GL_TEXTURE_2D, glyph.textureId);
378         glUniform1i(glyphTextureLoc, 0);
379 
380         float chWidth = cast(float)bitmap.width;
381         float chHeight = cast(float)bitmap.rows;
382         float texWidth = cast(float)glyph.width;
383         float texHeight = cast(float)glyph.height;
384 
385         float x = 0.5f / texWidth + chWidth / texWidth;
386         float y = 0.5f / texHeight + chHeight / texHeight;
387         
388         Vector2f glyphPosition = Vector2f(shift + bitmapGlyph.left, -bitmapGlyph.top); //-(bitmapGlyph.top - bitmap.rows))
389         Vector2f glyphScale = Vector2f(bitmap.width, bitmap.rows);
390         Vector2f glyphTexcoordScale = Vector2f(x, y);
391         
392         glUniform2fv(glyphPositionLoc, 1, glyphPosition.arrayof.ptr);
393         glUniform2fv(glyphScaleLoc, 1, glyphScale.arrayof.ptr);
394         glUniform2fv(glyphTexcoordScaleLoc, 1, glyphTexcoordScale.arrayof.ptr);
395 
396         glBindVertexArray(vao);
397         glDrawElements(GL_TRIANGLES, cast(uint)indices.length * 3, GL_UNSIGNED_INT, cast(void*)0);
398         glBindVertexArray(0);
399         
400         shift = glyph.advanceX >> 6;
401 
402         glBindTexture(GL_TEXTURE_2D, 0);
403 
404         return shift;
405     }
406 
407     int glyphAdvance(dchar code)
408     {
409         Glyph glyph;
410         if (code in glyphs)
411             glyph = glyphs[code];
412         else
413             glyph = glyphs[loadChar(code)];
414         return cast(int)(glyph.advanceX >> 6);
415     }
416 
417     override void render(RenderingContext* rc, Color4f color, string str)
418     {
419         if (!canRender)
420             return;
421     
422         glUseProgram(shaderProgram);
423         
424         glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, rc.modelViewMatrix.arrayof.ptr);
425         glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, rc.projectionMatrix.arrayof.ptr);
426         
427         glUniform4fv(glyphColorLoc, 1, color.arrayof.ptr);
428 
429         float shift = 0.0f;
430         UTF8Decoder dec = UTF8Decoder(str);
431         int ch;
432         do
433         {
434             ch = dec.decodeNext();
435             if (ch == 0 || ch == UTF8_END || ch == UTF8_ERROR) break;
436             dchar code = ch;
437             if (code.isASCII)
438             {
439                 if (code.isPrintable)
440                     shift += renderGlyph(code, shift);
441             }
442             else
443                 shift += renderGlyph(code, shift);
444         } while(ch != UTF8_END && ch != UTF8_ERROR);
445         
446         glUseProgram(0);
447     }
448 
449     override float width(string str)
450     {
451         float width = 0.0f;
452         UTF8Decoder dec = UTF8Decoder(str);
453         int ch;
454         do
455         {
456             ch = dec.decodeNext();
457             if (ch == 0 || ch == UTF8_END || ch == UTF8_ERROR) break;
458             dchar code = ch;
459             if (code.isASCII)
460             {
461                 if (code.isPrintable)
462                     width += glyphAdvance(code);
463             }
464             else
465                 width += glyphAdvance(code);
466         } while(ch != UTF8_END && ch != UTF8_ERROR);
467 
468         return width;
469     }
470 }