1 /* 2 Copyright (c) 2014-2015 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module dgl.ui.ftfont; 30 31 import std.stdio; 32 33 import std.string; 34 import std.ascii; 35 import std.range; 36 import std.file; 37 38 import dlib.core.memory; 39 import dlib.container.bst; 40 41 import derelict.opengl.gl; 42 import derelict.freetype.ft; 43 44 import dgl.ui.font; 45 46 struct Glyph 47 { 48 GLuint textureId = 0; 49 FT_Glyph ftGlyph = null; 50 int width = 0; 51 int height = 0; 52 FT_Pos advanceX = 0; 53 } 54 55 int nextPowerOfTwo(int a) 56 { 57 int rval = 1; 58 while(rval < a) 59 rval <<= 1; 60 return rval; 61 } 62 63 class CharStorage(T): BST!(T) 64 { 65 this() 66 { 67 super(); 68 } 69 70 void opIndexAssign(T v, dchar k) 71 { 72 insert(k, v); 73 } 74 75 T opIndex(dchar k) 76 { 77 auto node = find(k); 78 if (node is null) 79 return value.init; 80 else 81 return node.value; 82 } 83 84 T* opBinaryRight(string op)(dchar k) if (op == "in") 85 { 86 auto node = find(k); 87 if (node !is null) 88 return &node.value; 89 else 90 return null; 91 } 92 93 size_t length() 94 { 95 uint len = 1; 96 foreach(i, glyph; this) 97 len++; 98 return len; 99 } 100 } 101 102 final class FreeTypeFont: Font 103 { 104 FT_Face ftFace; 105 FT_Library ftLibrary; 106 107 CharStorage!Glyph glyphs; 108 109 this(string filename, uint height) 110 { 111 enum ASCII_CHARS = 128; 112 this.height = height; 113 114 if (FT_Init_FreeType(&ftLibrary)) 115 throw new Exception("FT_Init_FreeType failed"); 116 117 if (!exists(filename)) 118 throw new Exception("Cannot find font file " ~ filename); 119 120 if (FT_New_Face(ftLibrary, toStringz(filename), 0, &ftFace)) 121 throw new Exception("FT_New_Face failed (there is probably a problem with your font file)"); 122 123 FT_Set_Char_Size(ftFace, height << 6, height << 6, 96, 96); 124 125 glyphs = New!(CharStorage!Glyph)(); 126 127 foreach(i; 0..ASCII_CHARS) 128 { 129 GLuint tex; 130 glGenTextures(1, &tex); 131 loadGlyph(i, tex); 132 } 133 } 134 135 ~this() 136 { 137 //writefln("Deleting %s glyph(s) in FTFont...", glyphs.length); 138 foreach(i, glyph; glyphs) 139 glDeleteTextures(1, &glyph.textureId); 140 Delete(glyphs); 141 } 142 143 void free() 144 { 145 Delete(this); 146 } 147 148 uint loadGlyph(dchar code, GLuint texId) 149 { 150 uint charIndex = FT_Get_Char_Index(ftFace, code); 151 152 if (charIndex == 0) 153 { 154 //TODO: if character wasn't found in font file 155 } 156 157 if (FT_Load_Glyph(ftFace, charIndex, FT_LOAD_DEFAULT)) 158 throw new Exception("FT_Load_Glyph failed"); 159 160 FT_Glyph glyph; 161 if (FT_Get_Glyph(ftFace.glyph, &glyph)) 162 throw new Exception("FT_Get_Glyph failed"); 163 164 FT_Glyph_To_Bitmap(&glyph, FT_Render_Mode.FT_RENDER_MODE_NORMAL, null, 1); 165 FT_BitmapGlyph bitmapGlyph = cast(FT_BitmapGlyph)glyph; 166 167 FT_Bitmap bitmap = bitmapGlyph.bitmap; 168 169 int width = nextPowerOfTwo(bitmap.width); 170 int height = nextPowerOfTwo(bitmap.rows); 171 172 GLubyte[] img = New!(GLubyte[])(2 * width * height); 173 174 foreach(j; 0..height) 175 foreach(i; 0..width) 176 { 177 img[2 * (i + j * width)] = 255; 178 img[2 * (i + j * width) + 1] = 179 (i >= bitmap.width || j >= bitmap.rows)? 180 0 : bitmap.buffer[i + bitmap.width * j]; 181 } 182 183 glBindTexture(GL_TEXTURE_2D, texId); 184 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 185 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 186 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 188 189 glTexImage2D(GL_TEXTURE_2D, 190 0, GL_RGBA, width, height, 191 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, img.ptr); 192 193 Delete(img); 194 195 Glyph g = Glyph(texId, glyph, width, height, ftFace.glyph.advance.x); 196 glyphs[code] = g; 197 198 return charIndex; 199 } 200 201 dchar loadChar(dchar code) 202 { 203 GLuint tex; 204 glGenTextures(1, &tex); 205 loadGlyph(code, tex); 206 return code; 207 } 208 209 float renderGlyph(dchar code) 210 { 211 Glyph glyph; 212 if (code in glyphs) 213 glyph = glyphs[code]; 214 else 215 glyph = glyphs[loadChar(code)]; 216 217 FT_BitmapGlyph bitmapGlyph = cast(FT_BitmapGlyph)(glyph.ftGlyph); 218 FT_Bitmap bitmap = bitmapGlyph.bitmap; 219 220 glBindTexture(GL_TEXTURE_2D, glyph.textureId); 221 222 glPushMatrix(); 223 glTranslatef(bitmapGlyph.left, 0, 0); 224 225 float chWidth = cast(float)bitmap.width; 226 float chHeight = cast(float)bitmap.rows; 227 float texWidth = cast(float)glyph.width; 228 float texHeight = cast(float)glyph.height; 229 230 glTranslatef(0, bitmapGlyph.top - bitmap.rows, 0); 231 float x = 0.5f / texWidth + chWidth / texWidth; 232 float y = 0.5f / texHeight + chHeight / texHeight; 233 glBegin(GL_QUADS); 234 glTexCoord2f(0, 0); glVertex2f(0, bitmap.rows); 235 glTexCoord2f(0, y); glVertex2f(0, 0); 236 glTexCoord2f(x, y); glVertex2f(bitmap.width, 0); 237 glTexCoord2f(x, 0); glVertex2f(bitmap.width, bitmap.rows); 238 glEnd(); 239 glPopMatrix(); 240 float shift = glyph.advanceX >> 6; 241 glTranslatef(shift, 0, 0); 242 243 glBindTexture(GL_TEXTURE_2D, 0); 244 245 return shift; 246 } 247 248 int glyphAdvance(dchar code) 249 { 250 Glyph glyph; 251 if (code in glyphs) 252 glyph = glyphs[code]; 253 else 254 glyph = glyphs[loadChar(code)]; 255 return cast(int)(glyph.advanceX >> 6); 256 } 257 258 override void draw(string str) 259 { 260 foreach(ch; stride(str, 1)) 261 { 262 dchar code = ch; 263 if (code.isASCII) 264 { 265 if (code.isPrintable) 266 renderGlyph(code); 267 } 268 else 269 renderGlyph(code); 270 } 271 } 272 273 override float textWidth(string str) 274 { 275 float width = 0.0f; 276 foreach(ch; stride(str, 1)) 277 { 278 dchar code = ch; 279 if (code.isASCII) 280 { 281 if (code.isPrintable) 282 width += glyphAdvance(code); 283 } 284 else 285 width += glyphAdvance(code); 286 } 287 288 return width; 289 } 290 }