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