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 import std.traits; 12 13 alias CrdVector!2 imsize_t; 14 15 struct BitmapFont 16 { 17 ivec2[wchar] offset; 18 ivec2[wchar] size; 19 ivec2[wchar] bearing; 20 21 Image!2 texture; 22 } 23 24 class FTGlyphRenderException: Exception 25 { 26 @safe pure nothrow this( string msg, string file=__FILE__, size_t line=__LINE__ ) 27 { super( msg, file, line ); } 28 } 29 30 struct GlyphInfo 31 { 32 ivec2 pos, next; 33 @property ivec2 size() const { return ivec2( img.size ); } 34 Image!2 img; 35 } 36 37 struct GlyphParam 38 { 39 enum Flag 40 { 41 NONE = cast(ubyte)0, 42 BOLD = 0b0001, 43 ITALIC = 0b0010, 44 UNDERLINE = 0b0100, 45 STRIKED = 0b1000 46 } 47 48 ubyte flag = Flag.NONE; 49 uint height=12; 50 } 51 52 interface GlyphRender 53 { 54 void setParams( in GlyphParam p ); 55 @property ElemInfo imtype() const; 56 GlyphInfo render( wchar ch ); 57 BitmapFont generateBitmapFont( wstring ); 58 } 59 60 class FTGlyphRender : GlyphRender, ExternalMemoryManager 61 { 62 mixin EMM; 63 protected: 64 void selfDestroy() 65 { 66 if( FT_Done_Face !is null ) 67 FT_Done_Face( face ); 68 } 69 private: 70 static lib_inited = false; 71 static FT_Library ft; 72 73 static FTGlyphRender[string] openFTGR; 74 75 FT_Face face; 76 77 static this() 78 { 79 if( !lib_inited ) 80 { 81 DerelictFT.load(); 82 if( FT_Init_FreeType( &ft ) ) 83 throw new FTGlyphRenderException( "Couldn't init freetype library" ); 84 85 lib_inited = true; 86 } 87 } 88 89 this( string fontname ) 90 { 91 import std.file; 92 if( !fontname.exists ) 93 throw new FTGlyphRenderException( "Couldn't open font '" ~ fontname ~ "': file not exist" ); 94 95 bool loaderror = false; 96 foreach( i; 0 .. 100 ) 97 { 98 if( FT_New_Face( ft, fontname.dup.ptr, 0, &face ) ) loaderror = true; 99 else { loaderror = false; break; } 100 } 101 102 if( loaderror ) 103 throw new FTGlyphRenderException( "Couldn't open font '" ~ fontname ~ "': loading error" ); 104 105 if( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) 106 throw new FTGlyphRenderException( "Couldn't select unicode encoding" ); 107 } 108 109 public: 110 111 static GlyphRender get( string fontname ) 112 { 113 if( fontname !in openFTGR ) 114 openFTGR[fontname] = new FTGlyphRender( fontname ); 115 return openFTGR[fontname]; 116 } 117 118 void setParams( in GlyphParam p ) 119 { 120 FT_Set_Pixel_Sizes( face, 0, p.height ); 121 } 122 123 @property ElemInfo imtype() const { return ElemInfo( DataType.FLOAT, 1 ); } 124 125 GlyphInfo render( wchar ch ) 126 { 127 if( FT_Load_Char( face, cast(size_t)ch, FT_LOAD_RENDER ) ) 128 throw new FTGlyphRenderException( "Couldn't load char" ); 129 130 auto g = face.glyph; 131 132 ivec2 sz; 133 float[] img_data; 134 135 if( ch == ' ' ) 136 { 137 auto width = g.metrics.horiAdvance / 128.0;//TODO not proper way I think 138 sz = ivec2( width, g.bitmap.rows ); 139 img_data.length = sz.x * sz.y; 140 img_data[] = 0; 141 } 142 else 143 { 144 sz = ivec2( g.bitmap.width, g.bitmap.rows ); 145 img_data = amap!(a => a / 255.0f)(g.bitmap.buffer[0 .. sz.x * sz.y]); 146 } 147 148 return GlyphInfo( ivec2( g.bitmap_left, -g.bitmap_top ), 149 ivec2( cast(int)( g.advance.x >> 6 ), 150 cast(int)( g.advance.y >> 6 ) ), 151 Image!2( imsize_t(sz), imtype, img_data ) ); 152 } 153 154 BitmapFont generateBitmapFont( wstring chars ) 155 { 156 BitmapFont res; 157 GlyphInfo[wchar] glyphs; 158 foreach( c; chars ) 159 glyphs[c] = render( c ); 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 }