1 module des.fonts.ftrender; 2 3 import des.math.linear; 4 import des.il; 5 import des.util.logsys; 6 import des.util.arch.emm; 7 import des.util.stdext; 8 9 import derelict.freetype.ft; 10 import derelict.freetype.types; 11 12 import std.traits; 13 14 import des.fonts.base; 15 16 /// 17 class FTException : FontException 18 { 19 /// 20 FTError code; 21 /// 22 this( string msg, FTError code, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow 23 { 24 this.code = code; 25 super( msg, file, line ); 26 } 27 } 28 29 void checkFTCall(alias fnc, string info="", string file=__FILE__, size_t line=__LINE__,Args...)( Args args ) 30 { 31 auto err = cast(FTError)fnc(args); 32 auto fmtInfo = info ? ( format( " (%s)", info ) ) : ""; 33 if( err != FTError.NONE ) 34 throw new FTException( format( "'%s'%s fails: %s", fnc.stringof, fmtInfo, err ), err ); 35 } 36 37 class FTFontRender : FontRender, ExternalMemoryManager 38 { 39 mixin EMM; 40 41 protected: 42 43 void selfDestroy() 44 { 45 if( FT_Done_Face !is null ) 46 FT_Done_Face( face ); 47 } 48 49 private: 50 51 static lib_inited = false; 52 53 static FT_Library ft; 54 55 static FTFontRender[string] openRender; 56 57 static this() 58 { 59 if( !lib_inited ) 60 { 61 DerelictFT.load(); 62 checkFTCall!FT_Init_FreeType( &ft ); 63 lib_inited = true; 64 } 65 } 66 67 static ~this() 68 { 69 if( lib_inited && FT_Done_FreeType !is null ) 70 checkFTCall!FT_Done_FreeType( ft ); 71 } 72 73 protected: 74 75 this( string fontname ) 76 { 77 import std.file; 78 79 if( !fontname.exists ) 80 throw new FontException( "Couldn't open font '" ~ fontname ~ "': file not exist" ); 81 82 checkFTCall!FT_New_Face( ft, fontname.toStringz, 0, &face ); 83 84 checkFTCall!(FT_Select_Charmap,"unicode")( face, FT_ENCODING_UNICODE ); 85 } 86 87 FT_Face face; 88 89 FontRenderParam param; 90 91 static immutable ElemInfo imtype = ElemInfo( 1, DataType.UBYTE ); 92 93 public: 94 95 static FontRender get( string fontname ) 96 { 97 if( fontname !in openRender ) 98 openRender[fontname] = new FTFontRender( fontname ); 99 return openRender[fontname]; 100 } 101 102 void setParams( in FontRenderParam p ) { param = p; } 103 104 GlyphImage renderChar( wchar ch ) 105 { 106 checkFTCall!FT_Load_Char( face, cast(ulong)ch, FT_LOAD_RENDER ); 107 108 auto g = face.glyph; 109 110 ivec2 sz; 111 ubyte[] img_data; 112 113 if( ch == ' ' ) 114 { 115 auto width = g.metrics.horiAdvance >> 6; 116 sz = ivec2( width, g.bitmap.rows ); 117 img_data.length = sz.x * sz.y; 118 } 119 else 120 { 121 sz = ivec2( g.bitmap.width, g.bitmap.rows ); 122 img_data = g.bitmap.buffer[0 .. sz.x*sz.y].dup; 123 } 124 125 return GlyphImage( 126 Glyph( ""w ~ ch, 127 ivec2( g.bitmap_left, -g.bitmap_top ), 128 ivec2( g.advance.x >> 6, g.advance.y >> 6 ), 129 sz 130 ), 131 Image.external( sz, imtype, img_data ) 132 ); 133 } 134 135 BitmapFont generateBitmapFont( wstring chars ) 136 { 137 checkFTCall!FT_Set_Pixel_Sizes( face, 0, param.height ); 138 139 logger.Debug( param.height ); 140 141 BitmapFont res; 142 res.height = param.height; 143 144 GlyphImage[wchar] glyphs; 145 146 foreach( c; chars ) glyphs[c] = renderChar(c); 147 148 uint maxh = 0; 149 uint width = 0; 150 151 foreach( gi; glyphs ) 152 { 153 if( gi.image.size[1] > maxh ) 154 maxh = cast(uint)(gi.image.size[1]); 155 width += gi.image.size[0]; 156 } 157 158 res.image = Image( ivec2(width,maxh), imtype ); 159 160 auto offset = ivec2(0); 161 162 foreach( key, gi; glyphs ) 163 { 164 res.info[key] = BitmapGlyph( gi.glyph, offset ); 165 imCopy( res.image, offset, gi.image ); 166 offset += ivec2( gi.image.size[0], 0 ); 167 } 168 169 return res; 170 } 171 } 172 173 /// 174 enum FTError 175 { 176 NONE = 0x00, /// no error 177 178 CANNOT_OPEN_RESOURCE = 0x01, /// cannot open resource 179 UNKNOWN_FILE_FORMAT = 0x02, /// unknown file format 180 INVALID_FILE_FORMAT = 0x03, /// broken file 181 INVALID_VERSION = 0x04, /// invalid FreeType version 182 LOWER_MODULE_VERSION = 0x05, /// module version is too low 183 INVALID_ARGUMENT = 0x06, /// invalid argument 184 UNIMPLEMENTED_FEATURE = 0x07, /// unimplemented feature 185 INVALID_TABLE = 0x08, /// broken table 186 INVALID_OFFSET = 0x09, /// broken offset within table 187 ARRAY_TOO_LARGE = 0x0A, /// array allocation size too large 188 189 /* GLYPH/CHARACTER ERRORS */ 190 191 INVALID_GLYPH_INDEX = 0x10, /// invalid glyph index 192 INVALID_CHARACTER_CODE = 0x11, /// invalid character code 193 INVALID_GLYPH_FORMAT = 0x12, /// unsupported glyph image format 194 CANNOT_RENDER_GLYPH = 0x13, /// cannot render this glyph format 195 INVALID_OUTLINE = 0x14, /// invalid outline 196 INVALID_COMPOSITE = 0x15, /// invalid composite glyph 197 TOO_MANY_HINTS = 0x16, /// too many hints 198 INVALID_PIXEL_SIZE = 0x17, /// invalid pixel size 199 200 /* HANDLE ERRORS */ 201 202 INVALID_HANDLE = 0x20, /// invalid object handle 203 INVALID_LIBRARY_HANDLE = 0x21, /// invalid library handle 204 INVALID_DRIVER_HANDLE = 0x22, /// invalid module handle 205 INVALID_FACE_HANDLE = 0x23, /// invalid face handle 206 INVALID_SIZE_HANDLE = 0x24, /// invalid size handle 207 INVALID_SLOT_HANDLE = 0x25, /// invalid glyph slot handle 208 INVALID_CHARMAP_HANDLE = 0x26, /// invalid charmap handle 209 INVALID_CACHE_HANDLE = 0x27, /// invalid cache manager handle 210 INVALID_STREAM_HANDLE = 0x28, /// invalid stream handle 211 212 /* DRIVER ERRORS */ 213 214 TOO_MANY_DRIVERS = 0x30, /// too many modules 215 TOO_MANY_EXTENSIONS = 0x31, /// too many extensions 216 217 /* MEMORY ERRORS */ 218 219 OUT_OF_MEMORY = 0x40, /// out of memory 220 UNLISTED_OBJECT = 0x41, /// unlisted object 221 222 /* STREAM ERRORS */ 223 224 CANNOT_OPEN_STREAM = 0x51, /// cannot open stream 225 INVALID_STREAM_SEEK = 0x52, /// invalid stream seek 226 INVALID_STREAM_SKIP = 0x53, /// invalid stream skip 227 INVALID_STREAM_READ = 0x54, /// invalid stream read 228 INVALID_STREAM_OPERATION = 0x55, /// invalid stream operation 229 INVALID_FRAME_OPERATION = 0x56, /// invalid frame operation 230 NESTED_FRAME_ACCESS = 0x57, /// nested frame access 231 INVALID_FRAME_READ = 0x58, /// invalid frame read 232 233 /* RASTER ERRORS */ 234 235 RASTER_UNINITIALIZED = 0x60, /// raster uninitialized 236 RASTER_CORRUPTED = 0x61, /// raster corrupted 237 RASTER_OVERFLOW = 0x62, /// raster overflow 238 RASTER_NEGATIVE_HEIGHT = 0x63, /// negative height while rastering 239 240 /* CACHE ERRORS */ 241 242 TOO_MANY_CACHES = 0x70, /// too many registered caches 243 244 /* TRUETYPE AND SFNT ERRORS */ 245 246 INVALID_OPCODE = 0x80, /// invalid opcode 247 TOO_FEW_ARGUMENTS = 0x81, /// too few arguments 248 STACK_OVERFLOW = 0x82, /// stack overflow 249 CODE_OVERFLOW = 0x83, /// code overflow 250 BAD_ARGUMENT = 0x84, /// bad argument 251 DIVIDE_BY_ZERO = 0x85, /// division by zero 252 INVALID_REFERENCE = 0x86, /// invalid reference 253 DEBUG_OPCODE = 0x87, /// found debug opcode 254 ENDF_IN_EXEC_STREAM = 0x88, /// found ENDF opcode in execution stream 255 NESTED_DEFS = 0x89, /// nested DEFS 256 INVALID_CODERANGE = 0x8A, /// invalid code range 257 EXECUTION_TOO_LONG = 0x8B, /// execution context too long 258 TOO_MANY_FUNCTION_DEFS = 0x8C, /// too many function definitions 259 TOO_MANY_INSTRUCTION_DEFS = 0x8D, /// too many instruction definitions 260 TABLE_MISSING = 0x8E, /// SFNT font table missing 261 HORIZ_HEADER_MISSING = 0x8F, /// horizontal header (hhea) table missing 262 LOCATIONS_MISSING = 0x90, /// locations (loca) table missing 263 NAME_TABLE_MISSING = 0x91, /// name table missing 264 CMAP_TABLE_MISSING = 0x92, /// character map (cmap) table missing 265 HMTX_TABLE_MISSING = 0x93, /// horizontal metrics (hmtx) table missing 266 POST_TABLE_MISSING = 0x94, /// PostScript (post) table missing 267 INVALID_HORIZ_METRICS = 0x95, /// invalid horizontal metrics 268 INVALID_CHARMAP_FORMAT = 0x96, /// invalid character map (cmap) format 269 INVALID_PPEM = 0x97, /// invalid ppem value 270 INVALID_VERT_METRICS = 0x98, /// invalid vertical metrics 271 COULD_NOT_FIND_CONTEXT = 0x99, /// could not find context 272 INVALID_POST_TABLE_FORMAT = 0x9A, /// invalid PostScript (post) table format 273 INVALID_POST_TABLE = 0x9B, /// invalid PostScript (post) table 274 275 /* CFF, CID, AND TYPE 1 ERRORS */ 276 277 SYNTAX_ERROR = 0xA0, /// opcode syntax error 278 STACK_UNDERFLOW = 0xA1, /// argument stack underflow 279 IGNORE = 0xA2, /// ignore 280 NO_UNICODE_GLYPH_NAME = 0xA3, /// no Unicode glyph name found 281 282 283 /* BDF ERRORS */ 284 285 MISSING_STARTFONT_FIELD = 0xB0, /// `STARTFONT' field missing 286 MISSING_FONT_FIELD = 0xB1, /// `FONT' field missing 287 MISSING_SIZE_FIELD = 0xB2, /// `SIZE' field missing 288 MISSING_FONTBOUNDINGBOX_FIELD = 0xB3, /// `FONTBOUNDINGBOX' field missing 289 MISSING_CHARS_FIELD = 0xB4, /// `CHARS' field missing 290 MISSING_STARTCHAR_FIELD = 0xB5, /// `STARTCHAR' field missing 291 MISSING_ENCODING_FIELD = 0xB6, /// `ENCODING' field missing 292 MISSING_BBX_FIELD = 0xB7, /// `BBX' field missing 293 BBX_TOO_BIG = 0xB8, /// `BBX' too big 294 CORRUPTED_FONT_HEADER = 0xB9, /// Font header corrupted or missing fields 295 CORRUPTED_FONT_GLYPHS = 0xBA, /// Font glyphs corrupted or missing fields 296 }