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