1 module des.fonts.ftglyphrender; 2 3 import des.math.linear; 4 import des.il; 5 import des.util.arch.emm; 6 import des.util.stdext; 7 8 import derelict.freetype.ft; 9 import derelict.freetype.types; 10 11 alias SizeVector!2 imsize_t; 12 13 struct BitmapFont 14 { 15 ivec2[wchar] offset; 16 ivec2[wchar] size; 17 ivec2[wchar] bearing; 18 19 Image!2 texture; 20 } 21 22 class FTGlyphRenderException: Exception 23 { 24 @safe pure nothrow this( string msg, string file=__FILE__, size_t line=__LINE__ ) 25 { super( msg, file, line ); } 26 } 27 28 struct GlyphInfo 29 { 30 ivec2 pos, next; 31 @property ivec2 size() const { return ivec2( img.size ); } 32 Image!2 img; 33 } 34 35 struct GlyphParam 36 { 37 enum Flag 38 { 39 NONE = cast(ubyte)0, 40 BOLD = 0b0001, 41 ITALIC = 0b0010, 42 UNDERLINE = 0b0100, 43 STRIKED = 0b1000 44 } 45 46 ubyte flag = Flag.NONE; 47 uint height=12; 48 } 49 50 interface GlyphRender 51 { 52 void setParams( in GlyphParam p ); 53 @property ElemInfo imtype() const; 54 GlyphInfo render( wchar ch ); 55 BitmapFont generateBitmapFont(); 56 } 57 58 class FTGlyphRender : GlyphRender, ExternalMemoryManager 59 { 60 mixin EMM; 61 protected: 62 void selfDestroy() 63 { 64 if( FT_Done_Face !is null ) 65 FT_Done_Face( face ); 66 } 67 private: 68 static lib_inited = false; 69 static FT_Library ft; 70 71 static FTGlyphRender[string] openFTGR; 72 73 FT_Face face; 74 75 static this() 76 { 77 if( !lib_inited ) 78 { 79 DerelictFT.load(); 80 if( FT_Init_FreeType( &ft ) ) 81 throw new FTGlyphRenderException( "Couldn't init freetype library" ); 82 83 lib_inited = true; 84 } 85 } 86 87 this( string fontname ) 88 { 89 import std.file; 90 if( !fontname.exists ) 91 throw new FTGlyphRenderException( "Couldn't open font '" ~ fontname ~ "': file not exist" ); 92 93 bool loaderror = false; 94 foreach( i; 0 .. 100 ) 95 { 96 if( FT_New_Face( ft, fontname.dup.ptr, 0, &face ) ) loaderror = true; 97 else { loaderror = false; break; } 98 } 99 100 if( loaderror ) 101 throw new FTGlyphRenderException( "Couldn't open font '" ~ fontname ~ "': loading error" ); 102 103 if( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) 104 throw new FTGlyphRenderException( "Couldn't select unicode encoding" ); 105 } 106 107 public: 108 109 static GlyphRender get( string fontname ) 110 { 111 if( fontname !in openFTGR ) 112 openFTGR[fontname] = new FTGlyphRender( fontname ); 113 return openFTGR[fontname]; 114 } 115 116 void setParams( in GlyphParam p ) 117 { 118 FT_Set_Pixel_Sizes( face, 0, p.height ); 119 } 120 121 @property ElemInfo imtype() const { return ElemInfo( DataType.FLOAT, 1 ); } 122 123 GlyphInfo render( wchar ch ) 124 { 125 if( FT_Load_Char( face, cast(size_t)ch, FT_LOAD_RENDER ) ) 126 throw new FTGlyphRenderException( "Couldn't load char" ); 127 128 auto g = face.glyph; 129 130 ivec2 sz; 131 float[] img_data; 132 133 if( ch == ' ' ) 134 { 135 auto width = g.metrics.horiAdvance / 128.0;//TODO not proper way i think 136 sz = ivec2( width, g.bitmap.rows ); 137 img_data.length = sz.x * sz.y; 138 img_data[] = 0; 139 } 140 else 141 { 142 sz = ivec2( g.bitmap.width, g.bitmap.rows ); 143 img_data = amap!(a => a / 255.0f)(g.bitmap.buffer[0 .. sz.x * sz.y]); 144 } 145 146 return GlyphInfo( ivec2( g.bitmap_left, -g.bitmap_top ), 147 ivec2( cast(int)( g.advance.x >> 6 ), 148 cast(int)( g.advance.y >> 6 ) ), 149 Image!2( imsize_t(sz), imtype, img_data ) ); 150 } 151 152 BitmapFont generateBitmapFont()//rendering only russian/english letters and basic symbols 153 {//TODO check if one line texture is proper 154 BitmapFont res; 155 GlyphInfo[wchar] glyphs; 156 foreach( wchar i; 32 .. 128 )// !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 157 glyphs[i] = render( i ); 158 foreach( wchar i; 1040 .. 1104 )//АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя 159 glyphs[i] = render( i ); 160 uint maxh = 0; 161 uint width = 0; 162 foreach( ref g; glyphs ) 163 { 164 if( g.img.size.h > maxh ) 165 maxh = cast( uint )g.img.size.h; 166 width += g.img.size.w; 167 } 168 res.texture = Image!2( imsize_t( width, maxh ), imtype ); 169 170 uint offset = 0; 171 172 foreach( key, ref g; glyphs ) 173 { 174 res.offset[ key ] = ivec2( offset, 0 ); 175 res.size[ key ] = ivec2( g.img.size ); 176 res.bearing[ key ] = ivec2( g.pos ); 177 imPaste( res.texture, ivec2( offset, 0 ), g.img ); 178 offset += g.img.size.w; 179 } 180 return res; 181 } 182 183 static ~this() 184 { 185 if( lib_inited && FT_Done_FreeType !is null ) 186 FT_Done_FreeType( ft ); 187 } 188 }