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