1 /// D translation of stb_truetype v0.7 by Sean Barrett 2 /// More information on http://nothings.org/stb/stb_truetype.h 3 /// Removed: 4 /// - texture baking API 5 /// - font finding in the TTF itself. Make sure there is only one font in the TTF. 6 module dplug.graphics.stb_truetype; 7 8 import core.stdc.stdlib : malloc, free; 9 import core.stdc.string : memcpy, memset; 10 import core.stdc.math : floorf, ceilf; 11 12 import std.math : ceil, floor, sqrt; 13 14 import dplug.core.nogc; 15 import dplug.core.vec; 16 17 int ifloor(float x) nothrow @nogc 18 { 19 return cast(int)(floorf(x)); 20 } 21 22 int iceil(float x) nothrow @nogc 23 { 24 return cast(int)(ceilf(x)); 25 } 26 27 /// The following structure is defined publically so you can declare one on 28 /// the stack or as a global or etc, but you should treat it as opaque. 29 struct stbtt_fontinfo 30 { 31 const(ubyte) * data; // pointer to .ttf file 32 int fontstart; // offset of start of font 33 int numGlyphs; // number of glyphs, needed for range checking 34 int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf 35 int index_map; // a cmap mapping for our chosen character encoding 36 int indexToLocFormat; // format needed to map from glyph index to glyph 37 38 Vec!stbtt__edge edgeBuf; // edge buffer, used in rasterizing 39 Vec!stbtt__edge edgeScratchBuf; // scratch buffer, used for sorting edges. 40 } 41 42 43 enum STBTT_vmove = 1, 44 STBTT_vline = 2, 45 STBTT_vcurve = 3; 46 47 alias stbtt_vertex_type = short; 48 struct stbtt_vertex 49 { 50 stbtt_vertex_type x,y,cx,cy; 51 ubyte type, padding; 52 } 53 54 struct stbtt__bitmap 55 { 56 int w,h,stride; 57 ubyte *pixels; 58 } 59 enum // platformID 60 STBTT_PLATFORM_ID_UNICODE =0, 61 STBTT_PLATFORM_ID_MAC =1, 62 STBTT_PLATFORM_ID_ISO =2, 63 STBTT_PLATFORM_ID_MICROSOFT =3; 64 65 enum // encodingID for STBTT_PLATFORM_ID_MICROSOFT 66 STBTT_MS_EID_SYMBOL =0, 67 STBTT_MS_EID_UNICODE_BMP =1, 68 STBTT_MS_EID_SHIFTJIS =2, 69 STBTT_MS_EID_UNICODE_FULL =10; 70 71 // Accessors to parse data from file 72 73 ubyte ttBYTE(const(ubyte)* p) nothrow @nogc 74 { 75 return *p; 76 } 77 78 byte ttCHAR(const(ubyte)* p) nothrow @nogc 79 { 80 return *p; 81 } 82 83 int ttFixed(const(ubyte)* p) nothrow @nogc 84 { 85 return ttLONG(p); 86 } 87 88 ushort ttUSHORT(const(ubyte) *p) nothrow @nogc 89 { 90 return p[0]*256 + p[1]; 91 } 92 93 short ttSHORT(const(ubyte) *p) nothrow @nogc 94 { 95 return cast(short)(p[0]*256 + p[1]); 96 } 97 98 uint ttULONG(const(ubyte) *p) nothrow @nogc 99 { 100 return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; 101 } 102 103 int ttLONG(const(ubyte) *p) nothrow @nogc 104 { 105 return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; 106 } 107 108 bool stbtt_tag4(const(ubyte) *p, ubyte c0, ubyte c1, ubyte c2, ubyte c3) nothrow @nogc 109 { 110 return (p[0] == c0 && p[1] == c1 && p[2] == c2 && p[3] == c3); 111 } 112 113 bool stbtt_tag(const(ubyte) *p, string s) nothrow @nogc 114 { 115 return stbtt_tag4(p, s[0], s[1], s[2], s[3]); 116 } 117 118 bool stbtt__isfont(const(ubyte) *font) nothrow @nogc 119 { 120 // check the version number 121 if (stbtt_tag4(font, '1',0,0,0)) 122 return true; // TrueType 1 123 if (stbtt_tag(font, "typ1")) 124 return true; // TrueType with type 1 font -- we don't support this! 125 if (stbtt_tag(font, "OTTO")) 126 return true; // OpenType with CFF 127 if (stbtt_tag4(font, 0,1,0,0)) 128 return true; // OpenType 1.0 129 return false; 130 } 131 132 // @OPTIMIZE: binary search 133 uint stbtt__find_table(const(ubyte)* data, uint fontstart, string tag) nothrow @nogc 134 { 135 int num_tables = ttUSHORT(data+fontstart+4); 136 uint tabledir = fontstart + 12; 137 for (int i=0; i < num_tables; ++i) { 138 uint loc = tabledir + 16*i; 139 if (stbtt_tag(data+loc+0, tag)) 140 return ttULONG(data+loc+8); 141 } 142 return 0; 143 } 144 145 /// Each .ttf/.ttc file may have more than one font. Each font has a sequential 146 /// index number starting from 0. Call this function to get the font offset for 147 /// a given index; it returns -1 if the index is out of range. A regular .ttf 148 /// file will only define one font and it always be at offset 0, so it will 149 /// return '0' for index 0, and -1 for all other indices. You can just skip 150 /// this step if you know it's that kind of font. 151 int stbtt_GetFontOffsetForIndex(const(ubyte)* font_collection, int index) nothrow @nogc 152 { 153 // if it's just a font, there's only one valid index 154 if (stbtt__isfont(font_collection)) 155 return index == 0 ? 0 : -1; 156 157 // check if it's a TTC 158 if (stbtt_tag(font_collection, "ttcf")) { 159 // version 1? 160 if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { 161 int n = ttLONG(font_collection+8); 162 if (index >= n) 163 return -1; 164 return ttULONG(font_collection+12+index*14); 165 } 166 } 167 return -1; 168 } 169 170 /// Given an offset into the file that defines a font, this function builds 171 /// the necessary cached info for the rest of the system. You must allocate 172 /// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. 173 int stbtt_InitFont(stbtt_fontinfo* info, const(ubyte)* data2, int fontstart) nothrow @nogc 174 { 175 const(ubyte) *data = data2; 176 uint cmap, t; 177 int i,numTables; 178 179 info.data = data; 180 info.fontstart = fontstart; 181 182 cmap = stbtt__find_table(data, fontstart, "cmap"); // required 183 info.loca = stbtt__find_table(data, fontstart, "loca"); // required 184 info.head = stbtt__find_table(data, fontstart, "head"); // required 185 info.glyf = stbtt__find_table(data, fontstart, "glyf"); // required 186 info.hhea = stbtt__find_table(data, fontstart, "hhea"); // required 187 info.hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required 188 info.kern = stbtt__find_table(data, fontstart, "kern"); // not required 189 if (!cmap || !info.loca || !info.head || !info.glyf || !info.hhea || !info.hmtx) 190 return 0; 191 192 t = stbtt__find_table(data, fontstart, "maxp"); 193 if (t) 194 info.numGlyphs = ttUSHORT(data+t+4); 195 else 196 info.numGlyphs = 0xffff; 197 198 // find a cmap encoding table we understand *now* to avoid searching 199 // later. (todo: could make this installable) 200 // the same regardless of glyph. 201 numTables = ttUSHORT(data + cmap + 2); 202 info.index_map = 0; 203 for (i=0; i < numTables; ++i) { 204 uint encoding_record = cmap + 4 + 8 * i; 205 // find an encoding we understand: 206 switch(ttUSHORT(data+encoding_record)) 207 { 208 case STBTT_PLATFORM_ID_MICROSOFT: 209 switch (ttUSHORT(data+encoding_record+2)) 210 { 211 case STBTT_MS_EID_UNICODE_BMP: 212 case STBTT_MS_EID_UNICODE_FULL: 213 // MS/Unicode 214 info.index_map = cmap + ttULONG(data+encoding_record+4); 215 break; 216 default: 217 assert(0); 218 } 219 break; 220 default: 221 break; 222 } 223 } 224 if (info.index_map == 0) 225 return 0; 226 227 info.indexToLocFormat = ttUSHORT(data+info.head + 50); 228 info.edgeBuf = makeVec!stbtt__edge(); 229 info.edgeScratchBuf = makeVec!stbtt__edge(); 230 return 1; 231 } 232 233 void stbtt_FreeFont(stbtt_fontinfo* info) nothrow @nogc 234 { 235 destroyNoGC(info.edgeBuf); 236 destroyNoGC(info.edgeScratchBuf); 237 } 238 239 /// If you're going to perform multiple operations on the same character 240 /// and you want a speed-up, call this function with the character you're 241 /// going to process, then use glyph-based functions instead of the 242 /// codepoint-based functions. 243 int stbtt_FindGlyphIndex(const(stbtt_fontinfo) *info, int unicode_codepoint) nothrow @nogc 244 { 245 const(ubyte)* data = info.data; 246 uint index_map = info.index_map; 247 248 ushort format = ttUSHORT(data + index_map + 0); 249 if (format == 0) { // apple byte encoding 250 int bytes = ttUSHORT(data + index_map + 2); 251 if (unicode_codepoint < bytes-6) 252 return ttBYTE(data + index_map + 6 + unicode_codepoint); 253 return 0; 254 } else if (format == 6) { 255 uint first = ttUSHORT(data + index_map + 6); 256 uint count = ttUSHORT(data + index_map + 8); 257 if (cast(uint) unicode_codepoint >= first && cast(uint)unicode_codepoint < first+count) 258 return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); 259 return 0; 260 } else if (format == 2) { 261 assert(0); // @TODO: high-byte mapping for japanese/chinese/korean 262 } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges 263 ushort segcount = ttUSHORT(data+index_map+6) >> 1; 264 ushort searchRange = ttUSHORT(data+index_map+8) >> 1; 265 ushort entrySelector = ttUSHORT(data+index_map+10); 266 ushort rangeShift = ttUSHORT(data+index_map+12) >> 1; 267 ushort item, offset, start, end; 268 269 // do a binary search of the segments 270 uint endCount = index_map + 14; 271 uint search = endCount; 272 273 if (unicode_codepoint > 0xffff) 274 return 0; 275 276 // they lie from endCount .. endCount + segCount 277 // but searchRange is the nearest power of two, so... 278 if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) 279 search += rangeShift*2; 280 281 // now decrement to bias correctly to find smallest 282 search -= 2; 283 while (entrySelector) { 284 ushort start2, end2; 285 searchRange >>= 1; 286 start2 = ttUSHORT(data + search + 2 + segcount*2 + 2); 287 end2 = ttUSHORT(data + search + 2); 288 start2 = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); 289 end2 = ttUSHORT(data + search + searchRange*2); 290 if (unicode_codepoint > end2) 291 search += searchRange*2; 292 --entrySelector; 293 } 294 search += 2; 295 296 item = cast(ushort) ((search - endCount) >> 1); 297 298 assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); 299 start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); 300 end = ttUSHORT(data + index_map + 14 + 2 + 2*item); 301 if (unicode_codepoint < start) 302 return 0; 303 304 offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); 305 if (offset == 0) 306 return cast(ushort) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); 307 308 return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); 309 } else if (format == 12 || format == 13) { 310 uint ngroups = ttULONG(data+index_map+12); 311 int low,high; 312 low = 0; 313 high = ngroups; 314 // Binary search the right group. 315 while (low < high) { 316 int mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high 317 uint start_char = ttULONG(data+index_map+16+mid*12); 318 uint end_char = ttULONG(data+index_map+16+mid*12+4); 319 if (unicode_codepoint < start_char) 320 high = mid; 321 else if (unicode_codepoint > end_char) 322 low = mid+1; 323 else { 324 uint start_glyph = ttULONG(data+index_map+16+mid*12+8); 325 if (format == 12) 326 return start_glyph + unicode_codepoint-start_char; 327 else // format == 13 328 return start_glyph; 329 } 330 } 331 return 0; // not found 332 } 333 // @TODO 334 assert(0); 335 } 336 337 /// Returns: Number of vertices and fills *vertices with the pointer to them. 338 /// These are expressed in "unscaled" coordinates. 339 int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) nothrow @nogc 340 { 341 return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); 342 } 343 344 void stbtt_setvertex(stbtt_vertex *v, ubyte type, int x, int y, int cx, int cy) nothrow @nogc 345 { 346 v.type = type; 347 v.x = cast(short) x; 348 v.y = cast(short) y; 349 v.cx = cast(short) cx; 350 v.cy = cast(short) cy; 351 } 352 353 int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) nothrow @nogc 354 { 355 int g1,g2; 356 357 if (glyph_index >= info.numGlyphs) return -1; // glyph index out of range 358 if (info.indexToLocFormat >= 2) return -1; // unknown index.glyph map format 359 360 if (info.indexToLocFormat == 0) { 361 g1 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2) * 2; 362 g2 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2 + 2) * 2; 363 } else { 364 g1 = info.glyf + ttULONG (info.data + info.loca + glyph_index * 4); 365 g2 = info.glyf + ttULONG (info.data + info.loca + glyph_index * 4 + 4); 366 } 367 368 return g1==g2 ? -1 : g1; // if length is 0, return -1 369 } 370 371 /// As above, but takes one or more glyph indices for greater efficiency 372 int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) nothrow @nogc 373 { 374 int g = stbtt__GetGlyfOffset(info, glyph_index); 375 if (g < 0) return 0; 376 377 if (x0) *x0 = ttSHORT(info.data + g + 2); 378 if (y0) *y0 = ttSHORT(info.data + g + 4); 379 if (x1) *x1 = ttSHORT(info.data + g + 6); 380 if (y1) *y1 = ttSHORT(info.data + g + 8); 381 return 1; 382 } 383 384 /// Gets the bounding box of the visible part of the glyph, in unscaled coordinates 385 int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) nothrow @nogc 386 { 387 return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); 388 } 389 390 /// Returns: non-zero if nothing is drawn for this glyph 391 int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) nothrow @nogc 392 { 393 short numberOfContours; 394 int g = stbtt__GetGlyfOffset(info, glyph_index); 395 if (g < 0) return 1; 396 numberOfContours = ttSHORT(info.data + g); 397 return numberOfContours == 0; 398 } 399 400 int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, 401 int sx, int sy, int scx, int scy, int cx, int cy) nothrow @nogc 402 { 403 if (start_off) { 404 if (was_off) 405 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); 406 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); 407 } else { 408 if (was_off) 409 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); 410 else 411 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); 412 } 413 return num_vertices; 414 } 415 416 /// Returns: Number of vertices and fills *vertices with the pointer to them. 417 /// These are expressed in "unscaled" coordinates. 418 int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) nothrow @nogc 419 { 420 short numberOfContours; 421 const(ubyte)* endPtsOfContours; 422 const(ubyte)* data = info.data; 423 stbtt_vertex* vertices = null; 424 int num_vertices=0; 425 int g = stbtt__GetGlyfOffset(info, glyph_index); 426 427 *pvertices = null; 428 429 if (g < 0) return 0; 430 431 numberOfContours = ttSHORT(data + g); 432 433 if (numberOfContours > 0) { 434 ubyte flags=0,flagcount; 435 int ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; 436 int x,y,cx,cy,sx,sy, scx,scy; 437 const(ubyte)* points; 438 endPtsOfContours = (data + g + 10); 439 ins = ttUSHORT(data + g + 10 + numberOfContours * 2); 440 points = data + g + 10 + numberOfContours * 2 + 2 + ins; 441 442 n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); 443 444 m = n + 2*numberOfContours; // a loose bound on how many vertices we might need 445 vertices = cast(stbtt_vertex *) malloc(m * stbtt_vertex.sizeof); 446 if (vertices == null) 447 return 0; 448 449 next_move = 0; 450 flagcount=0; 451 452 // in first pass, we load uninterpreted data into the allocated array 453 // above, shifted to the end of the array so we won't overwrite it when 454 // we create our final data starting from the front 455 456 off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated 457 458 // first load flags 459 460 for (i=0; i < n; ++i) { 461 if (flagcount == 0) { 462 flags = *points++; 463 if (flags & 8) 464 flagcount = *points++; 465 } else 466 --flagcount; 467 vertices[off+i].type = flags; 468 } 469 470 // now load x coordinates 471 x=0; 472 for (i=0; i < n; ++i) { 473 flags = vertices[off+i].type; 474 if (flags & 2) { 475 short dx = *points++; 476 x += (flags & 16) ? dx : (-cast(int)dx); 477 } else { 478 if (!(flags & 16)) { 479 x = x + cast(short) (points[0]*256 + points[1]); 480 points += 2; 481 } 482 } 483 vertices[off+i].x = cast(short) x; 484 } 485 486 // now load y coordinates 487 y=0; 488 for (i=0; i < n; ++i) { 489 flags = vertices[off+i].type; 490 if (flags & 4) { 491 short dy = *points++; 492 y += (flags & 32) ? dy : (-cast(int)dy); 493 } else { 494 if (!(flags & 32)) { 495 y = y + cast(short) (points[0]*256 + points[1]); 496 points += 2; 497 } 498 } 499 vertices[off+i].y = cast(short) y; 500 } 501 502 // now convert them to our format 503 num_vertices=0; 504 sx = sy = cx = cy = scx = scy = 0; 505 for (i=0; i < n; ++i) { 506 flags = vertices[off+i].type; 507 x = cast(short) vertices[off+i].x; 508 y = cast(short) vertices[off+i].y; 509 510 if (next_move == i) { 511 if (i != 0) 512 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 513 514 // now start the new one 515 start_off = !(flags & 1); 516 if (start_off) { 517 // if we start off with an off-curve point, then when we need to find a point on the curve 518 // where we can start, and we need to save some state for when we wraparound. 519 scx = x; 520 scy = y; 521 if (!(vertices[off+i+1].type & 1)) { 522 // next point is also a curve point, so interpolate an on-point curve 523 sx = (x + cast(int) vertices[off+i+1].x) >> 1; 524 sy = (y + cast(int) vertices[off+i+1].y) >> 1; 525 } else { 526 // otherwise just use the next point as our start point 527 sx = cast(int) vertices[off+i+1].x; 528 sy = cast(int) vertices[off+i+1].y; 529 ++i; // we're using point i+1 as the starting point, so skip it 530 } 531 } else { 532 sx = x; 533 sy = y; 534 } 535 stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); 536 was_off = 0; 537 next_move = 1 + ttUSHORT(endPtsOfContours+j*2); 538 ++j; 539 } else { 540 if (!(flags & 1)) { // if it's a curve 541 if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint 542 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); 543 cx = x; 544 cy = y; 545 was_off = 1; 546 } else { 547 if (was_off) 548 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); 549 else 550 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); 551 was_off = 0; 552 } 553 } 554 } 555 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 556 } else if (numberOfContours == -1) { 557 // Compound shapes. 558 int more = 1; 559 const(ubyte)* comp = data + g + 10; 560 num_vertices = 0; 561 vertices = null; 562 while (more) { 563 ushort flags, gidx; 564 int comp_num_verts = 0, i; 565 stbtt_vertex* comp_verts = null, 566 tmp = null; 567 float[6] mtx = [1,0,0,1,0,0]; 568 float m, n; 569 570 flags = ttSHORT(comp); comp+=2; 571 gidx = ttSHORT(comp); comp+=2; 572 573 if (flags & 2) { // XY values 574 if (flags & 1) { // shorts 575 mtx[4] = ttSHORT(comp); comp+=2; 576 mtx[5] = ttSHORT(comp); comp+=2; 577 } else { 578 mtx[4] = ttCHAR(comp); comp+=1; 579 mtx[5] = ttCHAR(comp); comp+=1; 580 } 581 } 582 else { 583 // @TODO handle matching point 584 assert(0); 585 } 586 if (flags & (1<<3)) { // WE_HAVE_A_SCALE 587 mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 588 mtx[1] = mtx[2] = 0; 589 } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE 590 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 591 mtx[1] = mtx[2] = 0; 592 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 593 } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO 594 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 595 mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; 596 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 597 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 598 } 599 600 // Find transformation scales. 601 m = cast(float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 602 n = cast(float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); 603 604 // Get indexed glyph. 605 comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); 606 if (comp_num_verts > 0) { 607 // Transform vertices. 608 for (i = 0; i < comp_num_verts; ++i) { 609 stbtt_vertex* v = &comp_verts[i]; 610 stbtt_vertex_type x,y; 611 x=v.x; y=v.y; 612 v.x = cast(stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 613 v.y = cast(stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 614 x=v.cx; y=v.cy; 615 v.cx = cast(stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 616 v.cy = cast(stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 617 } 618 // Append vertices. 619 tmp = cast(stbtt_vertex*) malloc((num_vertices+comp_num_verts)*stbtt_vertex.sizeof); 620 if (!tmp) { 621 if (vertices) free(vertices); 622 if (comp_verts) free(comp_verts); 623 return 0; 624 } 625 if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*stbtt_vertex.sizeof); 626 memcpy(tmp+num_vertices, comp_verts, comp_num_verts*stbtt_vertex.sizeof); 627 if (vertices) free(vertices); 628 vertices = tmp; 629 free(comp_verts); 630 num_vertices += comp_num_verts; 631 } 632 // More components ? 633 more = flags & (1<<5); 634 } 635 } else if (numberOfContours < 0) { 636 // @TODO other compound variations? 637 assert(0); 638 } else { 639 // numberOfCounters == 0, do nothing 640 } 641 642 *pvertices = vertices; 643 return num_vertices; 644 } 645 646 void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) nothrow @nogc 647 { 648 ushort numOfLongHorMetrics = ttUSHORT(info.data+info.hhea + 34); 649 if (glyph_index < numOfLongHorMetrics) { 650 if (advanceWidth) *advanceWidth = ttSHORT(info.data + info.hmtx + 4*glyph_index); 651 if (leftSideBearing) *leftSideBearing = ttSHORT(info.data + info.hmtx + 4*glyph_index + 2); 652 } else { 653 if (advanceWidth) *advanceWidth = ttSHORT(info.data + info.hmtx + 4*(numOfLongHorMetrics-1)); 654 if (leftSideBearing) *leftSideBearing = ttSHORT(info.data + info.hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); 655 } 656 } 657 658 int stbtt_GetGlyphKernAdvance(const(stbtt_fontinfo)* info, int glyph1, int glyph2) nothrow @nogc 659 { 660 const(ubyte)* data = info.data + info.kern; 661 uint needle, straw; 662 int l, r, m; 663 664 // we only look at the first table. it must be 'horizontal' and format 0. 665 if (!info.kern) 666 return 0; 667 if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 668 return 0; 669 if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 670 return 0; 671 672 l = 0; 673 r = ttUSHORT(data+10) - 1; 674 needle = glyph1 << 16 | glyph2; 675 while (l <= r) { 676 m = (l + r) >> 1; 677 straw = ttULONG(data+18+(m*6)); // note: unaligned read 678 if (needle < straw) 679 r = m - 1; 680 else if (needle > straw) 681 l = m + 1; 682 else 683 return ttSHORT(data+22+(m*6)); 684 } 685 return 0; 686 } 687 688 /// an additional amount to add to the 'advance' value between ch1 and ch2 689 /// @TODO; for now always returns 0! 690 int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) nothrow @nogc 691 { 692 if (!info.kern) // if no kerning table, don't waste time looking up both codepoint.glyphs 693 return 0; 694 return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); 695 } 696 697 /// leftSideBearing is the offset from the current horizontal position to the left edge of the character 698 /// advanceWidth is the offset from the current horizontal position to the next horizontal position 699 /// these are expressed in unscaled coordinates 700 void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) nothrow @nogc 701 { 702 stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); 703 } 704 705 /// Ascent is the coordinate above the baseline the font extends; descent 706 /// is the coordinate below the baseline the font extends (i.e. it is typically negative) 707 /// lineGap is the spacing between one row's descent and the next row's ascent... 708 /// so you should advance the vertical position by "*ascent - *descent + *lineGap" 709 /// these are expressed in unscaled coordinates, so you must multiply by 710 /// the scale factor for a given size 711 void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) nothrow @nogc 712 { 713 if (ascent ) *ascent = ttSHORT(info.data+info.hhea + 4); 714 if (descent) *descent = ttSHORT(info.data+info.hhea + 6); 715 if (lineGap) *lineGap = ttSHORT(info.data+info.hhea + 8); 716 } 717 718 /// the bounding box around all possible characters 719 void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) nothrow @nogc 720 { 721 *x0 = ttSHORT(info.data + info.head + 36); 722 *y0 = ttSHORT(info.data + info.head + 38); 723 *x1 = ttSHORT(info.data + info.head + 40); 724 *y1 = ttSHORT(info.data + info.head + 42); 725 } 726 727 /// Computes a scale factor to produce a font whose "height" is 'pixels' tall. 728 /// Height is measured as the distance from the highest ascender to the lowest 729 /// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics 730 /// and computing: 731 /// scale = pixels / (ascent - descent) 732 /// so if you prefer to measure height by the ascent only, use a similar calculation. 733 float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) nothrow @nogc 734 { 735 int fheight = ttSHORT(info.data + info.hhea + 4) - ttSHORT(info.data + info.hhea + 6); 736 return cast(float) height / fheight; 737 } 738 739 /// computes a scale factor to produce a font whose EM size is mapped to 740 /// 'pixels' tall. This is probably what traditional APIs compute, but 741 /// I'm not positive. 742 float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) nothrow @nogc 743 { 744 int unitsPerEm = ttUSHORT(info.data + info.head + 18); 745 return pixels / unitsPerEm; 746 } 747 748 /// 749 void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) nothrow @nogc 750 { 751 free(v); 752 } 753 754 ////////////////////////////////////////////////////////////////////////////// 755 // 756 // antialiasing software rasterizer 757 // 758 759 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) nothrow @nogc 760 { 761 int x0, y0, x1, y1; 762 if (!stbtt_GetGlyphBox(font, glyph, &x0, &y0, &x1, &y1)) 763 { 764 // e.g. space character 765 if (ix0) *ix0 = 0; 766 if (iy0) *iy0 = 0; 767 if (ix1) *ix1 = 0; 768 if (iy1) *iy1 = 0; 769 } 770 else 771 { 772 // move to integral bboxes (treating pixels as little squares, what pixels get touched)? 773 if (ix0) *ix0 = ifloor( x0 * scale_x + shift_x); 774 if (iy0) *iy0 = ifloor(-y1 * scale_y + shift_y); 775 if (ix1) *ix1 = iceil( x1 * scale_x + shift_x); 776 if (iy1) *iy1 = iceil(-y0 * scale_y + shift_y); 777 } 778 } 779 780 void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) nothrow @nogc 781 { 782 stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); 783 } 784 785 /// Same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel 786 /// shift for the character. 787 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) nothrow @nogc 788 { 789 stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); 790 } 791 792 /// Gets the bbox of the bitmap centered around the glyph origin; so the 793 /// bitmap width is ix1-ix0, height is iy1-iy0, and location to place 794 /// the bitmap top left is (leftSideBearing*scale,iy0). 795 /// (Note that the bitmap uses y-increases-down, but the shape uses 796 /// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) 797 void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) nothrow @nogc 798 { 799 stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); 800 } 801 802 struct stbtt__edge 803 { 804 float x0,y0, x1,y1; 805 int invert; 806 } 807 808 struct stbtt__active_edge 809 { 810 int x,dx; 811 float ey; 812 stbtt__active_edge* next; 813 int valid; 814 } 815 816 enum FIXSHIFT = 10; 817 enum FIX = (1 << FIXSHIFT); 818 enum FIXMASK = (FIX-1); 819 820 stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point) nothrow @nogc 821 { 822 stbtt__active_edge *z = cast(stbtt__active_edge *) malloc(stbtt__active_edge.sizeof); // @TODO: make a pool of these!!! 823 float dxdy = (e.x1 - e.x0) / (e.y1 - e.y0); 824 assert(e.y0 <= start_point); 825 if (!z) return z; 826 // round dx down to avoid going too far 827 if (dxdy < 0) 828 z.dx = -ifloor(FIX * -dxdy); 829 else 830 z.dx = ifloor(FIX * dxdy); 831 z.x = ifloor(FIX * (e.x0 + dxdy * (start_point - e.y0))); 832 z.x -= off_x * FIX; 833 z.ey = e.y1; 834 z.next = null; 835 z.valid = e.invert ? 1 : -1; 836 return z; 837 } 838 839 // note: this routine clips fills that extend off the edges... ideally this 840 // wouldn't happen, but it could happen if the truetype glyph bounding boxes 841 // are wrong, or if the user supplies a too-small bitmap 842 void stbtt__fill_active_edges(ubyte *scanline, int len, stbtt__active_edge *e, int max_weight) nothrow @nogc 843 { 844 // non-zero winding fill 845 int x0=0, w=0; 846 847 while (e) { 848 if (w == 0) { 849 // if we're currently at zero, we need to record the edge start point 850 x0 = e.x; w += e.valid; 851 } else { 852 int x1 = e.x; w += e.valid; 853 // if we went to zero, we need to draw 854 if (w == 0) { 855 int i = x0 >> FIXSHIFT; 856 int j = x1 >> FIXSHIFT; 857 858 if (i < len && j >= 0) { 859 if (i == j) { 860 // x0,x1 are the same pixel, so compute combined coverage 861 scanline[i] = cast(ubyte)( scanline[i] + ((x1 - x0) * max_weight >> FIXSHIFT) ); 862 } else { 863 if (i >= 0) // add antialiasing for x0 864 scanline[i] = cast(ubyte)( scanline[i] + (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT) ) ; 865 else 866 i = -1; // clip 867 868 if (j < len) // add antialiasing for x1 869 scanline[j] = cast(ubyte)( scanline[j] + (((x1 & FIXMASK) * max_weight) >> FIXSHIFT) ); 870 else 871 j = len; // clip 872 873 for (++i; i < j; ++i) // fill pixels between x0 and x1 874 scanline[i] = cast(ubyte)( scanline[i] + max_weight ); 875 } 876 } 877 } 878 } 879 880 e = e.next; 881 } 882 } 883 884 void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y) nothrow @nogc 885 { 886 stbtt__active_edge* active = null; 887 int y,j=0; 888 int max_weight = (255 / vsubsample); // weight per vertical scanline 889 int s; // vertical subsample index 890 ubyte[512] scanline_data; 891 ubyte* scanline; 892 893 if (result.w > 512) 894 scanline = cast(ubyte *) malloc(result.w); 895 else 896 scanline = scanline_data.ptr; 897 898 y = off_y * vsubsample; 899 e[n].y0 = (off_y + result.h) * cast(float) vsubsample + 1; 900 901 while (j < result.h) { 902 memset(scanline, 0, result.w); 903 for (s=0; s < vsubsample; ++s) { 904 // find center of pixel for this scanline 905 float scan_y = y + 0.5f; 906 stbtt__active_edge **step = &active; 907 908 // update all active edges; 909 // remove all active edges that terminate before the center of this scanline 910 while (*step) { 911 stbtt__active_edge * z = *step; 912 if (z.ey <= scan_y) { 913 *step = z.next; // delete from list 914 assert(z.valid); 915 z.valid = 0; 916 free(z); 917 } else { 918 z.x += z.dx; // advance to position for current scanline 919 step = &((*step).next); // advance through list 920 } 921 } 922 923 // resort the list if needed 924 for(;;) { 925 int changed=0; 926 step = &active; 927 while (*step && (*step).next) { 928 if ((*step).x > (*step).next.x) { 929 stbtt__active_edge *t = *step; 930 stbtt__active_edge *q = t.next; 931 932 t.next = q.next; 933 q.next = t; 934 *step = q; 935 changed = 1; 936 } 937 step = &(*step).next; 938 } 939 if (!changed) break; 940 } 941 942 // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 943 while (e.y0 <= scan_y) { 944 if (e.y1 > scan_y) { 945 stbtt__active_edge *z = new_active(e, off_x, scan_y); 946 // find insertion point 947 if (active == null) 948 active = z; 949 else if (z.x < active.x) { 950 // insert at front 951 z.next = active; 952 active = z; 953 } else { 954 // find thing to insert AFTER 955 stbtt__active_edge *p = active; 956 while (p.next && p.next.x < z.x) 957 p = p.next; 958 // at this point, p.next.x is NOT < z.x 959 z.next = p.next; 960 p.next = z; 961 } 962 } 963 ++e; 964 } 965 966 // now process all active edges in XOR fashion 967 if (active) 968 stbtt__fill_active_edges(scanline, result.w, active, max_weight); 969 970 ++y; 971 } 972 memcpy(result.pixels + j * result.stride, scanline, result.w); 973 ++j; 974 } 975 976 while (active) { 977 stbtt__active_edge *z = active; 978 active = active.next; 979 free(z); 980 } 981 982 if (scanline != scanline_data.ptr) 983 free(scanline); 984 } 985 986 987 struct stbtt__point 988 { 989 float x,y; 990 } 991 992 void stbtt__rasterize(stbtt_fontinfo *info, 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) nothrow @nogc 993 { 994 float y_scale_inv = invert ? -scale_y : scale_y; 995 stbtt__edge *e; 996 int n,i,j,k,m; 997 int vsubsample = result.h < 8 ? 15 : 5; 998 // vsubsample should divide 255 evenly; otherwise we won't reach full opacity 999 1000 // now we have to blow out the windings into explicit edge lists 1001 n = 0; 1002 for (i=0; i < windings; ++i) 1003 n += wcount[i]; 1004 1005 info.edgeBuf.resize(n + 1); // add an extra one as a sentinel 1006 e = info.edgeBuf.ptr; 1007 1008 n = 0; 1009 1010 m=0; 1011 for (i=0; i < windings; ++i) { 1012 stbtt__point *p = pts + m; 1013 m += wcount[i]; 1014 j = wcount[i]-1; 1015 for (k=0; k < wcount[i]; j=k++) { 1016 int a=k,b=j; 1017 // skip the edge if horizontal 1018 if (p[j].y == p[k].y) 1019 continue; 1020 // add edge from j to k to the list 1021 e[n].invert = 0; 1022 if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { 1023 e[n].invert = 1; 1024 a=j,b=k; 1025 } 1026 e[n].x0 = p[a].x * scale_x + shift_x; 1027 e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; 1028 e[n].x1 = p[b].x * scale_x + shift_x; 1029 e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; 1030 ++n; 1031 } 1032 } 1033 1034 int edgeCompare(const(stbtt__edge) a, const(stbtt__edge) b) nothrow @nogc 1035 { 1036 if (a.y0 < b.y0) return -1; 1037 if (a.y0 > b.y0) return 1; 1038 return 0; 1039 } 1040 1041 // now sort the edges by their highest point (should snap to integer, and then by x) 1042 timSort!stbtt__edge(e[0..n], info.edgeScratchBuf, &edgeCompare); 1043 1044 // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule 1045 stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y); 1046 } 1047 1048 void stbtt__add_point(stbtt__point *points, int n, float x, float y) nothrow @nogc 1049 { 1050 if (!points) return; // during first pass, it's unallocated 1051 points[n].x = x; 1052 points[n].y = y; 1053 } 1054 1055 // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 1056 int stbtt__tesselate_curve(stbtt__point *points, int *num_points, double x0, double y0, double x1, double y1, double x2, double y2, double objspace_flatness_squared, int n) nothrow @nogc 1057 { 1058 bool stopSubdiv = (n > 16); 1059 1060 // midpoint 1061 double mx = (x0 + 2*x1 + x2)*0.25f; 1062 double my = (y0 + 2*y1 + y2)*0.25f; 1063 // versus directly drawn line 1064 double dx = (x0+x2)*0.5f - mx; 1065 double dy = (y0+y2)*0.5f - my; 1066 double squarexy = dx*dx+dy*dy; 1067 1068 bool addThisPoint = true; 1069 1070 if (squarexy > objspace_flatness_squared && !stopSubdiv) 1071 { 1072 // half-pixel error allowed... need to be smaller if AA 1073 int res1, res2; 1074 { 1075 double x01h = (x0 + x1) * 0.5f; 1076 double y01h = (y0 + y1) * 0.5f; 1077 res1 = stbtt__tesselate_curve(points, num_points, x0, y0, x01h, y01h, mx,my, objspace_flatness_squared,n+1); 1078 } 1079 1080 { 1081 double x12h = (x1 + x2) * 0.5f; 1082 double y12h = (y1 + y2) * 0.5f; 1083 res2 = stbtt__tesselate_curve(points, num_points, mx, my, x12h, y12h, x2,y2, objspace_flatness_squared,n+1); 1084 } 1085 1086 addThisPoint = false; 1087 } 1088 1089 if (addThisPoint) // do stuff here even in subdivided case to avoid TCO 1090 { 1091 stbtt__add_point(points, *num_points,x2,y2); 1092 *num_points = *num_points+1; 1093 } 1094 return 1; 1095 } 1096 1097 // returns number of contours 1098 stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours) nothrow @nogc 1099 { 1100 stbtt__point* points = null; 1101 int num_points=0; 1102 1103 float objspace_flatness_squared = objspace_flatness * objspace_flatness; 1104 int i,n=0,start=0, pass; 1105 1106 // count how many "moves" there are to get the contour count 1107 for (i=0; i < num_verts; ++i) 1108 if (vertices[i].type == STBTT_vmove) 1109 ++n; 1110 1111 *num_contours = n; 1112 if (n == 0) return null; 1113 1114 *contour_lengths = cast(int *) malloc(int.sizeof * n); 1115 1116 if (*contour_lengths == null) { 1117 *num_contours = 0; 1118 return null; 1119 } 1120 1121 // make two passes through the points so we don't need to realloc 1122 for (pass=0; pass < 2; ++pass) { 1123 float x=0,y=0; 1124 if (pass == 1) { 1125 points = cast(stbtt__point *) malloc(num_points * stbtt__point.sizeof); 1126 if (points == null) goto error; 1127 } 1128 num_points = 0; 1129 n= -1; 1130 for (i=0; i < num_verts; ++i) { 1131 switch (vertices[i].type) { 1132 case STBTT_vmove: 1133 // start the next contour 1134 if (n >= 0) 1135 (*contour_lengths)[n] = num_points - start; 1136 ++n; 1137 start = num_points; 1138 1139 x = vertices[i].x, y = vertices[i].y; 1140 stbtt__add_point(points, num_points++, x,y); 1141 break; 1142 case STBTT_vline: 1143 x = vertices[i].x, y = vertices[i].y; 1144 stbtt__add_point(points, num_points++, x, y); 1145 break; 1146 case STBTT_vcurve: 1147 stbtt__tesselate_curve(points, &num_points, x,y, 1148 vertices[i].cx, vertices[i].cy, 1149 vertices[i].x, vertices[i].y, 1150 objspace_flatness_squared, 0); 1151 x = vertices[i].x, y = vertices[i].y; 1152 break; 1153 default: 1154 assert(0); 1155 } 1156 } 1157 (*contour_lengths)[n] = num_points - start; 1158 } 1159 1160 return points; 1161 error: 1162 free(points); 1163 free(*contour_lengths); 1164 *contour_lengths = null; 1165 *num_contours = 0; 1166 return null; 1167 } 1168 1169 void stbtt_Rasterize(stbtt_fontinfo *info, 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) nothrow @nogc 1170 { 1171 float scale = scale_x > scale_y ? scale_y : scale_x; 1172 int winding_count; 1173 int* winding_lengths; 1174 stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count); 1175 if (windings) { 1176 stbtt__rasterize(info, result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert); 1177 free(winding_lengths); 1178 free(windings); 1179 } 1180 } 1181 1182 /// Frees the allocated bitmap. 1183 void stbtt_FreeBitmap(ubyte *bitmap) nothrow @nogc 1184 { 1185 free(bitmap); 1186 } 1187 1188 void stbtt_MakeGlyphBitmapSubpixel(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) nothrow @nogc 1189 { 1190 int ix0,iy0; 1191 stbtt_vertex *vertices; 1192 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1193 stbtt__bitmap gbm; 1194 1195 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,null,null); 1196 gbm.pixels = output; 1197 gbm.w = out_w; 1198 gbm.h = out_h; 1199 gbm.stride = out_stride; 1200 1201 if (gbm.w && gbm.h) 1202 stbtt_Rasterize(info, &gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1); 1203 1204 free(vertices); 1205 } 1206 1207 void stbtt_MakeGlyphBitmap(stbtt_fontinfo *info, ubyte *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) nothrow @nogc 1208 { 1209 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); 1210 } 1211 1212 /// Same as stbtt_MakeCodepointBitmap, but you can specify a subpixel 1213 /// shift for the character. 1214 void stbtt_MakeCodepointBitmapSubpixel(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) nothrow @nogc 1215 { 1216 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); 1217 }