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 }