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 dagon.core.libs; 45 import dagon.core.ownership; 46 import dagon.ui.font; 47 import dagon.graphics.rc; 48 49 struct Glyph 50 { 51 bool valid; 52 GLuint textureId = 0; 53 FT_Glyph ftGlyph = null; 54 int width = 0; 55 int height = 0; 56 FT_Pos advanceX = 0; 57 } 58 59 int nextPowerOfTwo(int a) 60 { 61 int rval = 1; 62 while(rval < a) 63 rval <<= 1; 64 return rval; 65 } 66 67 final class FreeTypeFont: Font 68 { 69 FT_Face ftFace; 70 FT_Library ftLibrary; 71 Dict!(Glyph, dchar) glyphs; 72 73 Vector2f[4] vertices; 74 Vector2f[4] texcoords; 75 uint[3][2] indices; 76 77 GLuint vao = 0; 78 GLuint vbo = 0; 79 GLuint tbo = 0; 80 GLuint eao = 0; 81 82 bool canRender = false; 83 84 GLuint shaderProgram; 85 GLuint vertexShader; 86 GLuint fragmentShader; 87 88 GLint modelViewMatrixLoc; 89 GLint projectionMatrixLoc; 90 91 GLint glyphPositionLoc; 92 GLint glyphScaleLoc; 93 GLint glyphTexcoordScaleLoc; 94 95 GLint glyphTextureLoc; 96 GLint glyphColorLoc; 97 98 string vs = import("Text.vs"); 99 string fs = import("Text.fs"); 100 101 this(uint height, Owner o) 102 { 103 super(o); 104 this.height = height; 105 106 if (FT_Init_FreeType(&ftLibrary)) 107 throw new Exception("FT_Init_FreeType failed"); 108 109 vertices[0] = Vector2f(0, 1); 110 vertices[1] = Vector2f(0, 0); 111 vertices[2] = Vector2f(1, 0); 112 vertices[3] = Vector2f(1, 1); 113 114 texcoords[0] = Vector2f(0, 1); 115 texcoords[1] = Vector2f(0, 0); 116 texcoords[2] = Vector2f(1, 0); 117 texcoords[3] = Vector2f(1, 1); 118 119 indices[0][0] = 0; 120 indices[0][1] = 1; 121 indices[0][2] = 2; 122 123 indices[1][0] = 0; 124 indices[1][1] = 2; 125 indices[1][2] = 3; 126 } 127 128 void createFromFile(string filename) 129 { 130 if (!exists(filename)) 131 throw new Exception("Cannot find font file " ~ filename); 132 133 if (FT_New_Face(ftLibrary, toStringz(filename), 0, &ftFace)) 134 throw new Exception("FT_New_Face failed (there is probably a problem with your font file)"); 135 136 FT_Set_Char_Size(ftFace, cast(int)height << 6, cast(int)height << 6, 96, 96); 137 glyphs = New!(Dict!(Glyph, dchar)); 138 } 139 140 void createFromMemory(ubyte[] buffer) 141 { 142 if (FT_New_Memory_Face(ftLibrary, buffer.ptr, cast(uint)buffer.length, 0, &ftFace)) 143 throw new Exception("FT_New_Face failed (there is probably a problem with your font file)"); 144 145 FT_Set_Char_Size(ftFace, cast(int)height << 6, cast(int)height << 6, 96, 96); 146 glyphs = New!(Dict!(Glyph, dchar)); 147 } 148 149 void prepareVAO() 150 { 151 if (canRender) 152 return; 153 154 glGenBuffers(1, &vbo); 155 glBindBuffer(GL_ARRAY_BUFFER, vbo); 156 glBufferData(GL_ARRAY_BUFFER, vertices.length * float.sizeof * 2, vertices.ptr, GL_STATIC_DRAW); 157 158 glGenBuffers(1, &tbo); 159 glBindBuffer(GL_ARRAY_BUFFER, tbo); 160 glBufferData(GL_ARRAY_BUFFER, texcoords.length * float.sizeof * 2, texcoords.ptr, GL_STATIC_DRAW); 161 162 glGenBuffers(1, &eao); 163 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 164 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof * 3, indices.ptr, GL_STATIC_DRAW); 165 166 glGenVertexArrays(1, &vao); 167 glBindVertexArray(vao); 168 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 169 170 glEnableVertexAttribArray(0); 171 glBindBuffer(GL_ARRAY_BUFFER, vbo); 172 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null); 173 174 glEnableVertexAttribArray(1); 175 glBindBuffer(GL_ARRAY_BUFFER, tbo); 176 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null); 177 178 //glBindBuffer(GL_ARRAY_BUFFER, 0); 179 //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 180 181 glBindVertexArray(0); 182 183 const(char*)pvs = vs.ptr; 184 const(char*)pfs = fs.ptr; 185 186 char[1000] infobuffer = 0; 187 int infobufferlen = 0; 188 189 vertexShader = glCreateShader(GL_VERTEX_SHADER); 190 glShaderSource(vertexShader, 1, &pvs, null); 191 glCompileShader(vertexShader); 192 GLint success = 0; 193 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); 194 if (!success) 195 { 196 GLint logSize = 0; 197 glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logSize); 198 glGetShaderInfoLog(vertexShader, 999, &logSize, infobuffer.ptr); 199 writeln("Error in vertex shader:"); 200 writeln(infobuffer[0..logSize]); 201 } 202 203 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 204 glShaderSource(fragmentShader, 1, &pfs, null); 205 glCompileShader(fragmentShader); 206 success = 0; 207 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); 208 if (!success) 209 { 210 GLint logSize = 0; 211 glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logSize); 212 glGetShaderInfoLog(fragmentShader, 999, &logSize, infobuffer.ptr); 213 writeln("Error in fragment shader:"); 214 writeln(infobuffer[0..logSize]); 215 } 216 217 shaderProgram = glCreateProgram(); 218 glAttachShader(shaderProgram, vertexShader); 219 glAttachShader(shaderProgram, fragmentShader); 220 glLinkProgram(shaderProgram); 221 222 modelViewMatrixLoc = glGetUniformLocation(shaderProgram, "modelViewMatrix"); 223 projectionMatrixLoc = glGetUniformLocation(shaderProgram, "projectionMatrix"); 224 225 glyphPositionLoc = glGetUniformLocation(shaderProgram, "glyphPosition"); 226 glyphScaleLoc = glGetUniformLocation(shaderProgram, "glyphScale"); 227 glyphTexcoordScaleLoc = glGetUniformLocation(shaderProgram, "glyphTexcoordScale"); 228 glyphTextureLoc = glGetUniformLocation(shaderProgram, "glyphTexture"); 229 glyphColorLoc = glGetUniformLocation(shaderProgram, "glyphColor"); 230 231 canRender = true; 232 } 233 234 void preloadASCII() 235 { 236 enum ASCII_CHARS = 128; 237 foreach(i; 0..ASCII_CHARS) 238 { 239 GLuint tex; 240 glGenTextures(1, &tex); 241 loadGlyph(i, tex); 242 } 243 } 244 245 ~this() 246 { 247 if (canRender) 248 { 249 glDeleteVertexArrays(1, &vao); 250 glDeleteBuffers(1, &vbo); 251 glDeleteBuffers(1, &tbo); 252 glDeleteBuffers(1, &eao); 253 } 254 255 foreach(i, glyph; glyphs) 256 glDeleteTextures(1, &glyph.textureId); 257 Delete(glyphs); 258 } 259 260 uint loadGlyph(dchar code, GLuint texId) 261 { 262 FT_Glyph glyph; 263 264 uint charIndex = FT_Get_Char_Index(ftFace, code); 265 266 if (charIndex == 0) 267 { 268 //TODO: if character wasn't found in font file 269 } 270 271 auto res = FT_Load_Glyph(ftFace, charIndex, FT_LOAD_DEFAULT); 272 273 if (res) 274 throw new Exception(format("FT_Load_Glyph failed with code %s", res)); 275 276 if (FT_Get_Glyph(ftFace.glyph, &glyph)) 277 throw new Exception("FT_Get_Glyph failed"); 278 279 FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, null, 1); 280 FT_BitmapGlyph bitmapGlyph = cast(FT_BitmapGlyph)glyph; 281 282 FT_Bitmap bitmap = bitmapGlyph.bitmap; 283 284 int width = nextPowerOfTwo(bitmap.width); 285 int height = nextPowerOfTwo(bitmap.rows); 286 287 GLubyte[] img = New!(GLubyte[])(2 * width * height); 288 289 foreach(j; 0..height) 290 foreach(i; 0..width) 291 { 292 img[2 * (i + j * width)] = 255; 293 img[2 * (i + j * width) + 1] = 294 (i >= bitmap.width || j >= bitmap.rows)? 295 0 : bitmap.buffer[i + bitmap.width * j]; 296 } 297 298 glBindTexture(GL_TEXTURE_2D, texId); 299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 303 304 glTexImage2D(GL_TEXTURE_2D, 305 0, GL_RG8, width, height, 306 0, GL_RG, GL_UNSIGNED_BYTE, img.ptr); 307 308 Delete(img); 309 310 Glyph g = Glyph(true, texId, glyph, width, height, ftFace.glyph.advance.x); 311 glyphs[code] = g; 312 313 return charIndex; 314 } 315 316 dchar loadChar(dchar code) 317 { 318 GLuint tex; 319 glGenTextures(1, &tex); 320 loadGlyph(code, tex); 321 return code; 322 } 323 324 float renderGlyph(dchar code, float shift) 325 { 326 Glyph glyph; 327 if (code in glyphs) 328 glyph = glyphs[code]; 329 else 330 glyph = glyphs[loadChar(code)]; 331 332 //if (!glyph.valid) 333 // return 0.0f; 334 335 FT_BitmapGlyph bitmapGlyph = cast(FT_BitmapGlyph)(glyph.ftGlyph); 336 FT_Bitmap bitmap = bitmapGlyph.bitmap; 337 338 glBindTexture(GL_TEXTURE_2D, glyph.textureId); 339 glUniform1i(glyphTextureLoc, 0); 340 341 float chWidth = cast(float)bitmap.width; 342 float chHeight = cast(float)bitmap.rows; 343 float texWidth = cast(float)glyph.width; 344 float texHeight = cast(float)glyph.height; 345 346 float x = 0.5f / texWidth + chWidth / texWidth; 347 float y = 0.5f / texHeight + chHeight / texHeight; 348 349 Vector2f glyphPosition = Vector2f(shift + bitmapGlyph.left, -bitmapGlyph.top); //-(bitmapGlyph.top - bitmap.rows)) 350 Vector2f glyphScale = Vector2f(bitmap.width, bitmap.rows); 351 Vector2f glyphTexcoordScale = Vector2f(x, y); 352 353 glUniform2fv(glyphPositionLoc, 1, glyphPosition.arrayof.ptr); 354 glUniform2fv(glyphScaleLoc, 1, glyphScale.arrayof.ptr); 355 glUniform2fv(glyphTexcoordScaleLoc, 1, glyphTexcoordScale.arrayof.ptr); 356 357 glBindVertexArray(vao); 358 glDrawElements(GL_TRIANGLES, cast(uint)indices.length * 3, GL_UNSIGNED_INT, cast(void*)0); 359 glBindVertexArray(0); 360 361 shift = glyph.advanceX >> 6; 362 363 glBindTexture(GL_TEXTURE_2D, 0); 364 365 return shift; 366 } 367 368 int glyphAdvance(dchar code) 369 { 370 Glyph glyph; 371 if (code in glyphs) 372 glyph = glyphs[code]; 373 else 374 glyph = glyphs[loadChar(code)]; 375 return cast(int)(glyph.advanceX >> 6); 376 } 377 378 override void render(RenderingContext* rc, Color4f color, string str) 379 { 380 if (!canRender) 381 return; 382 383 glUseProgram(shaderProgram); 384 385 glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, rc.modelViewMatrix.arrayof.ptr); 386 glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, rc.projectionMatrix.arrayof.ptr); 387 388 glUniform4fv(glyphColorLoc, 1, color.arrayof.ptr); 389 390 float shift = 0.0f; 391 UTF8Decoder dec = UTF8Decoder(str); 392 int ch; 393 do 394 { 395 ch = dec.decodeNext(); 396 if (ch == 0 || ch == UTF8_END || ch == UTF8_ERROR) break; 397 dchar code = ch; 398 if (code.isASCII) 399 { 400 if (code.isPrintable) 401 shift += renderGlyph(code, shift); 402 } 403 else 404 shift += renderGlyph(code, shift); 405 } while(ch != UTF8_END && ch != UTF8_ERROR); 406 407 glUseProgram(0); 408 } 409 410 override float width(string str) 411 { 412 float width = 0.0f; 413 UTF8Decoder dec = UTF8Decoder(str); 414 int ch; 415 do 416 { 417 ch = dec.decodeNext(); 418 if (ch == 0 || ch == UTF8_END || ch == UTF8_ERROR) break; 419 dchar code = ch; 420 if (code.isASCII) 421 { 422 if (code.isPrintable) 423 width += glyphAdvance(code); 424 } 425 else 426 width += glyphAdvance(code); 427 } while(ch != UTF8_END && ch != UTF8_ERROR); 428 429 return width; 430 } 431 }