1 /* *********************** 2 This module is no longer maintained, use arsd.ttf instead. 3 *************************/ 4 5 6 // stb_truetype.h - v0.6c - public domain 7 // authored from 2009-2012 by Sean Barrett / RAD Game Tools 8 // 9 // http://nothings.org/stb/stb_truetype.h 10 // 11 // port to D by adam d. ruppe. see the link above for more info about the lib and real author. 12 13 // here's some D convenience functions 14 module stb_truetype; 15 16 @system: 17 18 struct TtfFont { 19 stbtt_fontinfo font; 20 this(in ubyte[] data) { 21 load(data); 22 } 23 24 void load(in ubyte[] data) { 25 if(stbtt_InitFont(&font, data.ptr, stbtt_GetFontOffsetForIndex(data.ptr, 0)) == 0) 26 throw new Exception("load font problem"); 27 } 28 29 ubyte[] renderCharacter(dchar c, int size, out int width, out int height, float shift_x = 0.0, float shift_y = 0.0) { 30 auto ptr = stbtt_GetCodepointBitmapSubpixel(&font, 0.0,stbtt_ScaleForPixelHeight(&font, size), 31 shift_x, shift_y, c, &width, &height, null,null); 32 return ptr[0 .. width * height]; 33 } 34 35 void getStringSize(in char[] s, int size, out int width, out int height) { 36 float xpos=0; 37 38 auto scale = stbtt_ScaleForPixelHeight(&font, size); 39 int ascent, descent, line_gap; 40 stbtt_GetFontVMetrics(&font, &ascent,&descent,&line_gap); 41 auto baseline = cast(int) (ascent*scale); 42 43 import std.math; 44 45 int maxWidth; 46 47 foreach(i, dchar ch; s) { 48 int advance,lsb; 49 auto x_shift = xpos - floor(xpos); 50 stbtt_GetCodepointHMetrics(&font, ch, &advance, &lsb); 51 52 int x0, y0, x1, y1; 53 stbtt_GetCodepointBitmapBoxSubpixel(&font, ch, scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 54 55 maxWidth = cast(int)(xpos + x1); 56 57 xpos += (advance * scale); 58 if (i + 1 < s.length) 59 xpos += scale*stbtt_GetCodepointKernAdvance(&font, ch,s[i+1]); 60 } 61 62 width = maxWidth; 63 height = size; 64 } 65 66 ubyte[] renderString(in char[] s, int size, out int width, out int height) { 67 float xpos=0; 68 69 auto scale = stbtt_ScaleForPixelHeight(&font, size); 70 int ascent, descent, line_gap; 71 stbtt_GetFontVMetrics(&font, &ascent,&descent,&line_gap); 72 auto baseline = cast(int) (ascent*scale); 73 74 import std.math; 75 76 int swidth; 77 int sheight; 78 getStringSize(s, size, swidth, sheight); 79 auto screen = new ubyte[](swidth * sheight); 80 81 foreach(i, dchar ch; s) { 82 int advance,lsb; 83 auto x_shift = xpos - floor(xpos); 84 stbtt_GetCodepointHMetrics(&font, ch, &advance, &lsb); 85 int cw, cheight; 86 auto c = renderCharacter(ch, size, cw, cheight, x_shift, 0.0); 87 88 int x0, y0, x1, y1; 89 stbtt_GetCodepointBitmapBoxSubpixel(&font, ch, scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 90 91 int x = cast(int) xpos + x0; 92 int y = baseline + y0; 93 int cx = 0; 94 foreach(index, pixel; c) { 95 if(cx == cw) { 96 cx = 0; 97 y++; 98 x = cast(int) xpos + x0; 99 } 100 auto offset = swidth * y + x; 101 if(offset >= screen.length) 102 break; 103 int val = (cast(int) pixel * (255 - screen[offset]) / 255); 104 if(val > 255) 105 val = 255; 106 screen[offset] += cast(ubyte)(val); 107 x++; 108 cx++; 109 } 110 111 //stbtt_MakeCodepointBitmapSubpixel(&font, &screen[(baseline + y0) * swidth + cast(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale, x_shift,0, ch); 112 // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong 113 // because this API is really for baking character bitmaps into textures. if you want to render 114 // a sequence of characters, you really need to render each bitmap to a temp buffer, then 115 // "alpha blend" that into the working buffer 116 xpos += (advance * scale); 117 if (i + 1 < s.length) 118 xpos += scale*stbtt_GetCodepointKernAdvance(&font, ch,s[i+1]); 119 } 120 121 width = swidth; 122 height = sheight; 123 124 return screen; 125 } 126 127 // ~this() {} 128 } 129 130 131 // test program 132 /+ 133 int main(string[] args) 134 { 135 import std.conv; 136 import arsd.simpledisplay; 137 int c = (args.length > 1 ? to!int(args[1]) : 'a'), s = (args.length > 2 ? to!int(args[2]) : 20); 138 import std.file; 139 140 auto font = TtfFont(cast(ubyte[]) /*import("sans-serif.ttf"));//*/std.file.read(args.length > 3 ? args[3] : "sans-serif.ttf")); 141 142 int w, h; 143 auto bitmap = font.renderString("Hejlqo, world!qMpj", s, w, h); 144 auto img = new Image(w, h); 145 146 for (int j=0; j < h; ++j) { 147 for (int i=0; i < w; ++i) 148 img.putPixel(i, j, Color(0, (bitmap[j*w+i] > 128) ? 255 : 0, 0)); 149 } 150 img.displayImage(); 151 return 0; 152 } 153 +/ 154 155 156 157 158 // STB_IMAGE FOLLOWS 159 160 161 alias ubyte stbtt_uint8; 162 alias byte stbtt_int8; 163 alias ushort stbtt_uint16; 164 alias short stbtt_int16; 165 alias uint stbtt_uint32; 166 alias int stbtt_int32; 167 168 alias char[(stbtt_int32.sizeof)==4 ? 1 : -1] stbtt__check_size32; 169 alias char[(stbtt_int16.sizeof)==2 ? 1 : -1] stbtt__check_size16; 170 171 static import core.stdc.stdlib; 172 alias STBTT_sort = core.stdc.stdlib.qsort; 173 174 static import std.math; 175 int STBTT_ifloor(float x) { return cast(int) std.math.floor(x); } 176 int STBTT_iceil(float x) { return cast(int) std.math.ceil(x); } 177 178 void* STBTT_malloc(size_t x, in void* u) { import core.memory; return GC.malloc(x); /*return core.stdc.stdlib.malloc(x);*/ } 179 void STBTT_free(void* x,in void* u) { /*return core.stdc.stdlib.free(x);*/ } 180 181 static import core.stdc.string; 182 alias STBTT_strlen = core.stdc..string.strlen; 183 184 alias STBTT_memcpy = core.stdc..string.memcpy; 185 alias STBTT_memset = core.stdc..string.memset; 186 187 // //////////////////////////////////////////////////////////////////////////// 188 // 189 // TEXTURE BAKING API 190 // 191 // If you use this API, you only have to call two functions ever. 192 // 193 194 struct stbtt_bakedchar 195 { 196 ushort x0,y0,x1,y1; // coordinates of bbox in bitmap 197 float xoff,yoff,xadvance; 198 } 199 200 // if return is positive, the first unused row of the bitmap 201 // if return is negative, returns the negative of the number of characters that fit 202 // if return is 0, no characters fit and no rows were used 203 // This uses a very crappy packing. 204 205 struct stbtt_aligned_quad 206 { 207 float x0,y0,s0,t0; // top-left 208 float x1,y1,s1,t1; // bottom-right 209 } 210 211 // Call GetBakedQuad with char_index = 'character - first_char', and it 212 // creates the quad you need to draw and advances the current position. 213 // 214 // The coordinate system used assumes y increases downwards. 215 // 216 // Characters will extend both above and below the current position; 217 // see discussion of "BASELINE" above. 218 // 219 // It's inefficient; you might want to c&p it and optimize it. 220 221 222 // //////////////////////////////////////////////////////////////////////////// 223 // 224 // FONT LOADING 225 // 226 // 227 228 // Each .ttf/.ttc file may have more than one font. Each font has a sequential 229 // index number starting from 0. Call this function to get the font offset for 230 // a given index; it returns -1 if the index is out of range. A regular .ttf 231 // file will only define one font and it always be at offset 0, so it will 232 // return '0' for index 0, and -1 for all other indices. You can just skip 233 // this step if you know it's that kind of font. 234 235 236 // The following structure is defined publically so you can declare one on 237 // the stack or as a global or etc, but you should treat it as opaque. 238 struct stbtt_fontinfo 239 { 240 void * userdata; 241 ubyte * data; // pointer to .ttf file 242 int fontstart; // offset of start of font 243 244 int numGlyphs; // number of glyphs, needed for range checking 245 246 int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf 247 int index_map; // a cmap mapping for our chosen character encoding 248 int indexToLocFormat; // format needed to map from glyph index to glyph 249 } 250 251 // Given an offset into the file that defines a font, this function builds 252 // the necessary cached info for the rest of the system. You must allocate 253 // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't 254 // need to do anything special to free it, because the contents are pure 255 // value data with no additional data structures. Returns 0 on failure. 256 257 258 // //////////////////////////////////////////////////////////////////////////// 259 // 260 // GLYPH SHAPES (you probably don't need these, but they have to go before 261 // the bitmaps for C declaration-order reasons) 262 // 263 264 enum { 265 STBTT_vmove=1, 266 STBTT_vline, 267 STBTT_vcurve 268 } 269 270 alias short stbtt_vertex_type; 271 struct stbtt_vertex 272 { 273 stbtt_vertex_type x,y,cx,cy; 274 ubyte type,padding; 275 } 276 277 // @TODO: don't expose this structure 278 struct stbtt__bitmap 279 { 280 int w,h,stride; 281 ubyte *pixels; 282 } 283 284 // //////////////////////////////////////////////////////////////////////////// 285 // 286 // Finding the right font... 287 // 288 // You should really just solve this offline, keep your own tables 289 // of what font is what, and don't try to get it out of the .ttf file. 290 // That's because getting it out of the .ttf file is really hard, because 291 // the names in the file can appear in many possible encodings, in many 292 // possible languages, and e.g. if you need a case-insensitive comparison, 293 // the details of that depend on the encoding & language in a complex way 294 // (actually underspecified in truetype, but also gigantic). 295 // 296 // But you can use the provided functions in two possible ways: 297 // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on 298 // unicode-encoded names to try to find the font you want; 299 // you can run this before calling stbtt_InitFont() 300 // 301 // stbtt_GetFontNameString() lets you get any of the various strings 302 // from the file yourself and do your own comparisons on them. 303 // You have to have called stbtt_InitFont() first. 304 305 306 enum STBTT_MACSTYLE_DONTCARE = 0; 307 enum STBTT_MACSTYLE_BOLD = 1; 308 enum STBTT_MACSTYLE_ITALIC = 2; 309 enum STBTT_MACSTYLE_UNDERSCORE = 4; 310 enum STBTT_MACSTYLE_NONE = 8; // <= not same as 0, this makes us check the bitfield is 0 311 312 // returns the string (which may be big-endian double byte, e.g. for unicode) 313 // and puts the length in bytes in *length. 314 // 315 // some of the values for the IDs are below; for more see the truetype spec: 316 // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html 317 // http://www.microsoft.com/typography/otspec/name.htm 318 319 enum { // platformID 320 STBTT_PLATFORM_ID_UNICODE =0, 321 STBTT_PLATFORM_ID_MAC =1, 322 STBTT_PLATFORM_ID_ISO =2, 323 STBTT_PLATFORM_ID_MICROSOFT =3 324 }; 325 326 enum { // encodingID for STBTT_PLATFORM_ID_UNICODE 327 STBTT_UNICODE_EID_UNICODE_1_0 =0, 328 STBTT_UNICODE_EID_UNICODE_1_1 =1, 329 STBTT_UNICODE_EID_ISO_10646 =2, 330 STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, 331 STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 332 }; 333 334 enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT 335 STBTT_MS_EID_SYMBOL =0, 336 STBTT_MS_EID_UNICODE_BMP =1, 337 STBTT_MS_EID_SHIFTJIS =2, 338 STBTT_MS_EID_UNICODE_FULL =10 339 }; 340 341 enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes 342 STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, 343 STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, 344 STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, 345 STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 346 }; 347 348 enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... 349 // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs 350 STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, 351 STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, 352 STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, 353 STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, 354 STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, 355 STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D 356 }; 357 358 enum { // languageID for STBTT_PLATFORM_ID_MAC 359 STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, 360 STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, 361 STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, 362 STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , 363 STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , 364 STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, 365 STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 366 }; 367 368 // ///////////////////////////////////////////////////////////////////////////// 369 // ///////////////////////////////////////////////////////////////////////////// 370 // // 371 // // IMPLEMENTATION 372 // // 373 // // 374 375 // //////////////////////////////////////////////////////////////////////// 376 // 377 // accessors to parse data from file 378 // 379 380 // on platforms that don't allow misaligned reads, if we want to allow 381 // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE 382 383 stbtt_uint8 ttBYTE(in stbtt_uint8* p) { return * cast(stbtt_uint8 *) (p); } 384 stbtt_int8 ttCHAR(in stbtt_uint8* p) { return * cast(stbtt_int8 *) (p); } 385 stbtt_int32 ttFixed(in stbtt_uint8* p) { return ttLONG(p); } 386 387 version(BigEndian) { 388 stbtt_uint16 ttUSHORT(in stbtt_uint8* p) { return * cast(stbtt_uint16 *) (p); } 389 stbtt_int16 ttSHORT(in stbtt_uint8* p) { return * cast(stbtt_int16 *) (p); } 390 stbtt_uint32 ttULONG(in stbtt_uint8* p) { return * cast(stbtt_uint32 *) (p); } 391 stbtt_int32 ttLONG(in stbtt_uint8* p) { return * cast(stbtt_int32 *) (p); } 392 } else { 393 stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } 394 stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return cast(short)(p[0]*256 + p[1]); } 395 stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 396 stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 397 } 398 399 400 bool stbtt_tag4(in stbtt_uint8* p,int c0, int c1, int c2, int c3) { return ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)); } 401 bool stbtt_tag(in stbtt_uint8* p, in char* str) { return stbtt_tag4(p,str[0],str[1],str[2],str[3]); } 402 403 static int stbtt__isfont(const stbtt_uint8 *font) 404 { 405 // check the version number 406 if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 407 if (stbtt_tag(font, "typ1".ptr)) return 1; // TrueType with type 1 font -- we don't support this! 408 if (stbtt_tag(font, "OTTO".ptr)) return 1; // OpenType with CFF 409 if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 410 return 0; 411 } 412 413 // @OPTIMIZE: binary search 414 static stbtt_uint32 stbtt__find_table(const(stbtt_uint8) *data, stbtt_uint32 fontstart, const char *tag) 415 { 416 stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); 417 stbtt_uint32 tabledir = fontstart + 12; 418 stbtt_int32 i; 419 for (i=0; i < num_tables; ++i) { 420 stbtt_uint32 loc = tabledir + 16*i; 421 if (stbtt_tag(data+loc+0, tag)) 422 return ttULONG(data+loc+8); 423 } 424 return 0; 425 } 426 427 int stbtt_GetFontOffsetForIndex(const ubyte *font_collection, int index) 428 { 429 // if it's just a font, there's only one valid index 430 if (stbtt__isfont(font_collection)) 431 return index == 0 ? 0 : -1; 432 433 // check if it's a TTC 434 if (stbtt_tag(font_collection, "ttcf")) { 435 // version 1? 436 if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { 437 stbtt_int32 n = ttLONG(font_collection+8); 438 if (index >= n) 439 return -1; 440 return ttULONG(font_collection+12+index*14); 441 } 442 } 443 return -1; 444 } 445 446 int stbtt_InitFont(stbtt_fontinfo *info, const ubyte *data2, int fontstart) 447 { 448 stbtt_uint8 *data = cast(stbtt_uint8 *) data2; 449 stbtt_uint32 cmap, t; 450 stbtt_int32 i,numTables; 451 452 info.data = data; 453 info.fontstart = fontstart; 454 455 cmap = stbtt__find_table(data, fontstart, "cmap"); // required 456 info.loca = stbtt__find_table(data, fontstart, "loca"); // required 457 info.head = stbtt__find_table(data, fontstart, "head"); // required 458 info.glyf = stbtt__find_table(data, fontstart, "glyf"); // required 459 info.hhea = stbtt__find_table(data, fontstart, "hhea"); // required 460 info.hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required 461 info.kern = stbtt__find_table(data, fontstart, "kern"); // not required 462 if (!cmap || !info.loca || !info.head || !info.glyf || !info.hhea || !info.hmtx) 463 return 0; 464 465 t = stbtt__find_table(data, fontstart, "maxp"); 466 if (t) 467 info.numGlyphs = ttUSHORT(data+t+4); 468 else 469 info.numGlyphs = 0xffff; 470 471 // find a cmap encoding table we understand *now* to avoid searching 472 // later. (todo: could make this installable) 473 // the same regardless of glyph. 474 numTables = ttUSHORT(data + cmap + 2); 475 info.index_map = 0; 476 for (i=0; i < numTables; ++i) { 477 stbtt_uint32 encoding_record = cmap + 4 + 8 * i; 478 // find an encoding we understand: 479 switch(ttUSHORT(data+encoding_record)) { 480 default: break;//assert(0); 481 case STBTT_PLATFORM_ID_MICROSOFT: 482 switch (ttUSHORT(data+encoding_record+2)) { 483 default: assert(0); 484 case STBTT_MS_EID_UNICODE_BMP: 485 case STBTT_MS_EID_UNICODE_FULL: 486 // MS/Unicode 487 info.index_map = cmap + ttULONG(data+encoding_record+4); 488 break; 489 } 490 break; 491 } 492 } 493 if (info.index_map == 0) 494 return 0; 495 496 info.indexToLocFormat = ttUSHORT(data+info.head + 50); 497 return 1; 498 } 499 500 int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) 501 { 502 const(stbtt_uint8) *data = info.data; 503 stbtt_uint32 index_map = info.index_map; 504 505 stbtt_uint16 format = ttUSHORT(data + index_map + 0); 506 if (format == 0) { // apple byte encoding 507 stbtt_int32 bytes = ttUSHORT(data + index_map + 2); 508 if (unicode_codepoint < bytes-6) 509 return ttBYTE(data + index_map + 6 + unicode_codepoint); 510 return 0; 511 } else if (format == 6) { 512 stbtt_uint32 first = ttUSHORT(data + index_map + 6); 513 stbtt_uint32 count = ttUSHORT(data + index_map + 8); 514 if (cast(stbtt_uint32) unicode_codepoint >= first && cast(stbtt_uint32) unicode_codepoint < first+count) 515 return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); 516 return 0; 517 } else if (format == 2) { 518 assert(0); // @TODO: high-byte mapping for japanese/chinese/korean 519 //return 0; 520 } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges 521 stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; 522 stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; 523 stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); 524 stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; 525 stbtt_uint16 item, offset, start, end; 526 527 // do a binary search of the segments 528 stbtt_uint32 endCount = index_map + 14; 529 stbtt_uint32 search = endCount; 530 531 if (unicode_codepoint > 0xffff) 532 return 0; 533 534 // they lie from endCount .. endCount + segCount 535 // but searchRange is the nearest power of two, so... 536 if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) 537 search += rangeShift*2; 538 539 // now decrement to bias correctly to find smallest 540 search -= 2; 541 while (entrySelector) { 542 stbtt_uint16 start2, end2; 543 searchRange >>= 1; 544 start2 = ttUSHORT(data + search + 2 + segcount*2 + 2); 545 end2 = ttUSHORT(data + search + 2); 546 start2 = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); 547 end2 = ttUSHORT(data + search + searchRange*2); 548 if (unicode_codepoint > end2) 549 search += searchRange*2; 550 --entrySelector; 551 } 552 search += 2; 553 554 item = cast(stbtt_uint16) ((search - endCount) >> 1); 555 556 assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); 557 start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); 558 end = ttUSHORT(data + index_map + 14 + 2 + 2*item); 559 if (unicode_codepoint < start) 560 return 0; 561 562 offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); 563 if (offset == 0) 564 return cast(stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); 565 566 return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); 567 } else if (format == 12 || format == 13) { 568 stbtt_uint32 ngroups = ttULONG(data+index_map+12); 569 stbtt_int32 low,high; 570 low = 0; high = cast(stbtt_int32)ngroups; 571 // Binary search the right group. 572 while (low < high) { 573 stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high 574 stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); 575 stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); 576 if (cast(stbtt_uint32) unicode_codepoint < start_char) 577 high = mid; 578 else if (cast(stbtt_uint32) unicode_codepoint > end_char) 579 low = mid+1; 580 else { 581 stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); 582 if (format == 12) 583 return start_glyph + unicode_codepoint-start_char; 584 else // format == 13 585 return start_glyph; 586 } 587 } 588 return 0; // not found 589 } 590 // @TODO 591 assert(0); 592 // return 0; 593 } 594 595 int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) 596 { 597 return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); 598 } 599 600 static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) 601 { 602 v.type = type; 603 v.x = cast(stbtt_int16) x; 604 v.y = cast(stbtt_int16) y; 605 v.cx = cast(stbtt_int16) cx; 606 v.cy = cast(stbtt_int16) cy; 607 } 608 609 static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) 610 { 611 int g1,g2; 612 613 if (glyph_index >= info.numGlyphs) return -1; // glyph index out of range 614 if (info.indexToLocFormat >= 2) return -1; // unknown index.glyph map format 615 616 if (info.indexToLocFormat == 0) { 617 g1 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2) * 2; 618 g2 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2 + 2) * 2; 619 } else { 620 g1 = info.glyf + ttULONG (info.data + info.loca + glyph_index * 4); 621 g2 = info.glyf + ttULONG (info.data + info.loca + glyph_index * 4 + 4); 622 } 623 624 return g1==g2 ? -1 : g1; // if length is 0, return -1 625 } 626 627 int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) 628 { 629 int g = stbtt__GetGlyfOffset(info, glyph_index); 630 if (g < 0) return 0; 631 632 if (x0) *x0 = ttSHORT(info.data + g + 2); 633 if (y0) *y0 = ttSHORT(info.data + g + 4); 634 if (x1) *x1 = ttSHORT(info.data + g + 6); 635 if (y1) *y1 = ttSHORT(info.data + g + 8); 636 return 1; 637 } 638 639 int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) 640 { 641 return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); 642 } 643 644 int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) 645 { 646 stbtt_int16 numberOfContours; 647 int g = stbtt__GetGlyfOffset(info, glyph_index); 648 if (g < 0) return 1; 649 numberOfContours = ttSHORT(info.data + g); 650 return numberOfContours == 0; 651 } 652 653 static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, 654 stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) 655 { 656 if (start_off) { 657 if (was_off) 658 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); 659 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); 660 } else { 661 if (was_off) 662 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); 663 else 664 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); 665 } 666 return num_vertices; 667 } 668 669 int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) 670 { 671 stbtt_int16 numberOfContours; 672 const(stbtt_uint8) *endPtsOfContours; 673 const(stbtt_uint8) *data = info.data; 674 stbtt_vertex *vertices=null; 675 int num_vertices=0; 676 int g = stbtt__GetGlyfOffset(info, glyph_index); 677 678 *pvertices = null; 679 680 if (g < 0) return 0; 681 682 numberOfContours = ttSHORT(data + g); 683 684 if (numberOfContours > 0) { 685 stbtt_uint8 flags=0,flagcount; 686 stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; 687 stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; 688 const(stbtt_uint8) *points; 689 endPtsOfContours = (data + g + 10); 690 ins = ttUSHORT(data + g + 10 + numberOfContours * 2); 691 points = data + g + 10 + numberOfContours * 2 + 2 + ins; 692 693 n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); 694 695 m = n + 2*numberOfContours; // a loose bound on how many vertices we might need 696 vertices = cast(stbtt_vertex *) STBTT_malloc(m * vertices[0].sizeof, info.userdata); 697 if (vertices is null) 698 return 0; 699 700 next_move = 0; 701 flagcount=0; 702 703 // in first pass, we load uninterpreted data into the allocated array 704 // above, shifted to the end of the array so we won't overwrite it when 705 // we create our final data starting from the front 706 707 off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated 708 709 // first load flags 710 711 for (i=0; i < n; ++i) { 712 if (flagcount == 0) { 713 flags = *points++; 714 if (flags & 8) 715 flagcount = *points++; 716 } else 717 --flagcount; 718 vertices[off+i].type = flags; 719 } 720 721 // now load x coordinates 722 x=0; 723 for (i=0; i < n; ++i) { 724 flags = vertices[off+i].type; 725 if (flags & 2) { 726 stbtt_int16 dx = *points++; 727 x += (flags & 16) ? dx : -cast(int)dx; // ??? 728 } else { 729 if (!(flags & 16)) { 730 x = x + cast(stbtt_int16) (points[0]*256 + points[1]); 731 points += 2; 732 } 733 } 734 vertices[off+i].x = cast(stbtt_int16) x; 735 } 736 737 // now load y coordinates 738 y=0; 739 for (i=0; i < n; ++i) { 740 flags = vertices[off+i].type; 741 if (flags & 4) { 742 stbtt_int16 dy = *points++; 743 y += (flags & 32) ? dy : -cast(int)dy; // ??? 744 } else { 745 if (!(flags & 32)) { 746 y = y + cast(stbtt_int16) (points[0]*256 + points[1]); 747 points += 2; 748 } 749 } 750 vertices[off+i].y = cast(stbtt_int16) y; 751 } 752 753 // now convert them to our format 754 num_vertices=0; 755 sx = sy = cx = cy = scx = scy = 0; 756 for (i=0; i < n; ++i) { 757 flags = vertices[off+i].type; 758 x = cast(stbtt_int16) vertices[off+i].x; 759 y = cast(stbtt_int16) vertices[off+i].y; 760 761 if (next_move == i) { 762 if (i != 0) 763 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 764 765 // now start the new one 766 start_off = !(flags & 1); 767 if (start_off) { 768 // if we start off with an off-curve point, then when we need to find a point on the curve 769 // where we can start, and we need to save some state for when we wraparound. 770 scx = x; 771 scy = y; 772 if (!(vertices[off+i+1].type & 1)) { 773 // next point is also a curve point, so interpolate an on-point curve 774 sx = (x + cast(stbtt_int32) vertices[off+i+1].x) >> 1; 775 sy = (y + cast(stbtt_int32) vertices[off+i+1].y) >> 1; 776 } else { 777 // otherwise just use the next point as our start point 778 sx = cast(stbtt_int32) vertices[off+i+1].x; 779 sy = cast(stbtt_int32) vertices[off+i+1].y; 780 ++i; // we're using point i+1 as the starting point, so skip it 781 } 782 } else { 783 sx = x; 784 sy = y; 785 } 786 stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); 787 was_off = 0; 788 next_move = 1 + ttUSHORT(endPtsOfContours+j*2); 789 ++j; 790 } else { 791 if (!(flags & 1)) { // if it's a curve 792 if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint 793 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); 794 cx = x; 795 cy = y; 796 was_off = 1; 797 } else { 798 if (was_off) 799 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); 800 else 801 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); 802 was_off = 0; 803 } 804 } 805 } 806 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 807 } else if (numberOfContours == -1) { 808 // Compound shapes. 809 int more = 1; 810 const(stbtt_uint8) *comp = data + g + 10; 811 num_vertices = 0; 812 vertices = null; 813 while (more) { 814 stbtt_uint16 flags, gidx; 815 int comp_num_verts = 0, i; 816 stbtt_vertex* comp_verts = null, tmp = null; 817 float[6] mtx = [1,0,0,1,0,0]; 818 float m, n; 819 820 flags = ttSHORT(comp); comp+=2; 821 gidx = ttSHORT(comp); comp+=2; 822 823 if (flags & 2) { // XY values 824 if (flags & 1) { // shorts 825 mtx[4] = ttSHORT(comp); comp+=2; 826 mtx[5] = ttSHORT(comp); comp+=2; 827 } else { 828 mtx[4] = ttCHAR(comp); comp+=1; 829 mtx[5] = ttCHAR(comp); comp+=1; 830 } 831 } 832 else { 833 // @TODO handle matching point 834 assert(0); 835 } 836 if (flags & (1<<3)) { // WE_HAVE_A_SCALE 837 mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 838 mtx[1] = mtx[2] = 0; 839 } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE 840 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 841 mtx[1] = mtx[2] = 0; 842 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 843 } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO 844 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 845 mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; 846 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 847 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 848 } 849 850 // Find transformation scales. 851 m = cast(float) std.math.sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 852 n = cast(float) std.math.sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); 853 854 // Get indexed glyph. 855 comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); 856 if (comp_num_verts > 0) { 857 // Transform vertices. 858 for (i = 0; i < comp_num_verts; ++i) { 859 stbtt_vertex* v = &comp_verts[i]; 860 stbtt_vertex_type x,y; 861 x=v.x; y=v.y; 862 v.x = cast(stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 863 v.y = cast(stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 864 x=v.cx; y=v.cy; 865 v.cx = cast(stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 866 v.cy = cast(stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 867 } 868 // Append vertices. 869 tmp = cast(stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*stbtt_vertex.sizeof, info.userdata); 870 if (!tmp) { 871 if (vertices) STBTT_free(vertices, info.userdata); 872 if (comp_verts) STBTT_free(comp_verts, info.userdata); 873 return 0; 874 } 875 if (num_vertices > 0) core.stdc..string.memcpy(tmp, vertices, num_vertices*stbtt_vertex.sizeof); 876 core.stdc..string.memcpy(tmp+num_vertices, comp_verts, comp_num_verts*stbtt_vertex.sizeof); 877 if (vertices) STBTT_free(vertices, info.userdata); 878 vertices = tmp; 879 STBTT_free(comp_verts, info.userdata); 880 num_vertices += comp_num_verts; 881 } 882 // More components ? 883 more = flags & (1<<5); 884 } 885 } else if (numberOfContours < 0) { 886 // @TODO other compound variations? 887 assert(0); 888 } else { 889 // numberOfCounters == 0, do nothing 890 } 891 892 *pvertices = vertices; 893 return num_vertices; 894 } 895 896 void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) 897 { 898 stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info.data+info.hhea + 34); 899 if (glyph_index < numOfLongHorMetrics) { 900 if (advanceWidth) *advanceWidth = ttSHORT(info.data + info.hmtx + 4*glyph_index); 901 if (leftSideBearing) *leftSideBearing = ttSHORT(info.data + info.hmtx + 4*glyph_index + 2); 902 } else { 903 if (advanceWidth) *advanceWidth = ttSHORT(info.data + info.hmtx + 4*(numOfLongHorMetrics-1)); 904 if (leftSideBearing) *leftSideBearing = ttSHORT(info.data + info.hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); 905 } 906 } 907 908 int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) 909 { 910 const(stbtt_uint8) *data = info.data + info.kern; 911 stbtt_uint32 needle, straw; 912 int l, r, m; 913 914 // we only look at the first table. it must be 'horizontal' and format 0. 915 if (!info.kern) 916 return 0; 917 if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 918 return 0; 919 if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 920 return 0; 921 922 l = 0; 923 r = ttUSHORT(data+10) - 1; 924 needle = glyph1 << 16 | glyph2; 925 while (l <= r) { 926 m = (l + r) >> 1; 927 straw = ttULONG(data+18+(m*6)); // note: unaligned read 928 if (needle < straw) 929 r = m - 1; 930 else if (needle > straw) 931 l = m + 1; 932 else 933 return ttSHORT(data+22+(m*6)); 934 } 935 return 0; 936 } 937 938 int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) 939 { 940 if (!info.kern) // if no kerning table, don't waste time looking up both codepoint.glyphs 941 return 0; 942 return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); 943 } 944 945 void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) 946 { 947 stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); 948 } 949 950 void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) 951 { 952 if (ascent ) *ascent = ttSHORT(info.data+info.hhea + 4); 953 if (descent) *descent = ttSHORT(info.data+info.hhea + 6); 954 if (lineGap) *lineGap = ttSHORT(info.data+info.hhea + 8); 955 } 956 957 void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) 958 { 959 *x0 = ttSHORT(info.data + info.head + 36); 960 *y0 = ttSHORT(info.data + info.head + 38); 961 *x1 = ttSHORT(info.data + info.head + 40); 962 *y1 = ttSHORT(info.data + info.head + 42); 963 } 964 965 float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) 966 { 967 int fheight = ttSHORT(info.data + info.hhea + 4) - ttSHORT(info.data + info.hhea + 6); 968 return cast(float) height / fheight; 969 } 970 971 float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) 972 { 973 int unitsPerEm = ttUSHORT(info.data + info.head + 18); 974 return pixels / unitsPerEm; 975 } 976 977 void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) 978 { 979 STBTT_free(v, info.userdata); 980 } 981 982 // //////////////////////////////////////////////////////////////////////////// 983 // 984 // antialiasing software rasterizer 985 // 986 987 void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) 988 { 989 int x0,y0,x1,y1; 990 if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) 991 x0=y0=x1=y1=0; // e.g. space character 992 // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? 993 if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x + shift_x); 994 if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y + shift_y); 995 if (ix1) *ix1 = STBTT_iceil (x1 * scale_x + shift_x); 996 if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y + shift_y); 997 } 998 void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) 999 { 1000 stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); 1001 } 1002 1003 void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) 1004 { 1005 stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); 1006 } 1007 1008 void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) 1009 { 1010 stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); 1011 } 1012 1013 struct stbtt__edge { 1014 float x0,y0, x1,y1; 1015 int invert; 1016 } 1017 1018 struct stbtt__active_edge 1019 { 1020 int x,dx; 1021 float ey; 1022 stbtt__active_edge *next; 1023 int valid; 1024 } 1025 1026 enum FIXSHIFT = 10; 1027 enum FIX = (1 << FIXSHIFT); 1028 enum FIXMASK = (FIX-1); 1029 1030 static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, in void *userdata) 1031 { 1032 stbtt__active_edge *z = cast(stbtt__active_edge *) STBTT_malloc((stbtt__active_edge).sizeof, userdata); // @TODO: make a pool of these!!! 1033 float dxdy = (e.x1 - e.x0) / (e.y1 - e.y0); 1034 assert(e.y0 <= start_point); 1035 if (!z) return z; 1036 // round dx down to avoid going too far 1037 if (dxdy < 0) 1038 z.dx = -STBTT_ifloor(FIX * -dxdy); 1039 else 1040 z.dx = STBTT_ifloor(FIX * dxdy); 1041 z.x = STBTT_ifloor(FIX * (e.x0 + dxdy * (start_point - e.y0))); 1042 z.x -= off_x * FIX; 1043 z.ey = e.y1; 1044 z.next = null; 1045 z.valid = e.invert ? 1 : -1; 1046 return z; 1047 } 1048 1049 // note: this routine clips fills that extend off the edges... ideally this 1050 // wouldn't happen, but it could happen if the truetype glyph bounding boxes 1051 // are wrong, or if the user supplies a too-small bitmap 1052 static void stbtt__fill_active_edges(ubyte *scanline, int len, stbtt__active_edge *e, int max_weight) 1053 { 1054 // non-zero winding fill 1055 int x0=0, w=0; 1056 1057 while (e) { 1058 if (w == 0) { 1059 // if we're currently at zero, we need to record the edge start point 1060 x0 = e.x; w += e.valid; 1061 } else { 1062 int x1 = e.x; w += e.valid; 1063 // if we went to zero, we need to draw 1064 if (w == 0) { 1065 int i = x0 >> FIXSHIFT; 1066 int j = x1 >> FIXSHIFT; 1067 1068 if (i < len && j >= 0) { 1069 if (i == j) { 1070 // x0,x1 are the same pixel, so compute combined coverage 1071 scanline[i] = cast(ubyte)(scanline[i] + cast(stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT)); 1072 } else { 1073 if (i >= 0) // add antialiasing for x0 1074 scanline[i] = cast(ubyte)( scanline[i] + cast(stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT)); 1075 else 1076 i = -1; // clip 1077 1078 if (j < len) // add antialiasing for x1 1079 scanline[j] = cast(ubyte)(scanline[j] + cast(stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT)); 1080 else 1081 j = len; // clip 1082 1083 for (++i; i < j; ++i) // fill pixels between x0 and x1 1084 scanline[i] = cast(ubyte)(scanline[i] + cast(stbtt_uint8) max_weight); 1085 } 1086 } 1087 } 1088 } 1089 1090 e = e.next; 1091 } 1092 } 1093 1094 static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, in void *userdata) 1095 { 1096 stbtt__active_edge *active = null; 1097 int y,j=0; 1098 int max_weight = (255 / vsubsample); // weight per vertical scanline 1099 int s; // vertical subsample index 1100 ubyte[512] scanline_data; 1101 ubyte *scanline; 1102 1103 if (result.w > 512) 1104 scanline = cast(ubyte *) STBTT_malloc(result.w, userdata); 1105 else 1106 scanline = scanline_data.ptr; 1107 1108 y = off_y * vsubsample; 1109 e[n].y0 = (off_y + result.h) * cast(float) vsubsample + 1; 1110 1111 while (j < result.h) { 1112 STBTT_memset(scanline, 0, result.w); 1113 for (s=0; s < vsubsample; ++s) { 1114 // find center of pixel for this scanline 1115 float scan_y = y + 0.5f; 1116 stbtt__active_edge **step = &active; 1117 1118 // update all active edges; 1119 // remove all active edges that terminate before the center of this scanline 1120 while (*step) { 1121 stbtt__active_edge * z = *step; 1122 if (z.ey <= scan_y) { 1123 *step = z.next; // delete from list 1124 assert(z.valid); 1125 z.valid = 0; 1126 STBTT_free(z, userdata); 1127 } else { 1128 z.x += z.dx; // advance to position for current scanline 1129 step = &((*step).next); // advance through list 1130 } 1131 } 1132 1133 // resort the list if needed 1134 for(;;) { 1135 int changed=0; 1136 step = &active; 1137 while (*step && (*step).next) { 1138 if ((*step).x > (*step).next.x) { 1139 stbtt__active_edge *t = *step; 1140 stbtt__active_edge *q = t.next; 1141 1142 t.next = q.next; 1143 q.next = t; 1144 *step = q; 1145 changed = 1; 1146 } 1147 step = &(*step).next; 1148 } 1149 if (!changed) break; 1150 } 1151 1152 // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 1153 while (e.y0 <= scan_y) { 1154 if (e.y1 > scan_y) { 1155 stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); 1156 // find insertion point 1157 if (active == null) 1158 active = z; 1159 else if (z.x < active.x) { 1160 // insert at front 1161 z.next = active; 1162 active = z; 1163 } else { 1164 // find thing to insert AFTER 1165 stbtt__active_edge *p = active; 1166 while (p.next && p.next.x < z.x) 1167 p = p.next; 1168 // at this point, p.next.x is NOT < z.x 1169 z.next = p.next; 1170 p.next = z; 1171 } 1172 } 1173 ++e; 1174 } 1175 1176 // now process all active edges in XOR fashion 1177 if (active) 1178 stbtt__fill_active_edges(scanline, result.w, active, max_weight); 1179 1180 ++y; 1181 } 1182 STBTT_memcpy(result.pixels + j * result.stride, scanline, result.w); 1183 ++j; 1184 } 1185 1186 while (active) { 1187 stbtt__active_edge *z = active; 1188 active = active.next; 1189 STBTT_free(z, userdata); 1190 } 1191 1192 if (scanline != scanline_data.ptr) 1193 STBTT_free(scanline, userdata); 1194 } 1195 1196 extern(C) int stbtt__edge_compare(const void *p, const void *q) 1197 { 1198 stbtt__edge *a = cast(stbtt__edge *) p; 1199 stbtt__edge *b = cast(stbtt__edge *) q; 1200 1201 if (a.y0 < b.y0) return -1; 1202 if (a.y0 > b.y0) return 1; 1203 return 0; 1204 } 1205 1206 struct stbtt__point 1207 { 1208 float x,y; 1209 } 1210 1211 static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, in void *userdata) 1212 { 1213 float y_scale_inv = invert ? -scale_y : scale_y; 1214 stbtt__edge *e; 1215 int n,i,j,k,m; 1216 int vsubsample = result.h < 8 ? 15 : 5; 1217 // vsubsample should divide 255 evenly; otherwise we won't reach full opacity 1218 1219 // now we have to blow out the windings into explicit edge lists 1220 n = 0; 1221 for (i=0; i < windings; ++i) 1222 n += wcount[i]; 1223 1224 e = cast(stbtt__edge *) STBTT_malloc((*e).sizeof * (n+1), userdata); // add an extra one as a sentinel 1225 if (e is null) return; 1226 n = 0; 1227 1228 m=0; 1229 for (i=0; i < windings; ++i) { 1230 stbtt__point *p = pts + m; 1231 m += wcount[i]; 1232 j = wcount[i]-1; 1233 for (k=0; k < wcount[i]; j=k++) { 1234 int a=k,b=j; 1235 // skip the edge if horizontal 1236 if (p[j].y == p[k].y) 1237 continue; 1238 // add edge from j to k to the list 1239 e[n].invert = 0; 1240 if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { 1241 e[n].invert = 1; 1242 a=j;b=k; 1243 } 1244 e[n].x0 = p[a].x * scale_x + shift_x; 1245 e[n].y0 = p[a].y * y_scale_inv * vsubsample + shift_y; 1246 e[n].x1 = p[b].x * scale_x + shift_x; 1247 e[n].y1 = p[b].y * y_scale_inv * vsubsample + shift_y; 1248 ++n; 1249 } 1250 } 1251 1252 // now sort the edges by their highest point (should snap to integer, and then by x) 1253 STBTT_sort(cast(void*) e, n, (e[0]).sizeof, &stbtt__edge_compare); 1254 1255 // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule 1256 stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); 1257 1258 STBTT_free(e, userdata); 1259 } 1260 1261 static void stbtt__add_point(stbtt__point *points, int n, float x, float y) 1262 { 1263 if (!points) return; // during first pass, it's unallocated 1264 points[n].x = x; 1265 points[n].y = y; 1266 } 1267 1268 // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 1269 static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) 1270 { 1271 // midpoint 1272 float mx = (x0 + 2*x1 + x2)/4; 1273 float my = (y0 + 2*y1 + y2)/4; 1274 // versus directly drawn line 1275 float dx = (x0+x2)/2 - mx; 1276 float dy = (y0+y2)/2 - my; 1277 if (n > 16) // 65536 segments on one curve better be enough! 1278 return 1; 1279 if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA 1280 stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); 1281 stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); 1282 } else { 1283 stbtt__add_point(points, *num_points,x2,y2); 1284 *num_points = *num_points+1; 1285 } 1286 return 1; 1287 } 1288 1289 // returns number of contours 1290 stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, in void *userdata) 1291 { 1292 stbtt__point *points=null; 1293 int num_points=0; 1294 1295 float objspace_flatness_squared = objspace_flatness * objspace_flatness; 1296 int i,n=0,start=0, pass; 1297 1298 // count how many "moves" there are to get the contour count 1299 for (i=0; i < num_verts; ++i) 1300 if (vertices[i].type == STBTT_vmove) 1301 ++n; 1302 1303 *num_contours = n; 1304 if (n == 0) return null; 1305 1306 *contour_lengths = cast(int *) STBTT_malloc((**contour_lengths).sizeof * n, userdata); 1307 1308 if (*contour_lengths is null) { 1309 *num_contours = 0; 1310 return null; 1311 } 1312 1313 // make two passes through the points so we don't need to realloc 1314 for (pass=0; pass < 2; ++pass) { 1315 float x=0,y=0; 1316 if (pass == 1) { 1317 points = cast(stbtt__point *) STBTT_malloc(num_points * (points[0]).sizeof, userdata); 1318 if (points == null) goto error; 1319 } 1320 num_points = 0; 1321 n= -1; 1322 for (i=0; i < num_verts; ++i) { 1323 switch (vertices[i].type) { 1324 default: assert(0); 1325 case STBTT_vmove: 1326 // start the next contour 1327 if (n >= 0) 1328 (*contour_lengths)[n] = num_points - start; 1329 ++n; 1330 start = num_points; 1331 1332 x = vertices[i].x; y = vertices[i].y; 1333 stbtt__add_point(points, num_points++, x,y); 1334 break; 1335 case STBTT_vline: 1336 x = vertices[i].x, y = vertices[i].y; 1337 stbtt__add_point(points, num_points++, x, y); 1338 break; 1339 case STBTT_vcurve: 1340 stbtt__tesselate_curve(points, &num_points, x,y, 1341 vertices[i].cx, vertices[i].cy, 1342 vertices[i].x, vertices[i].y, 1343 objspace_flatness_squared, 0); 1344 x = vertices[i].x; y = vertices[i].y; 1345 break; 1346 } 1347 } 1348 (*contour_lengths)[n] = num_points - start; 1349 } 1350 1351 return points; 1352 error: 1353 STBTT_free(points, userdata); 1354 STBTT_free(*contour_lengths, userdata); 1355 *contour_lengths = null; 1356 *num_contours = 0; 1357 return null; 1358 } 1359 1360 void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, in void *userdata) 1361 { 1362 float scale = scale_x > scale_y ? scale_y : scale_x; 1363 int winding_count; 1364 int *winding_lengths; 1365 stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); 1366 if (windings) { 1367 stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); 1368 STBTT_free(winding_lengths, userdata); 1369 STBTT_free(windings, userdata); 1370 } 1371 } 1372 1373 void stbtt_FreeBitmap(ubyte *bitmap, void *userdata) 1374 { 1375 STBTT_free(bitmap, userdata); 1376 } 1377 1378 ubyte *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) 1379 { 1380 int ix0,iy0,ix1,iy1; 1381 stbtt__bitmap gbm; 1382 stbtt_vertex *vertices; 1383 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1384 1385 if (scale_x == 0) scale_x = scale_y; 1386 if (scale_y == 0) { 1387 if (scale_x == 0) return null; 1388 scale_y = scale_x; 1389 } 1390 1391 stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); 1392 1393 // now we get the size 1394 gbm.w = (ix1 - ix0); 1395 gbm.h = (iy1 - iy0); 1396 gbm.pixels = null; // in case we error 1397 1398 if (width ) *width = gbm.w; 1399 if (height) *height = gbm.h; 1400 if (xoff ) *xoff = ix0; 1401 if (yoff ) *yoff = iy0; 1402 1403 if (gbm.w && gbm.h) { 1404 gbm.pixels = cast(ubyte *) STBTT_malloc(gbm.w * gbm.h, info.userdata); 1405 if (gbm.pixels) { 1406 gbm.stride = gbm.w; 1407 1408 stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info.userdata); 1409 } 1410 } 1411 STBTT_free(vertices, info.userdata); 1412 return gbm.pixels; 1413 } 1414 1415 ubyte *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) 1416 { 1417 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); 1418 } 1419 1420 void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, ubyte *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) 1421 { 1422 int ix0,iy0; 1423 stbtt_vertex *vertices; 1424 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1425 stbtt__bitmap gbm; 1426 1427 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,null,null); 1428 gbm.pixels = output; 1429 gbm.w = out_w; 1430 gbm.h = out_h; 1431 gbm.stride = out_stride; 1432 1433 if (gbm.w && gbm.h) 1434 stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info.userdata); 1435 1436 STBTT_free(vertices, info.userdata); 1437 } 1438 1439 void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, ubyte *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) 1440 { 1441 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); 1442 } 1443 1444 ubyte *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 1445 { 1446 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 1447 } 1448 1449 void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, ubyte *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) 1450 { 1451 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); 1452 } 1453 1454 ubyte *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 1455 { 1456 return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 1457 } 1458 1459 void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, ubyte *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) 1460 { 1461 stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); 1462 } 1463 1464 // //////////////////////////////////////////////////////////////////////////// 1465 // 1466 // bitmap baking 1467 // 1468 // This is SUPER-CRAPPY packing to keep source code small 1469 1470 extern int stbtt_BakeFontBitmap(const ubyte *data, int offset, // font location (use offset=0 for plain .ttf) 1471 float pixel_height, // height of font in pixels 1472 ubyte *pixels, int pw, int ph, // bitmap to be filled in 1473 int first_char, int num_chars, // characters to bake 1474 stbtt_bakedchar *chardata) 1475 { 1476 float scale; 1477 int x,y,bottom_y, i; 1478 stbtt_fontinfo f; 1479 stbtt_InitFont(&f, data, offset); 1480 STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 1481 x=y=1; 1482 bottom_y = 1; 1483 1484 scale = stbtt_ScaleForPixelHeight(&f, pixel_height); 1485 1486 for (i=0; i < num_chars; ++i) { 1487 int advance, lsb, x0,y0,x1,y1,gw,gh; 1488 int g = stbtt_FindGlyphIndex(&f, first_char + i); 1489 stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); 1490 stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); 1491 gw = x1-x0; 1492 gh = y1-y0; 1493 if (x + gw + 1 >= pw) 1494 { y = bottom_y; x = 1; } // advance to next row 1495 if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row 1496 return -i; 1497 assert(x+gw < pw); 1498 assert(y+gh < ph); 1499 stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); 1500 chardata[i].x0 = cast(stbtt_int16) x; 1501 chardata[i].y0 = cast(stbtt_int16) y; 1502 chardata[i].x1 = cast(stbtt_int16) (x + gw); 1503 chardata[i].y1 = cast(stbtt_int16) (y + gh); 1504 chardata[i].xadvance = scale * advance; 1505 chardata[i].xoff = cast(float) x0; 1506 chardata[i].yoff = cast(float) y0; 1507 x = x + gw + 2; 1508 if (y+gh+2 > bottom_y) 1509 bottom_y = y+gh+2; 1510 } 1511 return bottom_y; 1512 } 1513 1514 void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) 1515 { 1516 float d3d_bias = opengl_fillrule ? 0 : -0.5f; 1517 float ipw = 1.0f / pw, iph = 1.0f / ph; 1518 stbtt_bakedchar *b = chardata + char_index; 1519 int round_x = STBTT_ifloor((*xpos + b.xoff) + 0.5); 1520 int round_y = STBTT_ifloor((*ypos + b.yoff) + 0.5); 1521 1522 q.x0 = round_x + d3d_bias; 1523 q.y0 = round_y + d3d_bias; 1524 q.x1 = round_x + b.x1 - b.x0 + d3d_bias; 1525 q.y1 = round_y + b.y1 - b.y0 + d3d_bias; 1526 1527 q.s0 = b.x0 * ipw; 1528 q.t0 = b.y0 * iph; 1529 q.s1 = b.x1 * ipw; 1530 q.t1 = b.y1 * iph; 1531 1532 *xpos += b.xadvance; 1533 } 1534 1535 // //////////////////////////////////////////////////////////////////////////// 1536 // 1537 // font name matching -- recommended not to use this 1538 // 1539 1540 // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 1541 static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const(stbtt_uint8) *s2, stbtt_int32 len2) 1542 { 1543 stbtt_int32 i=0; 1544 1545 // convert utf16 to utf8 and compare the results while converting 1546 while (len2) { 1547 stbtt_uint16 ch = s2[0]*256 + s2[1]; 1548 if (ch < 0x80) { 1549 if (i >= len1) return -1; 1550 if (s1[i++] != ch) return -1; 1551 } else if (ch < 0x800) { 1552 if (i+1 >= len1) return -1; 1553 if (s1[i++] != 0xc0 + (ch >> 6)) return -1; 1554 if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; 1555 } else if (ch >= 0xd800 && ch < 0xdc00) { 1556 stbtt_uint32 c; 1557 stbtt_uint16 ch2 = s2[2]*256 + s2[3]; 1558 if (i+3 >= len1) return -1; 1559 c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; 1560 if (s1[i++] != 0xf0 + (c >> 18)) return -1; 1561 if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; 1562 if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; 1563 if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; 1564 s2 += 2; // plus another 2 below 1565 len2 -= 2; 1566 } else if (ch >= 0xdc00 && ch < 0xe000) { 1567 return -1; 1568 } else { 1569 if (i+2 >= len1) return -1; 1570 if (s1[i++] != 0xe0 + (ch >> 12)) return -1; 1571 if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; 1572 if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; 1573 } 1574 s2 += 2; 1575 len2 -= 2; 1576 } 1577 return i; 1578 } 1579 1580 int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) 1581 { 1582 return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix(cast(const stbtt_uint8*) s1, len1,cast (const stbtt_uint8*) s2, len2); 1583 } 1584 1585 // returns results in whatever encoding you request... but note that 2-byte encodings 1586 // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare 1587 const(char) *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) 1588 { 1589 stbtt_int32 i,count,stringOffset; 1590 const(stbtt_uint8) *fc = font.data; 1591 stbtt_uint32 offset = font.fontstart; 1592 stbtt_uint32 nm = stbtt__find_table(fc, offset, "name".ptr); 1593 if (!nm) return null; 1594 1595 count = ttUSHORT(fc+nm+2); 1596 stringOffset = nm + ttUSHORT(fc+nm+4); 1597 for (i=0; i < count; ++i) { 1598 stbtt_uint32 loc = nm + 6 + 12 * i; 1599 if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) 1600 && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { 1601 *length = ttUSHORT(fc+loc+8); 1602 return cast(const(char) *) (fc+stringOffset+ttUSHORT(fc+loc+10)); 1603 } 1604 } 1605 return null; 1606 } 1607 1608 static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) 1609 { 1610 stbtt_int32 i; 1611 stbtt_int32 count = ttUSHORT(fc+nm+2); 1612 stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); 1613 1614 for (i=0; i < count; ++i) { 1615 stbtt_uint32 loc = nm + 6 + 12 * i; 1616 stbtt_int32 id = ttUSHORT(fc+loc+6); 1617 if (id == target_id) { 1618 // find the encoding 1619 stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); 1620 1621 // is this a Unicode encoding? 1622 if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { 1623 stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); 1624 1625 // check if there's a prefix match 1626 stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); 1627 if (matchlen >= 0) { 1628 // check for target_id+1 immediately following, with same encoding & language 1629 if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { 1630 stbtt_int32 slen2 = ttUSHORT(fc+loc+12+8), off2 = ttUSHORT(fc+loc+12+10); 1631 if (slen2 == 0) { 1632 if (matchlen == nlen) 1633 return 1; 1634 } else if (matchlen < nlen && name[matchlen] == ' ') { 1635 ++matchlen; 1636 if (stbtt_CompareUTF8toUTF16_bigendian(cast(char*) (name+matchlen), nlen-matchlen, cast(char*)(fc+stringOffset+off2),slen2)) 1637 return 1; 1638 } 1639 } else { 1640 // if nothing immediately following 1641 if (matchlen == nlen) 1642 return 1; 1643 } 1644 } 1645 } 1646 1647 // @TODO handle other encodings 1648 } 1649 } 1650 return 0; 1651 } 1652 1653 static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) 1654 { 1655 stbtt_int32 nlen = cast(stbtt_int32) STBTT_strlen(cast(char *) name); 1656 stbtt_uint32 nm,hd; 1657 if (!stbtt__isfont(fc+offset)) return 0; 1658 1659 // check italics/bold/underline flags in macStyle... 1660 if (flags) { 1661 hd = stbtt__find_table(fc, offset, "head"); 1662 if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; 1663 } 1664 1665 nm = stbtt__find_table(fc, offset, "name"); 1666 if (!nm) return 0; 1667 1668 if (flags) { 1669 // if we checked the macStyle flags, then just check the family and ignore the subfamily 1670 if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; 1671 if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; 1672 if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 1673 } else { 1674 if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; 1675 if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; 1676 if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 1677 } 1678 1679 return 0; 1680 } 1681 1682 int stbtt_FindMatchingFont(const ubyte *font_collection, const char *name_utf8, stbtt_int32 flags) 1683 { 1684 stbtt_int32 i; 1685 for (i=0;;++i) { 1686 stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); 1687 if (off < 0) return off; 1688 if (stbtt__matches(cast(stbtt_uint8 *) font_collection, off, cast(stbtt_uint8*) name_utf8, flags)) 1689 return off; 1690 } 1691 }