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