1 /++ 2 TrueType Font rendering. Meant to be used with [arsd.simpledisplay], but it doesn't actually require that. Port of stb_truetype plus D wrappers for convenience. 3 4 5 Credits: Started as a copy of stb_truetype by Sean Barrett and ketmar helped 6 with expansions. 7 +/ 8 module arsd.ttf; 9 10 // stb_truetype.h - v1.19 - public domain 11 // authored from 2009-2016 by Sean Barrett / RAD Game Tools 12 // 13 // http://nothings.org/stb/stb_truetype.h 14 // 15 // port to D by adam d. ruppe (v.6) and massively updated ketmar. see the link above for more info about the lib and real author. 16 17 // here's some D convenience functions 18 19 // need to do some print glyphs t it.... 20 21 22 /// 23 struct TtfFont { 24 stbtt_fontinfo font; 25 /// 26 this(in ubyte[] data) { 27 load(data); 28 } 29 30 /// 31 void load(in ubyte[] data) { 32 if(stbtt_InitFont(&font, data.ptr, stbtt_GetFontOffsetForIndex(data.ptr, 0)) == 0) 33 throw new Exception("load font problem"); 34 } 35 36 /// Note that you must stbtt_FreeBitmap(returnValue.ptr, null); this thing or it will leak!!!! 37 ubyte[] renderCharacter(dchar c, int size, out int width, out int height, float shift_x = 0.0, float shift_y = 0.0) { 38 auto ptr = stbtt_GetCodepointBitmapSubpixel(&font, 0.0,stbtt_ScaleForPixelHeight(&font, size), 39 shift_x, shift_y, c, &width, &height, null,null); 40 return ptr[0 .. width * height]; 41 } 42 43 /// 44 void getStringSize(in char[] s, int size, out int width, out int height) { 45 float xpos=0; 46 47 auto scale = stbtt_ScaleForPixelHeight(&font, size); 48 int ascent, descent, line_gap; 49 stbtt_GetFontVMetrics(&font, &ascent,&descent,&line_gap); 50 auto baseline = cast(int) (ascent*scale); 51 52 import std.math; 53 54 int maxWidth; 55 56 foreach(i, dchar ch; s) { 57 int advance,lsb; 58 auto x_shift = xpos - floor(xpos); 59 stbtt_GetCodepointHMetrics(&font, ch, &advance, &lsb); 60 61 int x0, y0, x1, y1; 62 stbtt_GetCodepointBitmapBoxSubpixel(&font, ch, scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 63 64 maxWidth = cast(int)(xpos + x1); 65 66 xpos += (advance * scale); 67 if (i + 1 < s.length) 68 xpos += scale*stbtt_GetCodepointKernAdvance(&font, ch,s[i+1]); 69 } 70 71 width = maxWidth; 72 height = size; 73 } 74 75 /// 76 ubyte[] renderString(in char[] s, int size, out int width, out int height) { 77 float xpos=0; 78 79 auto scale = stbtt_ScaleForPixelHeight(&font, size); 80 int ascent, descent, line_gap; 81 stbtt_GetFontVMetrics(&font, &ascent,&descent,&line_gap); 82 auto baseline = cast(int) (ascent*scale); 83 84 import std.math; 85 86 int swidth; 87 int sheight; 88 getStringSize(s, size, swidth, sheight); 89 auto screen = new ubyte[](swidth * sheight); 90 91 foreach(i, dchar ch; s) { 92 int advance,lsb; 93 auto x_shift = xpos - floor(xpos); 94 stbtt_GetCodepointHMetrics(&font, ch, &advance, &lsb); 95 int cw, cheight; 96 auto c = renderCharacter(ch, size, cw, cheight, x_shift, 0.0); 97 scope(exit) stbtt_FreeBitmap(c.ptr, null); 98 99 int x0, y0, x1, y1; 100 stbtt_GetCodepointBitmapBoxSubpixel(&font, ch, scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 101 102 int x = cast(int) xpos + x0; 103 int y = baseline + y0; 104 int cx = 0; 105 foreach(index, pixel; c) { 106 if(cx == cw) { 107 cx = 0; 108 y++; 109 x = cast(int) xpos + x0; 110 } 111 auto offset = swidth * y + x; 112 if(offset >= screen.length) 113 break; 114 int val = (cast(int) pixel * (255 - screen[offset]) / 255); 115 if(val > 255) 116 val = 255; 117 screen[offset] += cast(ubyte)(val); 118 x++; 119 cx++; 120 } 121 122 //stbtt_MakeCodepointBitmapSubpixel(&font, &screen[(baseline + y0) * swidth + cast(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale, x_shift,0, ch); 123 // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong 124 // because this API is really for baking character bitmaps into textures. if you want to render 125 // a sequence of characters, you really need to render each bitmap to a temp buffer, then 126 // "alpha blend" that into the working buffer 127 xpos += (advance * scale); 128 if (i + 1 < s.length) 129 xpos += scale*stbtt_GetCodepointKernAdvance(&font, ch,s[i+1]); 130 } 131 132 width = swidth; 133 height = sheight; 134 135 return screen; 136 } 137 138 // ~this() {} 139 } 140 141 /// Version of OpenGL you want it to use. Currently only one option. 142 enum OpenGlFontGLVersion { 143 old /// old style glBegin/glEnd stuff 144 } 145 146 /+ 147 This is mostly there if you want to draw different pieces together in 148 different colors or across different boxes (see what text didn't fit, etc.). 149 150 Used only with [OpenGlLimitedFont] right now. 151 +/ 152 struct DrawingTextContext { 153 const(char)[] text; /// remaining text 154 float x; /// current position of the baseline 155 float y; /// ditto 156 157 const int left; /// bounding box, if applicable 158 const int top; /// ditto 159 const int right; /// ditto 160 const int bottom; /// ditto 161 } 162 163 /++ 164 Note that the constructor calls OpenGL functions and thus this must be called AFTER 165 the context creation, e.g. on simpledisplay's window first visible delegate. 166 167 Any text with characters outside the range you bake in the constructor are simply 168 ignored - that's why it is called "limited" font. The [TtfFont] struct can generate 169 any string on-demand which is more flexible, and even faster for strings repeated 170 frequently, but slower for frequently-changing or one-off strings. That's what this 171 class is made for. 172 173 History: 174 Added April 24, 2020 175 +/ 176 class OpenGlLimitedFont(OpenGlFontGLVersion ver = OpenGlFontGLVersion.old) { 177 178 import arsd.simpledisplay; 179 180 static private int nextPowerOfTwo(int v) { 181 v--; 182 v |= v >> 1; 183 v |= v >> 2; 184 v |= v >> 4; 185 v |= v >> 8; 186 v |= v >> 16; 187 v++; 188 return v; 189 } 190 191 uint _tex; 192 stbtt_bakedchar[] charInfo; 193 194 import arsd.color; 195 196 /++ 197 198 Tip: if color == Color.transparent, it will not actually attempt to draw to OpenGL. You can use this 199 to help plan pagination inside the bounding box. 200 201 +/ 202 public final DrawingTextContext drawString(int x, int y, in char[] text, Color color = Color.white, Rectangle boundingBox = Rectangle.init) { 203 if(boundingBox == Rectangle.init) { 204 // if you hit a newline, at least keep it aligned here if something wasn't 205 // explicitly given. 206 boundingBox.left = x; 207 boundingBox.top = y; 208 boundingBox.right = int.max; 209 boundingBox.bottom = int.max; 210 } 211 DrawingTextContext dtc = DrawingTextContext(text, x, y, boundingBox.tupleof); 212 drawString(dtc, color); 213 return dtc; 214 } 215 216 /++ 217 It won't attempt to draw partial characters if it butts up against the bounding box, unless 218 word wrap was impossible. Use clipping if you need to cover that up and guarantee it never goes 219 outside the bounding box ever. 220 221 +/ 222 public final void drawString(ref DrawingTextContext context, Color color = Color.white) { 223 bool actuallyDraw = color != Color.transparent; 224 225 if(actuallyDraw) { 226 glBindTexture(GL_TEXTURE_2D, _tex); 227 228 glColor4f(cast(float)color.r/255.0, cast(float)color.g/255.0, cast(float)color.b/255.0, cast(float)color.a / 255.0); 229 } 230 231 bool newWord = true; 232 bool atStartOfLine = true; 233 float currentWordLength; 234 int currentWordCharsRemaining; 235 236 void calculateWordInfo() { 237 const(char)[] copy = context.text; 238 currentWordLength = 0.0; 239 currentWordCharsRemaining = 0; 240 241 while(copy.length) { 242 auto ch = copy[0]; 243 copy = copy[1 .. $]; 244 245 currentWordCharsRemaining++; 246 247 if(ch <= 32) 248 break; // not in a word anymore 249 250 if(ch < firstChar || ch > lastChar) 251 continue; 252 253 const b = charInfo[cast(int) ch - cast(int) firstChar]; 254 255 currentWordLength += b.xadvance; 256 } 257 } 258 259 bool newline() { 260 context.x = context.left; 261 context.y += lineHeight; 262 atStartOfLine = true; 263 264 if(context.y + descent > context.bottom) 265 return false; 266 267 return true; 268 } 269 270 while(context.text.length) { 271 if(newWord) { 272 calculateWordInfo(); 273 newWord = false; 274 275 if(context.x + currentWordLength > context.right) { 276 if(!newline()) 277 break; // ran out of space 278 } 279 } 280 281 // FIXME i should prolly UTF-8 decode.... 282 dchar ch = context.text[0]; 283 context.text = context.text[1 .. $]; 284 285 if(currentWordCharsRemaining) { 286 currentWordCharsRemaining--; 287 288 if(currentWordCharsRemaining == 0) 289 newWord = true; 290 } 291 292 if(ch == '\t') { 293 context.x += 20; 294 continue; 295 } 296 if(ch == '\n') { 297 if(newline()) 298 continue; 299 else 300 break; 301 } 302 303 if(ch < firstChar || ch > lastChar) { 304 if(ch == ' ') 305 context.x += lineHeight / 4; // fake space if not available in formal font (I recommend you do include it though) 306 continue; 307 } 308 309 const b = charInfo[cast(int) ch - cast(int) firstChar]; 310 311 int round_x = STBTT_ifloor((context.x + b.xoff) + 0.5f); 312 int round_y = STBTT_ifloor((context.y + b.yoff) + 0.5f); 313 314 // box to draw on the screen 315 auto x0 = round_x; 316 auto y0 = round_y; 317 auto x1 = round_x + b.x1 - b.x0; 318 auto y1 = round_y + b.y1 - b.y0; 319 320 // is that outside the bounding box we should draw in? 321 // if so on x, wordwrap to the next line. if so on y, 322 // return prematurely and let the user context handle it if needed. 323 324 // box to fetch off the surface 325 auto s0 = b.x0 * ipw; 326 auto t0 = b.y0 * iph; 327 auto s1 = b.x1 * ipw; 328 auto t1 = b.y1 * iph; 329 330 if(actuallyDraw) { 331 glBegin(GL_QUADS); 332 glTexCoord2f(s0, t0); glVertex2i(x0, y0); 333 glTexCoord2f(s1, t0); glVertex2i(x1, y0); 334 glTexCoord2f(s1, t1); glVertex2i(x1, y1); 335 glTexCoord2f(s0, t1); glVertex2i(x0, y1); 336 glEnd(); 337 } 338 339 context.x += b.xadvance; 340 } 341 342 if(actuallyDraw) 343 glBindTexture(GL_TEXTURE_2D, 0); // unbind the texture 344 } 345 346 private { 347 const dchar firstChar; 348 const dchar lastChar; 349 const int pw; 350 const int ph; 351 const float ipw; 352 const float iph; 353 } 354 355 public const int lineHeight; /// metrics 356 357 public const int ascent; /// ditto 358 public const int descent; /// ditto 359 public const int line_gap; /// ditto; 360 361 /++ 362 363 +/ 364 public this(const ubyte[] ttfData, float fontPixelHeight, dchar firstChar = 32, dchar lastChar = 127) { 365 366 assert(lastChar > firstChar); 367 assert(fontPixelHeight > 0); 368 369 this.firstChar = firstChar; 370 this.lastChar = lastChar; 371 372 int numChars = 1 + cast(int) lastChar - cast(int) firstChar; 373 374 lineHeight = cast(int) (fontPixelHeight + 0.5); 375 376 import std.math; 377 // will most likely be 512x512ish; about 256k likely 378 int height = cast(int) (fontPixelHeight + 1) * cast(int) (sqrt(cast(float) numChars) + 1); 379 height = nextPowerOfTwo(height); 380 int width = height; 381 382 this.pw = width; 383 this.ph = height; 384 385 ipw = 1.0f / pw; 386 iph = 1.0f / ph; 387 388 int len = width * height; 389 390 //import std.stdio; writeln(len); 391 392 import core.stdc.stdlib; 393 ubyte[] buffer = (cast(ubyte*) malloc(len))[0 .. len]; 394 if(buffer is null) assert(0); 395 scope(exit) free(buffer.ptr); 396 397 charInfo.length = numChars; 398 399 int ascent, descent, line_gap; 400 401 if(stbtt_BakeFontBitmap( 402 ttfData.ptr, 0, 403 fontPixelHeight, 404 buffer.ptr, width, height, 405 cast(int) firstChar, numChars, 406 charInfo.ptr, 407 &ascent, &descent, &line_gap 408 ) == 0) 409 throw new Exception("bake font didn't work"); 410 411 this.ascent = ascent; 412 this.descent = descent; 413 this.line_gap = line_gap; 414 415 glGenTextures(1, &_tex); 416 glBindTexture(GL_TEXTURE_2D, _tex); 417 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 418 419 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 420 421 glTexImage2D( 422 GL_TEXTURE_2D, 423 0, 424 GL_ALPHA, 425 width, 426 height, 427 0, 428 GL_ALPHA, 429 GL_UNSIGNED_BYTE, 430 buffer.ptr); 431 432 assert(!glGetError()); 433 434 glBindTexture(GL_TEXTURE_2D, 0); 435 } 436 } 437 438 439 // test program 440 /+ 441 int main(string[] args) 442 { 443 import std.conv; 444 import arsd.simpledisplay; 445 int c = (args.length > 1 ? to!int(args[1]) : 'a'), s = (args.length > 2 ? to!int(args[2]) : 20); 446 import std.file; 447 448 auto font = TtfFont(cast(ubyte[]) /*import("sans-serif.ttf"));//*/std.file.read(args.length > 3 ? args[3] : "sans-serif.ttf")); 449 450 int w, h; 451 auto bitmap = font.renderString("Hejlqo, world!qMpj", s, w, h); 452 auto img = new Image(w, h); 453 454 for (int j=0; j < h; ++j) { 455 for (int i=0; i < w; ++i) 456 img.putPixel(i, j, Color(0, (bitmap[j*w+i] > 128) ? 255 : 0, 0)); 457 } 458 img.displayImage(); 459 return 0; 460 } 461 +/ 462 463 464 465 466 // STB_TTF FOLLOWS 467 468 469 nothrow @trusted @nogc: 470 471 version = STB_RECT_PACK_VERSION; 472 473 // //////////////////////////////////////////////////////////////////////////// 474 // //////////////////////////////////////////////////////////////////////////// 475 // // 476 // // SAMPLE PROGRAMS 477 // // 478 // 479 // Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless 480 // 481 /+ 482 #if 0 483 #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 484 #include "stb_truetype.h" 485 486 unsigned char ttf_buffer[1<<20]; 487 unsigned char temp_bitmap[512*512]; 488 489 stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs 490 GLuint ftex; 491 492 void my_stbtt_initfont(void) 493 { 494 fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); 495 stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! 496 // can free ttf_buffer at this point 497 glGenTextures(1, &ftex); 498 glBindTexture(GL_TEXTURE_2D, ftex); 499 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); 500 // can free temp_bitmap at this point 501 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 502 } 503 504 void my_stbtt_print(float x, float y, char *text) 505 { 506 // assume orthographic projection with units = screen pixels, origin at top left 507 glEnable(GL_TEXTURE_2D); 508 glBindTexture(GL_TEXTURE_2D, ftex); 509 glBegin(GL_QUADS); 510 while (*text) { 511 if (*text >= 32 && *text < 128) { 512 stbtt_aligned_quad q; 513 stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 514 glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); 515 glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); 516 glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); 517 glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); 518 } 519 ++text; 520 } 521 glEnd(); 522 } 523 #endif 524 // 525 // 526 // //////////////////////////////////////////////////////////////////////////// 527 // 528 // Complete program (this compiles): get a single bitmap, print as ASCII art 529 // 530 #if 0 531 #include <stdio.h> 532 #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 533 #include "stb_truetype.h" 534 535 char ttf_buffer[1<<25]; 536 537 int main(int argc, char **argv) 538 { 539 stbtt_fontinfo font; 540 unsigned char *bitmap; 541 int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); 542 543 fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); 544 545 stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); 546 bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); 547 548 for (j=0; j < h; ++j) { 549 for (i=0; i < w; ++i) 550 putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); 551 putchar('\n'); 552 } 553 return 0; 554 } 555 #endif 556 // 557 // Output: 558 // 559 // .ii. 560 // @@@@@@. 561 // V@Mio@@o 562 // :i. V@V 563 // :oM@@M 564 // :@@@MM@M 565 // @@o o@M 566 // :@@. M@M 567 // @@@o@@@@ 568 // :M@@V:@@. 569 // 570 ////////////////////////////////////////////////////////////////////////////// 571 // 572 // Complete program: print "Hello World!" banner, with bugs 573 // 574 #if 0 575 char buffer[24<<20]; 576 unsigned char screen[20][79]; 577 578 int main(int arg, char **argv) 579 { 580 stbtt_fontinfo font; 581 int i,j,ascent,baseline,ch=0; 582 float scale, xpos=2; // leave a little padding in case the character extends left 583 char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness 584 585 fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); 586 stbtt_InitFont(&font, buffer, 0); 587 588 scale = stbtt_ScaleForPixelHeight(&font, 15); 589 stbtt_GetFontVMetrics(&font, &ascent,0,0); 590 baseline = (int) (ascent*scale); 591 592 while (text[ch]) { 593 int advance,lsb,x0,y0,x1,y1; 594 float x_shift = xpos - (float) floor(xpos); 595 stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); 596 stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 597 stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); 598 // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong 599 // because this API is really for baking character bitmaps into textures. if you want to render 600 // a sequence of characters, you really need to render each bitmap to a temp buffer, then 601 // "alpha blend" that into the working buffer 602 xpos += (advance * scale); 603 if (text[ch+1]) 604 xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); 605 ++ch; 606 } 607 608 for (j=0; j < 20; ++j) { 609 for (i=0; i < 78; ++i) 610 putchar(" .:ioVM@"[screen[j][i]>>5]); 611 putchar('\n'); 612 } 613 614 return 0; 615 } 616 #endif 617 +/ 618 619 // //////////////////////////////////////////////////////////////////////////// 620 // //////////////////////////////////////////////////////////////////////////// 621 // // 622 // // INTEGRATION WITH YOUR CODEBASE 623 // // 624 // // The following sections allow you to supply alternate definitions 625 // // of C library functions used by stb_truetype, e.g. if you don't 626 // // link with the C runtime library. 627 628 // #define your own (u)stbtt_int8/16/32 before including to override this 629 alias stbtt_uint8 = ubyte; 630 alias stbtt_int8 = byte; 631 alias stbtt_uint16 = ushort; 632 alias stbtt_int16 = short; 633 alias stbtt_uint32 = uint; 634 alias stbtt_int32 = int; 635 636 //typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; 637 //typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; 638 639 int STBTT_ifloor(T) (in T x) pure { pragma(inline, true); import std.math : floor; return cast(int)floor(x); } 640 int STBTT_iceil(T) (in T x) pure { pragma(inline, true); import std.math : ceil; return cast(int)ceil(x); } 641 642 T STBTT_sqrt(T) (in T x) pure { pragma(inline, true); import std.math : sqrt; return sqrt(x); } 643 T STBTT_pow(T) (in T x, in T y) pure { pragma(inline, true); import std.math : pow; return pow(x, y); } 644 645 T STBTT_fmod(T) (in T x, in T y) { pragma(inline, true); import std.math : fmod; return fmod(x, y); } 646 647 T STBTT_cos(T) (in T x) pure { pragma(inline, true); import std.math : cos; return cos(x); } 648 T STBTT_acos(T) (in T x) pure { pragma(inline, true); import std.math : acos; return acos(x); } 649 650 T STBTT_fabs(T) (in T x) pure { pragma(inline, true); import std.math : abs; return abs(x); } 651 652 // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h 653 void* STBTT_malloc (uint size, const(void)* uptr) { pragma(inline, true); import core.stdc.stdlib : malloc; return malloc(size); } 654 void STBTT_free (void *ptr, const(void)* uptr) { pragma(inline, true); import core.stdc.stdlib : free; free(ptr); } 655 /* 656 #ifndef STBTT_malloc 657 #include <stdlib.h> 658 #define STBTT_malloc(x,u) ((void)(u),malloc(x)) 659 #define STBTT_free(x,u) ((void)(u),free(x)) 660 #endif 661 */ 662 663 //alias STBTT_assert = assert; 664 665 uint STBTT_strlen (const(void)* p) { pragma(inline, true); import core.stdc.string : strlen; return (p !is null ? cast(uint)strlen(cast(const(char)*)p) : 0); } 666 void STBTT_memcpy (void* d, const(void)* s, uint count) { pragma(inline, true); import core.stdc.string : memcpy; if (count > 0) memcpy(d, s, count); } 667 void STBTT_memset (void* d, uint v, uint count) { pragma(inline, true); import core.stdc.string : memset; if (count > 0) memset(d, v, count); } 668 669 670 // ///////////////////////////////////////////////////////////////////////////// 671 // ///////////////////////////////////////////////////////////////////////////// 672 // // 673 // // INTERFACE 674 // // 675 // // 676 677 // private structure 678 struct stbtt__buf { 679 ubyte *data; 680 int cursor; 681 int size; 682 } 683 684 ////////////////////////////////////////////////////////////////////////////// 685 // 686 // TEXTURE BAKING API 687 // 688 // If you use this API, you only have to call two functions ever. 689 // 690 691 struct stbtt_bakedchar { 692 ushort x0,y0,x1,y1; // coordinates of bbox in bitmap 693 float xoff,yoff,xadvance; 694 } 695 696 /+ 697 STBTT_DEF int stbtt_BakeFontBitmap(const(ubyte)* data, int offset, // font location (use offset=0 for plain .ttf) 698 float pixel_height, // height of font in pixels 699 ubyte *pixels, int pw, int ph, // bitmap to be filled in 700 int first_char, int num_chars, // characters to bake 701 stbtt_bakedchar *chardata, // you allocate this, it's num_chars long 702 int* ascent, int * descent, int* line_gap); // metrics for use later too 703 +/ 704 // if return is positive, the first unused row of the bitmap 705 // if return is negative, returns the negative of the number of characters that fit 706 // if return is 0, no characters fit and no rows were used 707 // This uses a very crappy packing. 708 709 struct stbtt_aligned_quad { 710 float x0,y0,s0,t0; // top-left 711 float x1,y1,s1,t1; // bottom-right 712 } 713 714 /+ 715 STBTT_DEF void stbtt_GetBakedQuad(const(stbtt_bakedchar)* chardata, int pw, int ph, // same data as above 716 int char_index, // character to display 717 float *xpos, float *ypos, // pointers to current position in screen pixel space 718 stbtt_aligned_quad *q, // output: quad to draw 719 int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier 720 +/ 721 // Call GetBakedQuad with char_index = 'character - first_char', and it 722 // creates the quad you need to draw and advances the current position. 723 // 724 // The coordinate system used assumes y increases downwards. 725 // 726 // Characters will extend both above and below the current position; 727 // see discussion of "BASELINE" above. 728 // 729 // It's inefficient; you might want to c&p it and optimize it. 730 731 732 733 // //////////////////////////////////////////////////////////////////////////// 734 // 735 // NEW TEXTURE BAKING API 736 // 737 // This provides options for packing multiple fonts into one atlas, not 738 // perfectly but better than nothing. 739 740 struct stbtt_packedchar { 741 ushort x0,y0,x1,y1; // coordinates of bbox in bitmap 742 float xoff,yoff,xadvance; 743 float xoff2,yoff2; 744 } 745 746 //typedef struct stbtt_pack_context stbtt_pack_context; 747 //typedef struct stbtt_fontinfo stbtt_fontinfo; 748 //#ifndef STB_RECT_PACK_VERSION 749 //typedef struct stbrp_rect stbrp_rect; 750 //#endif 751 752 //STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, ubyte *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); 753 // Initializes a packing context stored in the passed-in stbtt_pack_context. 754 // Future calls using this context will pack characters into the bitmap passed 755 // in here: a 1-channel bitmap that is width * height. stride_in_bytes is 756 // the distance from one row to the next (or 0 to mean they are packed tightly 757 // together). "padding" is the amount of padding to leave between each 758 // character (normally you want '1' for bitmaps you'll use as textures with 759 // bilinear filtering). 760 // 761 // Returns 0 on failure, 1 on success. 762 763 //STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); 764 // Cleans up the packing context and frees all memory. 765 766 //#define STBTT_POINT_SIZE(x) (-(x)) 767 768 /+ 769 STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const(ubyte)* fontdata, int font_index, float font_size, 770 int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); 771 +/ 772 // Creates character bitmaps from the font_index'th font found in fontdata (use 773 // font_index=0 if you don't know what that is). It creates num_chars_in_range 774 // bitmaps for characters with unicode values starting at first_unicode_char_in_range 775 // and increasing. Data for how to render them is stored in chardata_for_range; 776 // pass these to stbtt_GetPackedQuad to get back renderable quads. 777 // 778 // font_size is the full height of the character from ascender to descender, 779 // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed 780 // by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() 781 // and pass that result as 'font_size': 782 // ..., 20 , ... // font max minus min y is 20 pixels tall 783 // ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall 784 785 struct stbtt_pack_range { 786 float font_size; 787 int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint 788 int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints 789 int num_chars; 790 stbtt_packedchar *chardata_for_range; // output 791 ubyte h_oversample, v_oversample; // don't set these, they're used internally 792 } 793 794 //STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const(ubyte)* fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); 795 // Creates character bitmaps from multiple ranges of characters stored in 796 // ranges. This will usually create a better-packed bitmap than multiple 797 // calls to stbtt_PackFontRange. Note that you can call this multiple 798 // times within a single PackBegin/PackEnd. 799 800 //STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); 801 // Oversampling a font increases the quality by allowing higher-quality subpixel 802 // positioning, and is especially valuable at smaller text sizes. 803 // 804 // This function sets the amount of oversampling for all following calls to 805 // stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given 806 // pack context. The default (no oversampling) is achieved by h_oversample=1 807 // and v_oversample=1. The total number of pixels required is 808 // h_oversample*v_oversample larger than the default; for example, 2x2 809 // oversampling requires 4x the storage of 1x1. For best results, render 810 // oversampled textures with bilinear filtering. Look at the readme in 811 // stb/tests/oversample for information about oversampled fonts 812 // 813 // To use with PackFontRangesGather etc., you must set it before calls 814 // call to PackFontRangesGatherRects. 815 816 /+ 817 STBTT_DEF void stbtt_GetPackedQuad(const(stbtt_packedchar)* chardata, int pw, int ph, // same data as above 818 int char_index, // character to display 819 float *xpos, float *ypos, // pointers to current position in screen pixel space 820 stbtt_aligned_quad *q, // output: quad to draw 821 int align_to_integer); 822 823 STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const(stbtt_fontinfo)* info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); 824 STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); 825 STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const(stbtt_fontinfo)* info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); 826 +/ 827 // Calling these functions in sequence is roughly equivalent to calling 828 // stbtt_PackFontRanges(). If you more control over the packing of multiple 829 // fonts, or if you want to pack custom data into a font texture, take a look 830 // at the source to of stbtt_PackFontRanges() and create a custom version 831 // using these functions, e.g. call GatherRects multiple times, 832 // building up a single array of rects, then call PackRects once, 833 // then call RenderIntoRects repeatedly. This may result in a 834 // better packing than calling PackFontRanges multiple times 835 // (or it may not). 836 837 // this is an opaque structure that you shouldn't mess with which holds 838 // all the context needed from PackBegin to PackEnd. 839 struct stbtt_pack_context { 840 void *user_allocator_context; 841 void *pack_info; 842 int width; 843 int height; 844 int stride_in_bytes; 845 int padding; 846 uint h_oversample, v_oversample; 847 ubyte *pixels; 848 void *nodes; 849 } 850 851 // //////////////////////////////////////////////////////////////////////////// 852 // 853 // FONT LOADING 854 // 855 // 856 857 //STBTT_DEF int stbtt_GetNumberOfFonts(const(ubyte)* data); 858 // This function will determine the number of fonts in a font file. TrueType 859 // collection (.ttc) files may contain multiple fonts, while TrueType font 860 // (.ttf) files only contain one font. The number of fonts can be used for 861 // indexing with the previous function where the index is between zero and one 862 // less than the total fonts. If an error occurs, -1 is returned. 863 864 //STBTT_DEF int stbtt_GetFontOffsetForIndex(const(ubyte)* data, int index); 865 // Each .ttf/.ttc file may have more than one font. Each font has a sequential 866 // index number starting from 0. Call this function to get the font offset for 867 // a given index; it returns -1 if the index is out of range. A regular .ttf 868 // file will only define one font and it always be at offset 0, so it will 869 // return '0' for index 0, and -1 for all other indices. 870 871 // The following structure is defined publically so you can declare one on 872 // the stack or as a global or etc, but you should treat it as opaque. 873 struct stbtt_fontinfo { 874 void * userdata; 875 ubyte * data; // pointer to .ttf file 876 int fontstart; // offset of start of font 877 878 int numGlyphs; // number of glyphs, needed for range checking 879 880 int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf 881 int index_map; // a cmap mapping for our chosen character encoding 882 int indexToLocFormat; // format needed to map from glyph index to glyph 883 884 stbtt__buf cff; // cff font data 885 stbtt__buf charstrings; // the charstring index 886 stbtt__buf gsubrs; // global charstring subroutines index 887 stbtt__buf subrs; // private charstring subroutines index 888 stbtt__buf fontdicts; // array of font dicts 889 stbtt__buf fdselect; // map from glyph to fontdict 890 } 891 892 //STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const(ubyte)* data, int offset); 893 // Given an offset into the file that defines a font, this function builds 894 // the necessary cached info for the rest of the system. You must allocate 895 // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't 896 // need to do anything special to free it, because the contents are pure 897 // value data with no additional data structures. Returns 0 on failure. 898 899 900 // //////////////////////////////////////////////////////////////////////////// 901 // 902 // CHARACTER TO GLYPH-INDEX CONVERSIOn 903 904 //STBTT_DEF int stbtt_FindGlyphIndex(const(stbtt_fontinfo)* info, int unicode_codepoint); 905 // If you're going to perform multiple operations on the same character 906 // and you want a speed-up, call this function with the character you're 907 // going to process, then use glyph-based functions instead of the 908 // codepoint-based functions. 909 910 911 // //////////////////////////////////////////////////////////////////////////// 912 // 913 // CHARACTER PROPERTIES 914 // 915 916 //STBTT_DEF float stbtt_ScaleForPixelHeight(const(stbtt_fontinfo)* info, float pixels); 917 // computes a scale factor to produce a font whose "height" is 'pixels' tall. 918 // Height is measured as the distance from the highest ascender to the lowest 919 // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics 920 // and computing: 921 // scale = pixels / (ascent - descent) 922 // so if you prefer to measure height by the ascent only, use a similar calculation. 923 924 //STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const(stbtt_fontinfo)* info, float pixels); 925 // computes a scale factor to produce a font whose EM size is mapped to 926 // 'pixels' tall. This is probably what traditional APIs compute, but 927 // I'm not positive. 928 929 //STBTT_DEF void stbtt_GetFontVMetrics(const(stbtt_fontinfo)* info, int *ascent, int *descent, int *lineGap); 930 // ascent is the coordinate above the baseline the font extends; descent 931 // is the coordinate below the baseline the font extends (i.e. it is typically negative) 932 // lineGap is the spacing between one row's descent and the next row's ascent... 933 // so you should advance the vertical position by "*ascent - *descent + *lineGap" 934 // these are expressed in unscaled coordinates, so you must multiply by 935 // the scale factor for a given size 936 937 //STBTT_DEF int stbtt_GetFontVMetricsOS2(const(stbtt_fontinfo)* info, int *typoAscent, int *typoDescent, int *typoLineGap); 938 // analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 939 // table (specific to MS/Windows TTF files). 940 // 941 // Returns 1 on success (table present), 0 on failure. 942 943 //STBTT_DEF void stbtt_GetFontBoundingBox(const(stbtt_fontinfo)* info, int *x0, int *y0, int *x1, int *y1); 944 // the bounding box around all possible characters 945 946 //STBTT_DEF void stbtt_GetCodepointHMetrics(const(stbtt_fontinfo)* info, int codepoint, int *advanceWidth, int *leftSideBearing); 947 // leftSideBearing is the offset from the current horizontal position to the left edge of the character 948 // advanceWidth is the offset from the current horizontal position to the next horizontal position 949 // these are expressed in unscaled coordinates 950 951 //STBTT_DEF int stbtt_GetCodepointKernAdvance(const(stbtt_fontinfo)* info, int ch1, int ch2); 952 // an additional amount to add to the 'advance' value between ch1 and ch2 953 954 //STBTT_DEF int stbtt_GetCodepointBox(const(stbtt_fontinfo)* info, int codepoint, int *x0, int *y0, int *x1, int *y1); 955 // Gets the bounding box of the visible part of the glyph, in unscaled coordinates 956 957 //STBTT_DEF void stbtt_GetGlyphHMetrics(const(stbtt_fontinfo)* info, int glyph_index, int *advanceWidth, int *leftSideBearing); 958 //STBTT_DEF int stbtt_GetGlyphKernAdvance(const(stbtt_fontinfo)* info, int glyph1, int glyph2); 959 //STBTT_DEF int stbtt_GetGlyphBox(const(stbtt_fontinfo)* info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 960 // as above, but takes one or more glyph indices for greater efficiency 961 962 963 ////////////////////////////////////////////////////////////////////////////// 964 // 965 // GLYPH SHAPES (you probably don't need these, but they have to go before 966 // the bitmaps for C declaration-order reasons) 967 // 968 969 //#ifndef STBTT_vmove // you can predefine these to use different values (but why?) 970 enum { 971 STBTT_vmove=1, 972 STBTT_vline, 973 STBTT_vcurve, 974 STBTT_vcubic 975 } 976 977 //#ifndef stbtt_vertex // you can predefine this to use different values (we share this with other code at RAD) 978 alias stbtt_vertex_type = short; // can't use stbtt_int16 because that's not visible in the header file 979 struct stbtt_vertex { 980 stbtt_vertex_type x,y,cx,cy,cx1,cy1; 981 ubyte type,padding; 982 } 983 //#endif 984 985 //STBTT_DEF int stbtt_IsGlyphEmpty(const(stbtt_fontinfo)* info, int glyph_index); 986 // returns non-zero if nothing is drawn for this glyph 987 988 //STBTT_DEF int stbtt_GetCodepointShape(const(stbtt_fontinfo)* info, int unicode_codepoint, stbtt_vertex **vertices); 989 //STBTT_DEF int stbtt_GetGlyphShape(const(stbtt_fontinfo)* info, int glyph_index, stbtt_vertex **vertices); 990 // returns # of vertices and fills *vertices with the pointer to them 991 // these are expressed in "unscaled" coordinates 992 // 993 // The shape is a series of countours. Each one starts with 994 // a STBTT_moveto, then consists of a series of mixed 995 // STBTT_lineto and STBTT_curveto segments. A lineto 996 // draws a line from previous endpoint to its x,y; a curveto 997 // draws a quadratic bezier from previous endpoint to 998 // its x,y, using cx,cy as the bezier control point. 999 1000 //STBTT_DEF void stbtt_FreeShape(const(stbtt_fontinfo)* info, stbtt_vertex *vertices); 1001 // frees the data allocated above 1002 1003 // //////////////////////////////////////////////////////////////////////////// 1004 // 1005 // BITMAP RENDERING 1006 // 1007 1008 //STBTT_DEF void stbtt_FreeBitmap(ubyte *bitmap, void *userdata); 1009 // frees the bitmap allocated below 1010 1011 //STBTT_DEF ubyte *stbtt_GetCodepointBitmap(const(stbtt_fontinfo)* info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); 1012 // allocates a large-enough single-channel 8bpp bitmap and renders the 1013 // specified character/glyph at the specified scale into it, with 1014 // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). 1015 // *width & *height are filled out with the width & height of the bitmap, 1016 // which is stored left-to-right, top-to-bottom. 1017 // 1018 // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap 1019 1020 //STBTT_DEF 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); 1021 // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel 1022 // shift for the character 1023 1024 //STBTT_DEF 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); 1025 // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap 1026 // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap 1027 // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the 1028 // width and height and positioning info for it first. 1029 1030 //STBTT_DEF 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); 1031 // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel 1032 // shift for the character 1033 1034 //STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(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 oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); 1035 // same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering 1036 // is performed (see stbtt_PackSetOversampling) 1037 1038 //STBTT_DEF void stbtt_GetCodepointBitmapBox(const(stbtt_fontinfo)* font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); 1039 // get the bbox of the bitmap centered around the glyph origin; so the 1040 // bitmap width is ix1-ix0, height is iy1-iy0, and location to place 1041 // the bitmap top left is (leftSideBearing*scale,iy0). 1042 // (Note that the bitmap uses y-increases-down, but the shape uses 1043 // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) 1044 1045 //STBTT_DEF 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); 1046 // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel 1047 // shift for the character 1048 1049 // the following functions are equivalent to the above functions, but operate 1050 // on glyph indices instead of Unicode codepoints (for efficiency) 1051 //STBTT_DEF ubyte *stbtt_GetGlyphBitmap(const(stbtt_fontinfo)* info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); 1052 //STBTT_DEF 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); 1053 //STBTT_DEF 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); 1054 //STBTT_DEF 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); 1055 //STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(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 oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); 1056 //STBTT_DEF void stbtt_GetGlyphBitmapBox(const(stbtt_fontinfo)* font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); 1057 //STBTT_DEF 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); 1058 1059 1060 // @TODO: don't expose this structure 1061 struct stbtt__bitmap { 1062 int w,h,stride; 1063 ubyte *pixels; 1064 } 1065 1066 // rasterize a shape with quadratic beziers into a bitmap 1067 /+ 1068 STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into 1069 float flatness_in_pixels, // allowable error of curve in pixels 1070 stbtt_vertex *vertices, // array of vertices defining shape 1071 int num_verts, // number of vertices in above array 1072 float scale_x, float scale_y, // scale applied to input vertices 1073 float shift_x, float shift_y, // translation applied to input vertices 1074 int x_off, int y_off, // another translation applied to input 1075 int invert, // if non-zero, vertically flip shape 1076 void *userdata); // context for to STBTT_MALLOC 1077 +/ 1078 1079 // //////////////////////////////////////////////////////////////////////////// 1080 // 1081 // Signed Distance Function (or Field) rendering 1082 1083 //STBTT_DEF void stbtt_FreeSDF(ubyte *bitmap, void *userdata); 1084 // frees the SDF bitmap allocated below 1085 1086 //STBTT_DEF ubyte * stbtt_GetGlyphSDF(const(stbtt_fontinfo)* info, float scale, int glyph, int padding, ubyte onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); 1087 //STBTT_DEF ubyte * stbtt_GetCodepointSDF(const(stbtt_fontinfo)* info, float scale, int codepoint, int padding, ubyte onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); 1088 // These functions compute a discretized SDF field for a single character, suitable for storing 1089 // in a single-channel texture, sampling with bilinear filtering, and testing against 1090 // larger than some threshhold to produce scalable fonts. 1091 // info -- the font 1092 // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap 1093 // glyph/codepoint -- the character to generate the SDF for 1094 // padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), 1095 // which allows effects like bit outlines 1096 // onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) 1097 // pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) 1098 // if positive, > onedge_value is inside; if negative, < onedge_value is inside 1099 // width,height -- output height & width of the SDF bitmap (including padding) 1100 // xoff,yoff -- output origin of the character 1101 // return value -- a 2D array of bytes 0..255, width*height in size 1102 // 1103 // pixel_dist_scale & onedge_value are a scale & bias that allows you to make 1104 // optimal use of the limited 0..255 for your application, trading off precision 1105 // and special effects. SDF values outside the range 0..255 are clamped to 0..255. 1106 // 1107 // Example: 1108 // scale = stbtt_ScaleForPixelHeight(22) 1109 // padding = 5 1110 // onedge_value = 180 1111 // pixel_dist_scale = 180/5.0 = 36.0 1112 // 1113 // This will create an SDF bitmap in which the character is about 22 pixels 1114 // high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled 1115 // shape, sample the SDF at each pixel and fill the pixel if the SDF value 1116 // is greater than or equal to 180/255. (You'll actually want to antialias, 1117 // which is beyond the scope of this example.) Additionally, you can compute 1118 // offset outlines (e.g. to stroke the character border inside & outside, 1119 // or only outside). For example, to fill outside the character up to 3 SDF 1120 // pixels, you would compare against (180-36.0*3)/255 = 72/255. The above 1121 // choice of variables maps a range from 5 pixels outside the shape to 1122 // 2 pixels inside the shape to 0..255; this is intended primarily for apply 1123 // outside effects only (the interior range is needed to allow proper 1124 // antialiasing of the font at *smaller* sizes) 1125 // 1126 // The function computes the SDF analytically at each SDF pixel, not by e.g. 1127 // building a higher-res bitmap and approximating it. In theory the quality 1128 // should be as high as possible for an SDF of this size & representation, but 1129 // unclear if this is true in practice (perhaps building a higher-res bitmap 1130 // and computing from that can allow drop-out prevention). 1131 // 1132 // The algorithm has not been optimized at all, so expect it to be slow 1133 // if computing lots of characters or very large sizes. 1134 1135 1136 1137 ////////////////////////////////////////////////////////////////////////////// 1138 // 1139 // Finding the right font... 1140 // 1141 // You should really just solve this offline, keep your own tables 1142 // of what font is what, and don't try to get it out of the .ttf file. 1143 // That's because getting it out of the .ttf file is really hard, because 1144 // the names in the file can appear in many possible encodings, in many 1145 // possible languages, and e.g. if you need a case-insensitive comparison, 1146 // the details of that depend on the encoding & language in a complex way 1147 // (actually underspecified in truetype, but also gigantic). 1148 // 1149 // But you can use the provided functions in two possible ways: 1150 // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on 1151 // unicode-encoded names to try to find the font you want; 1152 // you can run this before calling stbtt_InitFont() 1153 // 1154 // stbtt_GetFontNameString() lets you get any of the various strings 1155 // from the file yourself and do your own comparisons on them. 1156 // You have to have called stbtt_InitFont() first. 1157 1158 1159 //STBTT_DEF int stbtt_FindMatchingFont(const(ubyte)* fontdata, const(char)* name, int flags); 1160 // returns the offset (not index) of the font that matches, or -1 if none 1161 // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". 1162 // if you use any other flag, use a font name like "Arial"; this checks 1163 // the 'macStyle' header field; i don't know if fonts set this consistently 1164 enum { 1165 STBTT_MACSTYLE_DONTCARE = 0, 1166 STBTT_MACSTYLE_BOLD = 1, 1167 STBTT_MACSTYLE_ITALIC = 2, 1168 STBTT_MACSTYLE_UNDERSCORE = 4, 1169 STBTT_MACSTYLE_NONE = 8, // <= not same as 0, this makes us check the bitfield is 0 1170 } 1171 1172 //STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const(char)* s1, int len1, const(char)* s2, int len2); 1173 // returns 1/0 whether the first string interpreted as utf8 is identical to 1174 // the second string interpreted as big-endian utf16... useful for strings from next func 1175 1176 //STBTT_DEF const(char)* stbtt_GetFontNameString(const(stbtt_fontinfo)* font, int *length, int platformID, int encodingID, int languageID, int nameID); 1177 // returns the string (which may be big-endian double byte, e.g. for unicode) 1178 // and puts the length in bytes in *length. 1179 // 1180 // some of the values for the IDs are below; for more see the truetype spec: 1181 // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html 1182 // http://www.microsoft.com/typography/otspec/name.htm 1183 1184 enum { // platformID 1185 STBTT_PLATFORM_ID_UNICODE =0, 1186 STBTT_PLATFORM_ID_MAC =1, 1187 STBTT_PLATFORM_ID_ISO =2, 1188 STBTT_PLATFORM_ID_MICROSOFT =3 1189 } 1190 1191 enum { // encodingID for STBTT_PLATFORM_ID_UNICODE 1192 STBTT_UNICODE_EID_UNICODE_1_0 =0, 1193 STBTT_UNICODE_EID_UNICODE_1_1 =1, 1194 STBTT_UNICODE_EID_ISO_10646 =2, 1195 STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, 1196 STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 1197 } 1198 1199 enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT 1200 STBTT_MS_EID_SYMBOL =0, 1201 STBTT_MS_EID_UNICODE_BMP =1, 1202 STBTT_MS_EID_SHIFTJIS =2, 1203 STBTT_MS_EID_UNICODE_FULL =10 1204 } 1205 1206 enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes 1207 STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, 1208 STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, 1209 STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, 1210 STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 1211 } 1212 1213 enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... 1214 // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs 1215 STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, 1216 STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, 1217 STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, 1218 STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, 1219 STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, 1220 STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D 1221 } 1222 1223 enum { // languageID for STBTT_PLATFORM_ID_MAC 1224 STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, 1225 STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, 1226 STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, 1227 STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , 1228 STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , 1229 STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, 1230 STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 1231 } 1232 1233 1234 // ///////////////////////////////////////////////////////////////////////////// 1235 // ///////////////////////////////////////////////////////////////////////////// 1236 // // 1237 // // IMPLEMENTATION 1238 // // 1239 // // 1240 private: 1241 1242 enum STBTT_MAX_OVERSAMPLE = 8; // it also must be POT 1243 static assert(STBTT_MAX_OVERSAMPLE > 0 && STBTT_MAX_OVERSAMPLE <= 255, "STBTT_MAX_OVERSAMPLE cannot be > 255"); 1244 1245 //typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; 1246 1247 enum STBTT_RASTERIZER_VERSION = 2; 1248 1249 /* 1250 #ifdef _MSC_VER 1251 #define STBTT__NOTUSED(v) (void)(v) 1252 #else 1253 #define STBTT__NOTUSED(v) (void)sizeof(v) 1254 #endif 1255 */ 1256 1257 // //////////////////////////////////////////////////////////////////////// 1258 // 1259 // stbtt__buf helpers to parse data from file 1260 // 1261 1262 private stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) 1263 { 1264 if (b.cursor >= b.size) 1265 return 0; 1266 return b.data[b.cursor++]; 1267 } 1268 1269 private stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) 1270 { 1271 if (b.cursor >= b.size) 1272 return 0; 1273 return b.data[b.cursor]; 1274 } 1275 1276 private void stbtt__buf_seek(stbtt__buf *b, int o) 1277 { 1278 assert(!(o > b.size || o < 0)); 1279 b.cursor = (o > b.size || o < 0) ? b.size : o; 1280 } 1281 1282 private void stbtt__buf_skip(stbtt__buf *b, int o) 1283 { 1284 stbtt__buf_seek(b, b.cursor + o); 1285 } 1286 1287 private stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) 1288 { 1289 stbtt_uint32 v = 0; 1290 int i; 1291 assert(n >= 1 && n <= 4); 1292 for (i = 0; i < n; i++) 1293 v = (v << 8) | stbtt__buf_get8(b); 1294 return v; 1295 } 1296 1297 private stbtt__buf stbtt__new_buf(const(void)* p, size_t size) 1298 { 1299 stbtt__buf r; 1300 assert(size < 0x40000000); 1301 r.data = cast(stbtt_uint8*) p; 1302 r.size = cast(int) size; 1303 r.cursor = 0; 1304 return r; 1305 } 1306 1307 //#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) 1308 //#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) 1309 ushort stbtt__buf_get16 (stbtt__buf *b) { pragma(inline, true); return cast(ushort)stbtt__buf_get(b, 2); } 1310 uint stbtt__buf_get32 (stbtt__buf *b) { pragma(inline, true); return cast(uint)stbtt__buf_get(b, 4); } 1311 1312 private stbtt__buf stbtt__buf_range(const(stbtt__buf)* b, int o, int s) 1313 { 1314 stbtt__buf r = stbtt__new_buf(null, 0); 1315 if (o < 0 || s < 0 || o > b.size || s > b.size - o) return r; 1316 r.data = cast(ubyte*)b.data + o; 1317 r.size = s; 1318 return r; 1319 } 1320 1321 private stbtt__buf stbtt__cff_get_index(stbtt__buf *b) 1322 { 1323 int count, start, offsize; 1324 start = b.cursor; 1325 count = stbtt__buf_get16(b); 1326 if (count) { 1327 offsize = stbtt__buf_get8(b); 1328 assert(offsize >= 1 && offsize <= 4); 1329 stbtt__buf_skip(b, offsize * count); 1330 stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); 1331 } 1332 return stbtt__buf_range(b, start, b.cursor - start); 1333 } 1334 1335 private stbtt_uint32 stbtt__cff_int(stbtt__buf *b) 1336 { 1337 int b0 = stbtt__buf_get8(b); 1338 if (b0 >= 32 && b0 <= 246) return b0 - 139; 1339 else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; 1340 else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; 1341 else if (b0 == 28) return stbtt__buf_get16(b); 1342 else if (b0 == 29) return stbtt__buf_get32(b); 1343 assert(0); 1344 } 1345 1346 private void stbtt__cff_skip_operand(stbtt__buf *b) { 1347 int v, b0 = stbtt__buf_peek8(b); 1348 assert(b0 >= 28); 1349 if (b0 == 30) { 1350 stbtt__buf_skip(b, 1); 1351 while (b.cursor < b.size) { 1352 v = stbtt__buf_get8(b); 1353 if ((v & 0xF) == 0xF || (v >> 4) == 0xF) 1354 break; 1355 } 1356 } else { 1357 stbtt__cff_int(b); 1358 } 1359 } 1360 1361 private stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) 1362 { 1363 stbtt__buf_seek(b, 0); 1364 while (b.cursor < b.size) { 1365 int start = b.cursor, end, op; 1366 while (stbtt__buf_peek8(b) >= 28) 1367 stbtt__cff_skip_operand(b); 1368 end = b.cursor; 1369 op = stbtt__buf_get8(b); 1370 if (op == 12) op = stbtt__buf_get8(b) | 0x100; 1371 if (op == key) return stbtt__buf_range(b, start, end-start); 1372 } 1373 return stbtt__buf_range(b, 0, 0); 1374 } 1375 1376 private void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *outstb) 1377 { 1378 int i; 1379 stbtt__buf operands = stbtt__dict_get(b, key); 1380 for (i = 0; i < outcount && operands.cursor < operands.size; i++) 1381 outstb[i] = stbtt__cff_int(&operands); 1382 } 1383 1384 private int stbtt__cff_index_count(stbtt__buf *b) 1385 { 1386 stbtt__buf_seek(b, 0); 1387 return stbtt__buf_get16(b); 1388 } 1389 1390 private stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) 1391 { 1392 int count, offsize, start, end; 1393 stbtt__buf_seek(&b, 0); 1394 count = stbtt__buf_get16(&b); 1395 offsize = stbtt__buf_get8(&b); 1396 assert(i >= 0 && i < count); 1397 assert(offsize >= 1 && offsize <= 4); 1398 stbtt__buf_skip(&b, i*offsize); 1399 start = stbtt__buf_get(&b, offsize); 1400 end = stbtt__buf_get(&b, offsize); 1401 return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); 1402 } 1403 1404 ////////////////////////////////////////////////////////////////////////// 1405 // 1406 // accessors to parse data from file 1407 // 1408 1409 // on platforms that don't allow misaligned reads, if we want to allow 1410 // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE 1411 1412 //#define ttBYTE(p) (* (stbtt_uint8 *) (p)) 1413 //#define ttCHAR(p) (* (stbtt_int8 *) (p)) 1414 //#define ttFixed(p) ttLONG(p) 1415 stbtt_uint8 ttBYTE (const(void)* p) pure { pragma(inline, true); return *cast(const(stbtt_uint8)*)p; } 1416 stbtt_int8 ttCHAR (const(void)* p) pure { pragma(inline, true); return *cast(const(stbtt_int8)*)p; } 1417 1418 private stbtt_uint16 ttUSHORT(const(stbtt_uint8)* p) { return p[0]*256 + p[1]; } 1419 private stbtt_int16 ttSHORT(const(stbtt_uint8)* p) { return cast(stbtt_int16)(p[0]*256 + p[1]); } 1420 private stbtt_uint32 ttULONG(const(stbtt_uint8)* p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 1421 private stbtt_int32 ttLONG(const(stbtt_uint8)* p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 1422 1423 //#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) 1424 //#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) 1425 1426 bool stbtt_tag4 (const(void)* p, ubyte c0, ubyte c1, ubyte c2, ubyte c3) pure { 1427 return 1428 (cast(const(ubyte)*)p)[0] == c0 && 1429 (cast(const(ubyte)*)p)[1] == c1 && 1430 (cast(const(ubyte)*)p)[2] == c2 && 1431 (cast(const(ubyte)*)p)[3] == c3; 1432 } 1433 1434 bool stbtt_tag (const(void)* p, const(void)* str) { 1435 //stbtt_tag4(p,str[0],str[1],str[2],str[3]) 1436 import core.stdc.string : memcmp; 1437 return (memcmp(p, str, 4) == 0); 1438 } 1439 1440 private int stbtt__isfont(stbtt_uint8 *font) 1441 { 1442 // check the version number 1443 if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 1444 if (stbtt_tag(font, "typ1".ptr)) return 1; // TrueType with type 1 font -- we don't support this! 1445 if (stbtt_tag(font, "OTTO".ptr)) return 1; // OpenType with CFF 1446 if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 1447 if (stbtt_tag(font, "true".ptr)) return 1; // Apple specification for TrueType fonts 1448 return 0; 1449 } 1450 1451 // @OPTIMIZE: binary search 1452 private stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const(char)* tag) 1453 { 1454 stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); 1455 stbtt_uint32 tabledir = fontstart + 12; 1456 stbtt_int32 i; 1457 for (i=0; i < num_tables; ++i) { 1458 stbtt_uint32 loc = tabledir + 16*i; 1459 if (stbtt_tag(data+loc+0, tag)) 1460 return ttULONG(data+loc+8); 1461 } 1462 return 0; 1463 } 1464 1465 private int stbtt_GetFontOffsetForIndex_internal(ubyte *font_collection, int index) 1466 { 1467 // if it's just a font, there's only one valid index 1468 if (stbtt__isfont(font_collection)) 1469 return index == 0 ? 0 : -1; 1470 1471 // check if it's a TTC 1472 if (stbtt_tag(font_collection, "ttcf".ptr)) { 1473 // version 1? 1474 if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { 1475 stbtt_int32 n = ttLONG(font_collection+8); 1476 if (index >= n) 1477 return -1; 1478 return ttULONG(font_collection+12+index*4); 1479 } 1480 } 1481 return -1; 1482 } 1483 1484 private int stbtt_GetNumberOfFonts_internal(ubyte *font_collection) 1485 { 1486 // if it's just a font, there's only one valid font 1487 if (stbtt__isfont(font_collection)) 1488 return 1; 1489 1490 // check if it's a TTC 1491 if (stbtt_tag(font_collection, "ttcf".ptr)) { 1492 // version 1? 1493 if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { 1494 return ttLONG(font_collection+8); 1495 } 1496 } 1497 return 0; 1498 } 1499 1500 private stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) 1501 { 1502 stbtt_uint32 subrsoff = 0; 1503 stbtt_uint32[2] private_loc = 0; 1504 stbtt__buf pdict; 1505 stbtt__dict_get_ints(&fontdict, 18, 2, private_loc.ptr); 1506 if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(null, 0); 1507 pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); 1508 stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); 1509 if (!subrsoff) return stbtt__new_buf(null, 0); 1510 stbtt__buf_seek(&cff, private_loc[1]+subrsoff); 1511 return stbtt__cff_get_index(&cff); 1512 } 1513 1514 private int stbtt_InitFont_internal(stbtt_fontinfo *info, ubyte *data, int fontstart) 1515 { 1516 stbtt_uint32 cmap, t; 1517 stbtt_int32 i,numTables; 1518 1519 info.data = data; 1520 info.fontstart = fontstart; 1521 info.cff = stbtt__new_buf(null, 0); 1522 1523 cmap = stbtt__find_table(data, fontstart, "cmap"); // required 1524 info.loca = stbtt__find_table(data, fontstart, "loca"); // required 1525 info.head = stbtt__find_table(data, fontstart, "head"); // required 1526 info.glyf = stbtt__find_table(data, fontstart, "glyf"); // required 1527 info.hhea = stbtt__find_table(data, fontstart, "hhea"); // required 1528 info.hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required 1529 info.kern = stbtt__find_table(data, fontstart, "kern"); // not required 1530 info.gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required 1531 1532 if (!cmap || !info.head || !info.hhea || !info.hmtx) 1533 return 0; 1534 if (info.glyf) { 1535 // required for truetype 1536 if (!info.loca) return 0; 1537 } else { 1538 // initialization for CFF / Type2 fonts (OTF) 1539 stbtt__buf b, topdict, topdictidx; 1540 stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; 1541 stbtt_uint32 cff; 1542 1543 cff = stbtt__find_table(data, fontstart, "CFF "); 1544 if (!cff) return 0; 1545 1546 info.fontdicts = stbtt__new_buf(null, 0); 1547 info.fdselect = stbtt__new_buf(null, 0); 1548 1549 // @TODO this should use size from table (not 512MB) 1550 info.cff = stbtt__new_buf(data+cff, 512*1024*1024); 1551 b = info.cff; 1552 1553 // read the header 1554 stbtt__buf_skip(&b, 2); 1555 stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize 1556 1557 // @TODO the name INDEX could list multiple fonts, 1558 // but we just use the first one. 1559 stbtt__cff_get_index(&b); // name INDEX 1560 topdictidx = stbtt__cff_get_index(&b); 1561 topdict = stbtt__cff_index_get(topdictidx, 0); 1562 stbtt__cff_get_index(&b); // string INDEX 1563 info.gsubrs = stbtt__cff_get_index(&b); 1564 1565 stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); 1566 stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); 1567 stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); 1568 stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); 1569 info.subrs = stbtt__get_subrs(b, topdict); 1570 1571 // we only support Type 2 charstrings 1572 if (cstype != 2) return 0; 1573 if (charstrings == 0) return 0; 1574 1575 if (fdarrayoff) { 1576 // looks like a CID font 1577 if (!fdselectoff) return 0; 1578 stbtt__buf_seek(&b, fdarrayoff); 1579 info.fontdicts = stbtt__cff_get_index(&b); 1580 info.fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); 1581 } 1582 1583 stbtt__buf_seek(&b, charstrings); 1584 info.charstrings = stbtt__cff_get_index(&b); 1585 } 1586 1587 t = stbtt__find_table(data, fontstart, "maxp"); 1588 if (t) 1589 info.numGlyphs = ttUSHORT(data+t+4); 1590 else 1591 info.numGlyphs = 0xffff; 1592 1593 // find a cmap encoding table we understand *now* to avoid searching 1594 // later. (todo: could make this installable) 1595 // the same regardless of glyph. 1596 numTables = ttUSHORT(data + cmap + 2); 1597 info.index_map = 0; 1598 for (i=0; i < numTables; ++i) { 1599 stbtt_uint32 encoding_record = cmap + 4 + 8 * i; 1600 // find an encoding we understand: 1601 switch(ttUSHORT(data+encoding_record)) { 1602 case STBTT_PLATFORM_ID_MICROSOFT: 1603 switch (ttUSHORT(data+encoding_record+2)) { 1604 case STBTT_MS_EID_UNICODE_BMP: 1605 case STBTT_MS_EID_UNICODE_FULL: 1606 // MS/Unicode 1607 info.index_map = cmap + ttULONG(data+encoding_record+4); 1608 break; 1609 default: 1610 } 1611 break; 1612 case STBTT_PLATFORM_ID_UNICODE: 1613 // Mac/iOS has these 1614 // all the encodingIDs are unicode, so we don't bother to check it 1615 info.index_map = cmap + ttULONG(data+encoding_record+4); 1616 break; 1617 default: 1618 } 1619 } 1620 if (info.index_map == 0) 1621 return 0; 1622 1623 info.indexToLocFormat = ttUSHORT(data+info.head + 50); 1624 return 1; 1625 } 1626 1627 public int stbtt_FindGlyphIndex(const(stbtt_fontinfo)* info, int unicode_codepoint) 1628 { 1629 stbtt_uint8 *data = cast(stbtt_uint8*)info.data; 1630 stbtt_uint32 index_map = info.index_map; 1631 1632 stbtt_uint16 format = ttUSHORT(data + index_map + 0); 1633 if (format == 0) { // apple byte encoding 1634 stbtt_int32 bytes = ttUSHORT(data + index_map + 2); 1635 if (unicode_codepoint < bytes-6) 1636 return ttBYTE(data + index_map + 6 + unicode_codepoint); 1637 return 0; 1638 } else if (format == 6) { 1639 stbtt_uint32 first = ttUSHORT(data + index_map + 6); 1640 stbtt_uint32 count = ttUSHORT(data + index_map + 8); 1641 if (cast(stbtt_uint32) unicode_codepoint >= first && cast(stbtt_uint32) unicode_codepoint < first+count) 1642 return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); 1643 return 0; 1644 } else if (format == 2) { 1645 assert(0); // @TODO: high-byte mapping for japanese/chinese/korean 1646 } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges 1647 stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; 1648 stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; 1649 stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); 1650 stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; 1651 1652 // do a binary search of the segments 1653 stbtt_uint32 endCount = index_map + 14; 1654 stbtt_uint32 search = endCount; 1655 1656 if (unicode_codepoint > 0xffff) 1657 return 0; 1658 1659 // they lie from endCount .. endCount + segCount 1660 // but searchRange is the nearest power of two, so... 1661 if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) 1662 search += rangeShift*2; 1663 1664 // now decrement to bias correctly to find smallest 1665 search -= 2; 1666 while (entrySelector) { 1667 stbtt_uint16 end; 1668 searchRange >>= 1; 1669 end = ttUSHORT(data + search + searchRange*2); 1670 if (unicode_codepoint > end) 1671 search += searchRange*2; 1672 --entrySelector; 1673 } 1674 search += 2; 1675 1676 { 1677 stbtt_uint16 offset, start; 1678 stbtt_uint16 item = cast(stbtt_uint16) ((search - endCount) >> 1); 1679 1680 assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); 1681 start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); 1682 if (unicode_codepoint < start) 1683 return 0; 1684 1685 offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); 1686 if (offset == 0) 1687 return cast(stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); 1688 1689 return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); 1690 } 1691 } else if (format == 12 || format == 13) { 1692 stbtt_uint32 ngroups = ttULONG(data+index_map+12); 1693 stbtt_int32 low,high; 1694 low = 0; high = cast(stbtt_int32)ngroups; 1695 // Binary search the right group. 1696 while (low < high) { 1697 stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high 1698 stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); 1699 stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); 1700 if (cast(stbtt_uint32) unicode_codepoint < start_char) 1701 high = mid; 1702 else if (cast(stbtt_uint32) unicode_codepoint > end_char) 1703 low = mid+1; 1704 else { 1705 stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); 1706 if (format == 12) 1707 return start_glyph + unicode_codepoint-start_char; 1708 else // format == 13 1709 return start_glyph; 1710 } 1711 } 1712 return 0; // not found 1713 } 1714 // @TODO 1715 assert(0); 1716 } 1717 1718 public int stbtt_GetCodepointShape(stbtt_fontinfo* info, int unicode_codepoint, stbtt_vertex **vertices) 1719 { 1720 return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); 1721 } 1722 1723 private void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) 1724 { 1725 v.type = type; 1726 v.x = cast(stbtt_int16) x; 1727 v.y = cast(stbtt_int16) y; 1728 v.cx = cast(stbtt_int16) cx; 1729 v.cy = cast(stbtt_int16) cy; 1730 } 1731 1732 private int stbtt__GetGlyfOffset(const(stbtt_fontinfo)* info, int glyph_index) 1733 { 1734 int g1,g2; 1735 1736 assert(!info.cff.size); 1737 1738 if (glyph_index >= info.numGlyphs) return -1; // glyph index out of range 1739 if (info.indexToLocFormat >= 2) return -1; // unknown index->glyph map format 1740 1741 if (info.indexToLocFormat == 0) { 1742 g1 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2) * 2; 1743 g2 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2 + 2) * 2; 1744 } else { 1745 g1 = info.glyf + ttULONG (info.data + info.loca + glyph_index * 4); 1746 g2 = info.glyf + ttULONG (info.data + info.loca + glyph_index * 4 + 4); 1747 } 1748 1749 return g1==g2 ? -1 : g1; // if length is 0, return -1 1750 } 1751 1752 //private int stbtt__GetGlyphInfoT2(const(stbtt_fontinfo)* info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 1753 1754 public int stbtt_GetGlyphBox(stbtt_fontinfo* info, int glyph_index, int *x0, int *y0, int *x1, int *y1) 1755 { 1756 if (info.cff.size) { 1757 stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); 1758 } else { 1759 int g = stbtt__GetGlyfOffset(info, glyph_index); 1760 if (g < 0) return 0; 1761 1762 if (x0) *x0 = ttSHORT(info.data + g + 2); 1763 if (y0) *y0 = ttSHORT(info.data + g + 4); 1764 if (x1) *x1 = ttSHORT(info.data + g + 6); 1765 if (y1) *y1 = ttSHORT(info.data + g + 8); 1766 } 1767 return 1; 1768 } 1769 1770 public int stbtt_GetCodepointBox(stbtt_fontinfo* info, int codepoint, int *x0, int *y0, int *x1, int *y1) 1771 { 1772 return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); 1773 } 1774 1775 public int stbtt_IsGlyphEmpty(stbtt_fontinfo* info, int glyph_index) 1776 { 1777 stbtt_int16 numberOfContours; 1778 int g; 1779 if (info.cff.size) 1780 return stbtt__GetGlyphInfoT2(info, glyph_index, null, null, null, null) == 0; 1781 g = stbtt__GetGlyfOffset(info, glyph_index); 1782 if (g < 0) return 1; 1783 numberOfContours = ttSHORT(info.data + g); 1784 return numberOfContours == 0; 1785 } 1786 1787 private int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, 1788 stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) 1789 { 1790 if (start_off) { 1791 if (was_off) 1792 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); 1793 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); 1794 } else { 1795 if (was_off) 1796 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); 1797 else 1798 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); 1799 } 1800 return num_vertices; 1801 } 1802 1803 private int stbtt__GetGlyphShapeTT(stbtt_fontinfo* info, int glyph_index, stbtt_vertex **pvertices) 1804 { 1805 stbtt_int16 numberOfContours; 1806 stbtt_uint8 *endPtsOfContours; 1807 stbtt_uint8 *data = cast(stbtt_uint8*)info.data; 1808 stbtt_vertex *vertices = null; 1809 int num_vertices=0; 1810 int g = stbtt__GetGlyfOffset(info, glyph_index); 1811 1812 *pvertices = null; 1813 1814 if (g < 0) return 0; 1815 1816 numberOfContours = ttSHORT(data + g); 1817 1818 if (numberOfContours > 0) { 1819 stbtt_uint8 flags=0,flagcount; 1820 stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; 1821 stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; 1822 stbtt_uint8 *points; 1823 endPtsOfContours = (data + g + 10); 1824 ins = ttUSHORT(data + g + 10 + numberOfContours * 2); 1825 points = data + g + 10 + numberOfContours * 2 + 2 + ins; 1826 1827 n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); 1828 1829 m = n + 2*numberOfContours; // a loose bound on how many vertices we might need 1830 vertices = cast(stbtt_vertex *) STBTT_malloc(m * cast(uint)vertices[0].sizeof, info.userdata); 1831 if (vertices is null) 1832 return 0; 1833 1834 next_move = 0; 1835 flagcount=0; 1836 1837 // in first pass, we load uninterpreted data into the allocated array 1838 // above, shifted to the end of the array so we won't overwrite it when 1839 // we create our final data starting from the front 1840 1841 off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated 1842 1843 // first load flags 1844 1845 for (i=0; i < n; ++i) { 1846 if (flagcount == 0) { 1847 flags = *points++; 1848 if (flags & 8) 1849 flagcount = *points++; 1850 } else 1851 --flagcount; 1852 vertices[off+i].type = flags; 1853 } 1854 1855 // now load x coordinates 1856 x=0; 1857 for (i=0; i < n; ++i) { 1858 flags = vertices[off+i].type; 1859 if (flags & 2) { 1860 stbtt_int16 dx = *points++; 1861 x += (flags & 16) ? cast(int)dx : -cast(int)dx; // ??? 1862 } else { 1863 if (!(flags & 16)) { 1864 x = x + cast(stbtt_int16) (points[0]*256 + points[1]); 1865 points += 2; 1866 } 1867 } 1868 vertices[off+i].x = cast(stbtt_int16) x; 1869 } 1870 1871 // now load y coordinates 1872 y=0; 1873 for (i=0; i < n; ++i) { 1874 flags = vertices[off+i].type; 1875 if (flags & 4) { 1876 stbtt_int16 dy = *points++; 1877 y += (flags & 32) ? cast(int)dy : -cast(int)dy; // ??? 1878 } else { 1879 if (!(flags & 32)) { 1880 y = y + cast(stbtt_int16) (points[0]*256 + points[1]); 1881 points += 2; 1882 } 1883 } 1884 vertices[off+i].y = cast(stbtt_int16) y; 1885 } 1886 1887 // now convert them to our format 1888 num_vertices=0; 1889 sx = sy = cx = cy = scx = scy = 0; 1890 for (i=0; i < n; ++i) { 1891 flags = vertices[off+i].type; 1892 x = cast(stbtt_int16) vertices[off+i].x; 1893 y = cast(stbtt_int16) vertices[off+i].y; 1894 1895 if (next_move == i) { 1896 if (i != 0) 1897 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1898 1899 // now start the new one 1900 start_off = !(flags & 1); 1901 if (start_off) { 1902 // if we start off with an off-curve point, then when we need to find a point on the curve 1903 // where we can start, and we need to save some state for when we wraparound. 1904 scx = x; 1905 scy = y; 1906 if (!(vertices[off+i+1].type & 1)) { 1907 // next point is also a curve point, so interpolate an on-point curve 1908 sx = (x + cast(stbtt_int32) vertices[off+i+1].x) >> 1; 1909 sy = (y + cast(stbtt_int32) vertices[off+i+1].y) >> 1; 1910 } else { 1911 // otherwise just use the next point as our start point 1912 sx = cast(stbtt_int32) vertices[off+i+1].x; 1913 sy = cast(stbtt_int32) vertices[off+i+1].y; 1914 ++i; // we're using point i+1 as the starting point, so skip it 1915 } 1916 } else { 1917 sx = x; 1918 sy = y; 1919 } 1920 stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); 1921 was_off = 0; 1922 next_move = 1 + ttUSHORT(endPtsOfContours+j*2); 1923 ++j; 1924 } else { 1925 if (!(flags & 1)) { // if it's a curve 1926 if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint 1927 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); 1928 cx = x; 1929 cy = y; 1930 was_off = 1; 1931 } else { 1932 if (was_off) 1933 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); 1934 else 1935 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); 1936 was_off = 0; 1937 } 1938 } 1939 } 1940 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1941 } else if (numberOfContours == -1) { 1942 // Compound shapes. 1943 int more = 1; 1944 stbtt_uint8 *comp = data + g + 10; 1945 num_vertices = 0; 1946 vertices = null; 1947 while (more) { 1948 stbtt_uint16 flags, gidx; 1949 int comp_num_verts = 0, i; 1950 stbtt_vertex *comp_verts = null, tmp = null; 1951 float[6] mtx = [1,0,0,1,0,0]; 1952 float m, n; 1953 1954 flags = ttSHORT(comp); comp+=2; 1955 gidx = ttSHORT(comp); comp+=2; 1956 1957 if (flags & 2) { // XY values 1958 if (flags & 1) { // shorts 1959 mtx[4] = ttSHORT(comp); comp+=2; 1960 mtx[5] = ttSHORT(comp); comp+=2; 1961 } else { 1962 mtx[4] = ttCHAR(comp); comp+=1; 1963 mtx[5] = ttCHAR(comp); comp+=1; 1964 } 1965 } 1966 else { 1967 // @TODO handle matching point 1968 assert(0); 1969 } 1970 if (flags & (1<<3)) { // WE_HAVE_A_SCALE 1971 mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1972 mtx[1] = mtx[2] = 0; 1973 } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE 1974 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 1975 mtx[1] = mtx[2] = 0; 1976 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1977 } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO 1978 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 1979 mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; 1980 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 1981 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1982 } 1983 1984 // Find transformation scales. 1985 m = cast(float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 1986 n = cast(float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); 1987 1988 // Get indexed glyph. 1989 comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); 1990 if (comp_num_verts > 0) { 1991 // Transform vertices. 1992 for (i = 0; i < comp_num_verts; ++i) { 1993 stbtt_vertex* v = &comp_verts[i]; 1994 stbtt_vertex_type x,y; 1995 x=v.x; y=v.y; 1996 v.x = cast(stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 1997 v.y = cast(stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 1998 x=v.cx; y=v.cy; 1999 v.cx = cast(stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 2000 v.cy = cast(stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 2001 } 2002 // Append vertices. 2003 tmp = cast(stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*cast(uint)stbtt_vertex.sizeof, info.userdata); 2004 if (!tmp) { 2005 if (vertices) STBTT_free(vertices, info.userdata); 2006 if (comp_verts) STBTT_free(comp_verts, info.userdata); 2007 return 0; 2008 } 2009 if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*cast(uint)stbtt_vertex.sizeof); 2010 STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*cast(uint)stbtt_vertex.sizeof); 2011 if (vertices) STBTT_free(vertices, info.userdata); 2012 vertices = tmp; 2013 STBTT_free(comp_verts, info.userdata); 2014 num_vertices += comp_num_verts; 2015 } 2016 // More components ? 2017 more = flags & (1<<5); 2018 } 2019 } else if (numberOfContours < 0) { 2020 // @TODO other compound variations? 2021 assert(0); 2022 } else { 2023 // numberOfCounters == 0, do nothing 2024 } 2025 2026 *pvertices = vertices; 2027 return num_vertices; 2028 } 2029 2030 struct stbtt__csctx { 2031 int bounds; 2032 int started; 2033 float first_x, first_y; 2034 float x, y; 2035 stbtt_int32 min_x, max_x, min_y, max_y; 2036 2037 stbtt_vertex *pvertices; 2038 int num_vertices; 2039 } 2040 2041 //#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, null, 0} 2042 2043 private void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) 2044 { 2045 if (x > c.max_x || !c.started) c.max_x = x; 2046 if (y > c.max_y || !c.started) c.max_y = y; 2047 if (x < c.min_x || !c.started) c.min_x = x; 2048 if (y < c.min_y || !c.started) c.min_y = y; 2049 c.started = 1; 2050 } 2051 2052 private void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) 2053 { 2054 if (c.bounds) { 2055 stbtt__track_vertex(c, x, y); 2056 if (type == STBTT_vcubic) { 2057 stbtt__track_vertex(c, cx, cy); 2058 stbtt__track_vertex(c, cx1, cy1); 2059 } 2060 } else { 2061 stbtt_setvertex(&c.pvertices[c.num_vertices], type, x, y, cx, cy); 2062 c.pvertices[c.num_vertices].cx1 = cast(stbtt_int16) cx1; 2063 c.pvertices[c.num_vertices].cy1 = cast(stbtt_int16) cy1; 2064 } 2065 c.num_vertices++; 2066 } 2067 2068 private void stbtt__csctx_close_shape(stbtt__csctx *ctx) 2069 { 2070 if (ctx.first_x != ctx.x || ctx.first_y != ctx.y) 2071 stbtt__csctx_v(ctx, STBTT_vline, cast(int)ctx.first_x, cast(int)ctx.first_y, 0, 0, 0, 0); 2072 } 2073 2074 private void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) 2075 { 2076 stbtt__csctx_close_shape(ctx); 2077 ctx.first_x = ctx.x = ctx.x + dx; 2078 ctx.first_y = ctx.y = ctx.y + dy; 2079 stbtt__csctx_v(ctx, STBTT_vmove, cast(int)ctx.x, cast(int)ctx.y, 0, 0, 0, 0); 2080 } 2081 2082 private void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) 2083 { 2084 ctx.x += dx; 2085 ctx.y += dy; 2086 stbtt__csctx_v(ctx, STBTT_vline, cast(int)ctx.x, cast(int)ctx.y, 0, 0, 0, 0); 2087 } 2088 2089 private void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) 2090 { 2091 float cx1 = ctx.x + dx1; 2092 float cy1 = ctx.y + dy1; 2093 float cx2 = cx1 + dx2; 2094 float cy2 = cy1 + dy2; 2095 ctx.x = cx2 + dx3; 2096 ctx.y = cy2 + dy3; 2097 stbtt__csctx_v(ctx, STBTT_vcubic, cast(int)ctx.x, cast(int)ctx.y, cast(int)cx1, cast(int)cy1, cast(int)cx2, cast(int)cy2); 2098 } 2099 2100 private stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) 2101 { 2102 int count = stbtt__cff_index_count(&idx); 2103 int bias = 107; 2104 if (count >= 33900) 2105 bias = 32768; 2106 else if (count >= 1240) 2107 bias = 1131; 2108 n += bias; 2109 if (n < 0 || n >= count) 2110 return stbtt__new_buf(null, 0); 2111 return stbtt__cff_index_get(idx, n); 2112 } 2113 2114 private stbtt__buf stbtt__cid_get_glyph_subrs(stbtt_fontinfo* info, int glyph_index) 2115 { 2116 stbtt__buf fdselect = info.fdselect; 2117 int nranges, start, end, v, fmt, fdselector = -1, i; 2118 2119 stbtt__buf_seek(&fdselect, 0); 2120 fmt = stbtt__buf_get8(&fdselect); 2121 if (fmt == 0) { 2122 // untested 2123 stbtt__buf_skip(&fdselect, glyph_index); 2124 fdselector = stbtt__buf_get8(&fdselect); 2125 } else if (fmt == 3) { 2126 nranges = stbtt__buf_get16(&fdselect); 2127 start = stbtt__buf_get16(&fdselect); 2128 for (i = 0; i < nranges; i++) { 2129 v = stbtt__buf_get8(&fdselect); 2130 end = stbtt__buf_get16(&fdselect); 2131 if (glyph_index >= start && glyph_index < end) { 2132 fdselector = v; 2133 break; 2134 } 2135 start = end; 2136 } 2137 } 2138 if (fdselector == -1) stbtt__new_buf(null, 0); 2139 return stbtt__get_subrs(info.cff, stbtt__cff_index_get(info.fontdicts, fdselector)); 2140 } 2141 2142 private int stbtt__run_charstring(stbtt_fontinfo* info, int glyph_index, stbtt__csctx *c) 2143 { 2144 int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; 2145 int has_subrs = 0, clear_stack; 2146 float[48] s = void; 2147 stbtt__buf[10] subr_stack = void; 2148 stbtt__buf subrs = info.subrs, b; 2149 float f; 2150 2151 static int STBTT__CSERR(string s) { pragma(inline, true); return 0; } 2152 2153 // this currently ignores the initial width value, which isn't needed if we have hmtx 2154 b = stbtt__cff_index_get(info.charstrings, glyph_index); 2155 while (b.cursor < b.size) { 2156 i = 0; 2157 clear_stack = 1; 2158 b0 = stbtt__buf_get8(&b); 2159 switch (b0) { 2160 // @TODO implement hinting 2161 case 0x13: // hintmask 2162 case 0x14: // cntrmask 2163 if (in_header) 2164 maskbits += (sp / 2); // implicit "vstem" 2165 in_header = 0; 2166 stbtt__buf_skip(&b, (maskbits + 7) / 8); 2167 break; 2168 2169 case 0x01: // hstem 2170 case 0x03: // vstem 2171 case 0x12: // hstemhm 2172 case 0x17: // vstemhm 2173 maskbits += (sp / 2); 2174 break; 2175 2176 case 0x15: // rmoveto 2177 in_header = 0; 2178 if (sp < 2) return STBTT__CSERR("rmoveto stack"); 2179 stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); 2180 break; 2181 case 0x04: // vmoveto 2182 in_header = 0; 2183 if (sp < 1) return STBTT__CSERR("vmoveto stack"); 2184 stbtt__csctx_rmove_to(c, 0, s[sp-1]); 2185 break; 2186 case 0x16: // hmoveto 2187 in_header = 0; 2188 if (sp < 1) return STBTT__CSERR("hmoveto stack"); 2189 stbtt__csctx_rmove_to(c, s[sp-1], 0); 2190 break; 2191 2192 case 0x05: // rlineto 2193 if (sp < 2) return STBTT__CSERR("rlineto stack"); 2194 for (; i + 1 < sp; i += 2) 2195 stbtt__csctx_rline_to(c, s[i], s[i+1]); 2196 break; 2197 2198 // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical 2199 // starting from a different place. 2200 2201 case 0x07: // vlineto 2202 if (sp < 1) return STBTT__CSERR("vlineto stack"); 2203 goto vlineto; 2204 case 0x06: // hlineto 2205 if (sp < 1) return STBTT__CSERR("hlineto stack"); 2206 for (;;) { 2207 if (i >= sp) break; 2208 stbtt__csctx_rline_to(c, s[i], 0); 2209 i++; 2210 vlineto: 2211 if (i >= sp) break; 2212 stbtt__csctx_rline_to(c, 0, s[i]); 2213 i++; 2214 } 2215 break; 2216 2217 case 0x1F: // hvcurveto 2218 if (sp < 4) return STBTT__CSERR("hvcurveto stack"); 2219 goto hvcurveto; 2220 case 0x1E: // vhcurveto 2221 if (sp < 4) return STBTT__CSERR("vhcurveto stack"); 2222 for (;;) { 2223 if (i + 3 >= sp) break; 2224 stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); 2225 i += 4; 2226 hvcurveto: 2227 if (i + 3 >= sp) break; 2228 stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); 2229 i += 4; 2230 } 2231 break; 2232 2233 case 0x08: // rrcurveto 2234 if (sp < 6) return STBTT__CSERR("rcurveline stack"); 2235 for (; i + 5 < sp; i += 6) 2236 stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); 2237 break; 2238 2239 case 0x18: // rcurveline 2240 if (sp < 8) return STBTT__CSERR("rcurveline stack"); 2241 for (; i + 5 < sp - 2; i += 6) 2242 stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); 2243 if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); 2244 stbtt__csctx_rline_to(c, s[i], s[i+1]); 2245 break; 2246 2247 case 0x19: // rlinecurve 2248 if (sp < 8) return STBTT__CSERR("rlinecurve stack"); 2249 for (; i + 1 < sp - 6; i += 2) 2250 stbtt__csctx_rline_to(c, s[i], s[i+1]); 2251 if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); 2252 stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); 2253 break; 2254 2255 case 0x1A: // vvcurveto 2256 case 0x1B: // hhcurveto 2257 if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); 2258 f = 0.0; 2259 if (sp & 1) { f = s[i]; i++; } 2260 for (; i + 3 < sp; i += 4) { 2261 if (b0 == 0x1B) 2262 stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); 2263 else 2264 stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); 2265 f = 0.0; 2266 } 2267 break; 2268 2269 case 0x0A: // callsubr 2270 if (!has_subrs) { 2271 if (info.fdselect.size) 2272 subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); 2273 has_subrs = 1; 2274 } 2275 // fallthrough 2276 goto case; 2277 case 0x1D: // callgsubr 2278 if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); 2279 v = cast(int) s[--sp]; 2280 if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); 2281 subr_stack[subr_stack_height++] = b; 2282 b = stbtt__get_subr(b0 == 0x0A ? subrs : info.gsubrs, v); 2283 if (b.size == 0) return STBTT__CSERR("subr not found"); 2284 b.cursor = 0; 2285 clear_stack = 0; 2286 break; 2287 2288 case 0x0B: // return 2289 if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); 2290 b = subr_stack[--subr_stack_height]; 2291 clear_stack = 0; 2292 break; 2293 2294 case 0x0E: // endchar 2295 stbtt__csctx_close_shape(c); 2296 return 1; 2297 2298 case 0x0C: { // two-byte escape 2299 float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; 2300 float dx, dy; 2301 int b1 = stbtt__buf_get8(&b); 2302 switch (b1) { 2303 // @TODO These "flex" implementations ignore the flex-depth and resolution, 2304 // and always draw beziers. 2305 case 0x22: // hflex 2306 if (sp < 7) return STBTT__CSERR("hflex stack"); 2307 dx1 = s[0]; 2308 dx2 = s[1]; 2309 dy2 = s[2]; 2310 dx3 = s[3]; 2311 dx4 = s[4]; 2312 dx5 = s[5]; 2313 dx6 = s[6]; 2314 stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); 2315 stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); 2316 break; 2317 2318 case 0x23: // flex 2319 if (sp < 13) return STBTT__CSERR("flex stack"); 2320 dx1 = s[0]; 2321 dy1 = s[1]; 2322 dx2 = s[2]; 2323 dy2 = s[3]; 2324 dx3 = s[4]; 2325 dy3 = s[5]; 2326 dx4 = s[6]; 2327 dy4 = s[7]; 2328 dx5 = s[8]; 2329 dy5 = s[9]; 2330 dx6 = s[10]; 2331 dy6 = s[11]; 2332 //fd is s[12] 2333 stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); 2334 stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); 2335 break; 2336 2337 case 0x24: // hflex1 2338 if (sp < 9) return STBTT__CSERR("hflex1 stack"); 2339 dx1 = s[0]; 2340 dy1 = s[1]; 2341 dx2 = s[2]; 2342 dy2 = s[3]; 2343 dx3 = s[4]; 2344 dx4 = s[5]; 2345 dx5 = s[6]; 2346 dy5 = s[7]; 2347 dx6 = s[8]; 2348 stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); 2349 stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); 2350 break; 2351 2352 case 0x25: // flex1 2353 if (sp < 11) return STBTT__CSERR("flex1 stack"); 2354 dx1 = s[0]; 2355 dy1 = s[1]; 2356 dx2 = s[2]; 2357 dy2 = s[3]; 2358 dx3 = s[4]; 2359 dy3 = s[5]; 2360 dx4 = s[6]; 2361 dy4 = s[7]; 2362 dx5 = s[8]; 2363 dy5 = s[9]; 2364 dx6 = dy6 = s[10]; 2365 dx = dx1+dx2+dx3+dx4+dx5; 2366 dy = dy1+dy2+dy3+dy4+dy5; 2367 if (STBTT_fabs(dx) > STBTT_fabs(dy)) 2368 dy6 = -dy; 2369 else 2370 dx6 = -dx; 2371 stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); 2372 stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); 2373 break; 2374 2375 default: 2376 return STBTT__CSERR("unimplemented"); 2377 } 2378 } break; 2379 2380 default: 2381 if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) 2382 return STBTT__CSERR("reserved operator"); 2383 2384 // push immediate 2385 if (b0 == 255) { 2386 f = cast(float)cast(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; 2387 } else { 2388 stbtt__buf_skip(&b, -1); 2389 f = cast(float)cast(stbtt_int16)stbtt__cff_int(&b); 2390 } 2391 if (sp >= 48) return STBTT__CSERR("push stack overflow"); 2392 s[sp++] = f; 2393 clear_stack = 0; 2394 break; 2395 } 2396 if (clear_stack) sp = 0; 2397 } 2398 return STBTT__CSERR("no endchar"); 2399 } 2400 2401 private int stbtt__GetGlyphShapeT2(stbtt_fontinfo* info, int glyph_index, stbtt_vertex **pvertices) 2402 { 2403 // runs the charstring twice, once to count and once to output (to avoid realloc) 2404 stbtt__csctx count_ctx = stbtt__csctx(1,0, 0,0, 0,0, 0,0,0,0, null, 0); //STBTT__CSCTX_INIT(1); 2405 stbtt__csctx output_ctx = stbtt__csctx(0,0, 0,0, 0,0, 0,0,0,0, null, 0); //STBTT__CSCTX_INIT(0); 2406 if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { 2407 *pvertices = cast(stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*cast(uint)stbtt_vertex.sizeof, info.userdata); 2408 output_ctx.pvertices = *pvertices; 2409 if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { 2410 assert(output_ctx.num_vertices == count_ctx.num_vertices); 2411 return output_ctx.num_vertices; 2412 } 2413 } 2414 *pvertices = null; 2415 return 0; 2416 } 2417 2418 public int stbtt__GetGlyphInfoT2(stbtt_fontinfo* info, int glyph_index, int *x0, int *y0, int *x1, int *y1) 2419 { 2420 stbtt__csctx c = stbtt__csctx(1,0, 0,0, 0,0, 0,0,0,0, null, 0); //STBTT__CSCTX_INIT(1); 2421 int r = stbtt__run_charstring(info, glyph_index, &c); //k8: sorry 2422 if (x0) *x0 = r ? c.min_x : 0; 2423 if (y0) *y0 = r ? c.min_y : 0; 2424 if (x1) *x1 = r ? c.max_x : 0; 2425 if (y1) *y1 = r ? c.max_y : 0; 2426 return r ? c.num_vertices : 0; 2427 } 2428 2429 public int stbtt_GetGlyphShape(stbtt_fontinfo* info, int glyph_index, stbtt_vertex **pvertices) 2430 { 2431 if (!info.cff.size) 2432 return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); 2433 else 2434 return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); 2435 } 2436 2437 public void stbtt_GetGlyphHMetrics(const(stbtt_fontinfo)* info, int glyph_index, int *advanceWidth, int *leftSideBearing) 2438 { 2439 stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info.data+info.hhea + 34); 2440 if (glyph_index < numOfLongHorMetrics) { 2441 if (advanceWidth) *advanceWidth = ttSHORT(info.data + info.hmtx + 4*glyph_index); 2442 if (leftSideBearing) *leftSideBearing = ttSHORT(info.data + info.hmtx + 4*glyph_index + 2); 2443 } else { 2444 if (advanceWidth) *advanceWidth = ttSHORT(info.data + info.hmtx + 4*(numOfLongHorMetrics-1)); 2445 if (leftSideBearing) *leftSideBearing = ttSHORT(info.data + info.hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); 2446 } 2447 } 2448 2449 private int stbtt__GetGlyphKernInfoAdvance(stbtt_fontinfo* info, int glyph1, int glyph2) 2450 { 2451 stbtt_uint8 *data = info.data + info.kern; 2452 stbtt_uint32 needle, straw; 2453 int l, r, m; 2454 2455 // we only look at the first table. it must be 'horizontal' and format 0. 2456 if (!info.kern) 2457 return 0; 2458 if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 2459 return 0; 2460 if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 2461 return 0; 2462 2463 l = 0; 2464 r = ttUSHORT(data+10) - 1; 2465 needle = glyph1 << 16 | glyph2; 2466 while (l <= r) { 2467 m = (l + r) >> 1; 2468 straw = ttULONG(data+18+(m*6)); // note: unaligned read 2469 if (needle < straw) 2470 r = m - 1; 2471 else if (needle > straw) 2472 l = m + 1; 2473 else 2474 return ttSHORT(data+22+(m*6)); 2475 } 2476 return 0; 2477 } 2478 2479 private stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) 2480 { 2481 stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); 2482 switch(coverageFormat) { 2483 case 1: { 2484 stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); 2485 2486 // Binary search. 2487 stbtt_int32 l=0, r=glyphCount-1, m; 2488 int straw, needle=glyph; 2489 while (l <= r) { 2490 stbtt_uint8 *glyphArray = coverageTable + 4; 2491 stbtt_uint16 glyphID; 2492 m = (l + r) >> 1; 2493 glyphID = ttUSHORT(glyphArray + 2 * m); 2494 straw = glyphID; 2495 if (needle < straw) 2496 r = m - 1; 2497 else if (needle > straw) 2498 l = m + 1; 2499 else { 2500 return m; 2501 } 2502 } 2503 } break; 2504 2505 case 2: { 2506 stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); 2507 stbtt_uint8 *rangeArray = coverageTable + 4; 2508 2509 // Binary search. 2510 stbtt_int32 l=0, r=rangeCount-1, m; 2511 int strawStart, strawEnd, needle=glyph; 2512 while (l <= r) { 2513 stbtt_uint8 *rangeRecord; 2514 m = (l + r) >> 1; 2515 rangeRecord = rangeArray + 6 * m; 2516 strawStart = ttUSHORT(rangeRecord); 2517 strawEnd = ttUSHORT(rangeRecord + 2); 2518 if (needle < strawStart) 2519 r = m - 1; 2520 else if (needle > strawEnd) 2521 l = m + 1; 2522 else { 2523 stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); 2524 return startCoverageIndex + glyph - strawStart; 2525 } 2526 } 2527 } break; 2528 2529 default: { 2530 // There are no other cases. 2531 assert(0); 2532 } 2533 } 2534 2535 return -1; 2536 } 2537 2538 private stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) 2539 { 2540 stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); 2541 switch(classDefFormat) 2542 { 2543 case 1: { 2544 stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); 2545 stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); 2546 stbtt_uint8 *classDef1ValueArray = classDefTable + 6; 2547 2548 if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) 2549 return cast(stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); 2550 2551 classDefTable = classDef1ValueArray + 2 * glyphCount; 2552 } break; 2553 2554 case 2: { 2555 stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); 2556 stbtt_uint8 *classRangeRecords = classDefTable + 4; 2557 2558 // Binary search. 2559 stbtt_int32 l=0, r=classRangeCount-1, m; 2560 int strawStart, strawEnd, needle=glyph; 2561 while (l <= r) { 2562 stbtt_uint8 *classRangeRecord; 2563 m = (l + r) >> 1; 2564 classRangeRecord = classRangeRecords + 6 * m; 2565 strawStart = ttUSHORT(classRangeRecord); 2566 strawEnd = ttUSHORT(classRangeRecord + 2); 2567 if (needle < strawStart) 2568 r = m - 1; 2569 else if (needle > strawEnd) 2570 l = m + 1; 2571 else 2572 return cast(stbtt_int32)ttUSHORT(classRangeRecord + 4); 2573 } 2574 2575 classDefTable = classRangeRecords + 6 * classRangeCount; 2576 } break; 2577 2578 default: { 2579 // There are no other cases. 2580 assert(0); 2581 } 2582 } 2583 2584 return -1; 2585 } 2586 2587 // Define to STBTT_assert(x) if you want to break on unimplemented formats. 2588 //#define STBTT_GPOS_TODO_assert(x) 2589 2590 private stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(stbtt_fontinfo* info, int glyph1, int glyph2) 2591 { 2592 stbtt_uint16 lookupListOffset; 2593 stbtt_uint8 *lookupList; 2594 stbtt_uint16 lookupCount; 2595 stbtt_uint8 *data; 2596 stbtt_int32 i; 2597 2598 if (!info.gpos) return 0; 2599 2600 data = info.data + info.gpos; 2601 2602 if (ttUSHORT(data+0) != 1) return 0; // Major version 1 2603 if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 2604 2605 lookupListOffset = ttUSHORT(data+8); 2606 lookupList = data + lookupListOffset; 2607 lookupCount = ttUSHORT(lookupList); 2608 2609 for (i=0; i<lookupCount; ++i) { 2610 stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); 2611 stbtt_uint8 *lookupTable = lookupList + lookupOffset; 2612 2613 stbtt_uint16 lookupType = ttUSHORT(lookupTable); 2614 stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4); 2615 stbtt_uint8 *subTableOffsets = lookupTable + 6; 2616 switch(lookupType) { 2617 case 2: { // Pair Adjustment Positioning Subtable 2618 stbtt_int32 sti; 2619 for (sti=0; sti<subTableCount; sti++) { 2620 stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti); 2621 stbtt_uint8 *table = lookupTable + subtableOffset; 2622 stbtt_uint16 posFormat = ttUSHORT(table); 2623 stbtt_uint16 coverageOffset = ttUSHORT(table + 2); 2624 stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1); 2625 if (coverageIndex == -1) continue; 2626 2627 switch (posFormat) { 2628 case 1: { 2629 stbtt_int32 l, r, m; 2630 int straw, needle; 2631 stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); 2632 stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); 2633 stbtt_int32 valueRecordPairSizeInBytes = 2; 2634 stbtt_uint16 pairSetCount = ttUSHORT(table + 8); 2635 stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); 2636 stbtt_uint8 *pairValueTable = table + pairPosOffset; 2637 stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); 2638 stbtt_uint8 *pairValueArray = pairValueTable + 2; 2639 // TODO: Support more formats. 2640 //!STBTT_GPOS_TODO_assert(valueFormat1 == 4); 2641 if (valueFormat1 != 4) return 0; 2642 //!STBTT_GPOS_TODO_assert(valueFormat2 == 0); 2643 if (valueFormat2 != 0) return 0; 2644 2645 assert(coverageIndex < pairSetCount); 2646 2647 needle=glyph2; 2648 r=pairValueCount-1; 2649 l=0; 2650 2651 // Binary search. 2652 while (l <= r) { 2653 stbtt_uint16 secondGlyph; 2654 stbtt_uint8 *pairValue; 2655 m = (l + r) >> 1; 2656 pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; 2657 secondGlyph = ttUSHORT(pairValue); 2658 straw = secondGlyph; 2659 if (needle < straw) 2660 r = m - 1; 2661 else if (needle > straw) 2662 l = m + 1; 2663 else { 2664 stbtt_int16 xAdvance = ttSHORT(pairValue + 2); 2665 return xAdvance; 2666 } 2667 } 2668 } break; 2669 2670 case 2: { 2671 stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); 2672 stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); 2673 2674 stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); 2675 stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); 2676 int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); 2677 int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); 2678 2679 stbtt_uint16 class1Count = ttUSHORT(table + 12); 2680 stbtt_uint16 class2Count = ttUSHORT(table + 14); 2681 assert(glyph1class < class1Count); 2682 assert(glyph2class < class2Count); 2683 2684 // TODO: Support more formats. 2685 //!STBTT_GPOS_TODO_assert(valueFormat1 == 4); 2686 if (valueFormat1 != 4) return 0; 2687 //!STBTT_GPOS_TODO_assert(valueFormat2 == 0); 2688 if (valueFormat2 != 0) return 0; 2689 2690 if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { 2691 stbtt_uint8 *class1Records = table + 16; 2692 stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); 2693 stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); 2694 return xAdvance; 2695 } 2696 } break; 2697 2698 default: { 2699 // There are no other cases. 2700 assert(0); 2701 } 2702 } 2703 } 2704 break; 2705 } 2706 2707 default: 2708 // TODO: Implement other stuff. 2709 break; 2710 } 2711 } 2712 2713 return 0; 2714 } 2715 2716 public int stbtt_GetGlyphKernAdvance(stbtt_fontinfo* info, int g1, int g2) 2717 { 2718 int xAdvance = 0; 2719 2720 if (info.gpos) 2721 xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); 2722 2723 if (info.kern) 2724 xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); 2725 2726 return xAdvance; 2727 } 2728 2729 public int stbtt_GetCodepointKernAdvance(stbtt_fontinfo* info, int ch1, int ch2) 2730 { 2731 if (!info.kern && !info.gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs 2732 return 0; 2733 return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); 2734 } 2735 2736 public void stbtt_GetCodepointHMetrics(const(stbtt_fontinfo)* info, int codepoint, int *advanceWidth, int *leftSideBearing) 2737 { 2738 stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); 2739 } 2740 2741 public void stbtt_GetFontVMetrics(const(stbtt_fontinfo)* info, int *ascent, int *descent, int *lineGap) 2742 { 2743 if (ascent ) *ascent = ttSHORT(info.data+info.hhea + 4); 2744 if (descent) *descent = ttSHORT(info.data+info.hhea + 6); 2745 if (lineGap) *lineGap = ttSHORT(info.data+info.hhea + 8); 2746 } 2747 2748 public int stbtt_GetFontVMetricsOS2(stbtt_fontinfo* info, int *typoAscent, int *typoDescent, int *typoLineGap) 2749 { 2750 int tab = stbtt__find_table(info.data, info.fontstart, "OS/2"); 2751 if (!tab) 2752 return 0; 2753 if (typoAscent ) *typoAscent = ttSHORT(info.data+tab + 68); 2754 if (typoDescent) *typoDescent = ttSHORT(info.data+tab + 70); 2755 if (typoLineGap) *typoLineGap = ttSHORT(info.data+tab + 72); 2756 return 1; 2757 } 2758 2759 public void stbtt_GetFontBoundingBox(const(stbtt_fontinfo)* info, int *x0, int *y0, int *x1, int *y1) 2760 { 2761 *x0 = ttSHORT(info.data + info.head + 36); 2762 *y0 = ttSHORT(info.data + info.head + 38); 2763 *x1 = ttSHORT(info.data + info.head + 40); 2764 *y1 = ttSHORT(info.data + info.head + 42); 2765 } 2766 2767 public float stbtt_ScaleForPixelHeight(const(stbtt_fontinfo)* info, float height) 2768 { 2769 int fheight = ttSHORT(info.data + info.hhea + 4) - ttSHORT(info.data + info.hhea + 6); 2770 return cast(float) height / fheight; 2771 } 2772 2773 public float stbtt_ScaleForMappingEmToPixels(const(stbtt_fontinfo)* info, float pixels) 2774 { 2775 int unitsPerEm = ttUSHORT(info.data + info.head + 18); 2776 return pixels / unitsPerEm; 2777 } 2778 2779 public void stbtt_FreeShape(const(stbtt_fontinfo)* info, stbtt_vertex *v) 2780 { 2781 STBTT_free(v, info.userdata); 2782 } 2783 2784 ////////////////////////////////////////////////////////////////////////////// 2785 // 2786 // antialiasing software rasterizer 2787 // 2788 2789 public void stbtt_GetGlyphBitmapBoxSubpixel(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) 2790 { 2791 int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning 2792 if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { 2793 // e.g. space character 2794 if (ix0) *ix0 = 0; 2795 if (iy0) *iy0 = 0; 2796 if (ix1) *ix1 = 0; 2797 if (iy1) *iy1 = 0; 2798 } else { 2799 // move to integral bboxes (treating pixels as little squares, what pixels get touched)? 2800 if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); 2801 if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); 2802 if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); 2803 if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); 2804 } 2805 } 2806 2807 public void stbtt_GetGlyphBitmapBox(stbtt_fontinfo* font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) 2808 { 2809 stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); 2810 } 2811 2812 public void stbtt_GetCodepointBitmapBoxSubpixel(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) 2813 { 2814 stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); 2815 } 2816 2817 public void stbtt_GetCodepointBitmapBox(stbtt_fontinfo* font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) 2818 { 2819 stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); 2820 } 2821 2822 ////////////////////////////////////////////////////////////////////////////// 2823 // 2824 // Rasterizer 2825 2826 struct stbtt__hheap_chunk { 2827 stbtt__hheap_chunk *next; 2828 } 2829 2830 struct stbtt__hheap { 2831 stbtt__hheap_chunk *head; 2832 void *first_free; 2833 int num_remaining_in_head_chunk; 2834 } 2835 2836 private void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) 2837 { 2838 if (hh.first_free) { 2839 void *p = hh.first_free; 2840 hh.first_free = * cast(void **) p; 2841 return p; 2842 } else { 2843 if (hh.num_remaining_in_head_chunk == 0) { 2844 int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); 2845 stbtt__hheap_chunk *c = cast(stbtt__hheap_chunk *) STBTT_malloc(cast(uint)(stbtt__hheap_chunk.sizeof + size * count), userdata); 2846 if (c == null) 2847 return null; 2848 c.next = hh.head; 2849 hh.head = c; 2850 hh.num_remaining_in_head_chunk = count; 2851 } 2852 --hh.num_remaining_in_head_chunk; 2853 return cast(char *) (hh.head) + cast(uint)stbtt__hheap_chunk.sizeof + size * hh.num_remaining_in_head_chunk; 2854 } 2855 } 2856 2857 private void stbtt__hheap_free(stbtt__hheap *hh, void *p) 2858 { 2859 *cast(void **) p = hh.first_free; 2860 hh.first_free = p; 2861 } 2862 2863 private void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) 2864 { 2865 stbtt__hheap_chunk *c = hh.head; 2866 while (c) { 2867 stbtt__hheap_chunk *n = c.next; 2868 STBTT_free(c, userdata); 2869 c = n; 2870 } 2871 } 2872 2873 struct stbtt__edge { 2874 float x0,y0, x1,y1; 2875 int invert; 2876 } 2877 2878 2879 struct stbtt__active_edge { 2880 stbtt__active_edge *next; 2881 static if (STBTT_RASTERIZER_VERSION == 1) { 2882 int x,dx; 2883 float ey; 2884 int direction; 2885 } else static if (STBTT_RASTERIZER_VERSION == 2) { 2886 float fx,fdx,fdy; 2887 float direction; 2888 float sy; 2889 float ey; 2890 } else { 2891 static assert(0, "Unrecognized value of STBTT_RASTERIZER_VERSION"); 2892 } 2893 } 2894 2895 static if (STBTT_RASTERIZER_VERSION == 1) { 2896 enum STBTT_FIXSHIFT = 10; 2897 enum STBTT_FIX = (1 << STBTT_FIXSHIFT); 2898 enum STBTT_FIXMASK = (STBTT_FIX-1); 2899 2900 private stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) 2901 { 2902 stbtt__active_edge *z = void; 2903 z = cast(stbtt__active_edge *) stbtt__hheap_alloc(hh, cast(uint)(*z).sizeof, userdata); 2904 float dxdy = (e.x1 - e.x0) / (e.y1 - e.y0); 2905 assert(z != null); 2906 if (!z) return z; 2907 2908 // round dx down to avoid overshooting 2909 if (dxdy < 0) 2910 z.dx = -STBTT_ifloor(STBTT_FIX * -dxdy); 2911 else 2912 z.dx = STBTT_ifloor(STBTT_FIX * dxdy); 2913 2914 z.x = STBTT_ifloor(STBTT_FIX * e.x0 + z.dx * (start_point - e.y0)); // use z->dx so when we offset later it's by the same amount 2915 z.x -= off_x * STBTT_FIX; 2916 2917 z.ey = e.y1; 2918 z.next = 0; 2919 z.direction = e.invert ? 1 : -1; 2920 return z; 2921 } 2922 } else static if (STBTT_RASTERIZER_VERSION == 2) { 2923 private stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) 2924 { 2925 stbtt__active_edge *z = void; 2926 z = cast(stbtt__active_edge *) stbtt__hheap_alloc(hh, cast(uint)(*z).sizeof, userdata); 2927 float dxdy = (e.x1 - e.x0) / (e.y1 - e.y0); 2928 assert(z != null); 2929 //STBTT_assert(e->y0 <= start_point); 2930 if (!z) return z; 2931 z.fdx = dxdy; 2932 z.fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; 2933 z.fx = e.x0 + dxdy * (start_point - e.y0); 2934 z.fx -= off_x; 2935 z.direction = e.invert ? 1.0f : -1.0f; 2936 z.sy = e.y0; 2937 z.ey = e.y1; 2938 z.next = null; 2939 return z; 2940 } 2941 } else { 2942 static assert(0, "Unrecognized value of STBTT_RASTERIZER_VERSION"); 2943 } 2944 2945 static if (STBTT_RASTERIZER_VERSION == 1) { 2946 // note: this routine clips fills that extend off the edges... ideally this 2947 // wouldn't happen, but it could happen if the truetype glyph bounding boxes 2948 // are wrong, or if the user supplies a too-small bitmap 2949 private void stbtt__fill_active_edges(ubyte *scanline, int len, stbtt__active_edge *e, int max_weight) 2950 { 2951 // non-zero winding fill 2952 int x0=0, w=0; 2953 2954 while (e) { 2955 if (w == 0) { 2956 // if we're currently at zero, we need to record the edge start point 2957 x0 = e.x; w += e.direction; 2958 } else { 2959 int x1 = e.x; w += e.direction; 2960 // if we went to zero, we need to draw 2961 if (w == 0) { 2962 int i = x0 >> STBTT_FIXSHIFT; 2963 int j = x1 >> STBTT_FIXSHIFT; 2964 2965 if (i < len && j >= 0) { 2966 if (i == j) { 2967 // x0,x1 are the same pixel, so compute combined coverage 2968 scanline[i] = scanline[i] + cast(stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); 2969 } else { 2970 if (i >= 0) // add antialiasing for x0 2971 scanline[i] = scanline[i] + cast(stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); 2972 else 2973 i = -1; // clip 2974 2975 if (j < len) // add antialiasing for x1 2976 scanline[j] = scanline[j] + cast(stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); 2977 else 2978 j = len; // clip 2979 2980 for (++i; i < j; ++i) // fill pixels between x0 and x1 2981 scanline[i] = scanline[i] + cast(stbtt_uint8) max_weight; 2982 } 2983 } 2984 } 2985 } 2986 2987 e = e.next; 2988 } 2989 } 2990 2991 private void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) 2992 { 2993 stbtt__hheap hh = { 0, 0, 0 }; 2994 stbtt__active_edge *active = null; 2995 int y,j=0; 2996 int max_weight = (255 / vsubsample); // weight per vertical scanline 2997 int s; // vertical subsample index 2998 ubyte[512] scanline_data = void; 2999 ubyte *scanline; 3000 3001 if (result.w > 512) 3002 scanline = cast(ubyte *) STBTT_malloc(result.w, userdata); 3003 else 3004 scanline = scanline_data; 3005 3006 y = off_y * vsubsample; 3007 e[n].y0 = (off_y + result.h) * cast(float) vsubsample + 1; 3008 3009 while (j < result.h) { 3010 STBTT_memset(scanline, 0, result.w); 3011 for (s=0; s < vsubsample; ++s) { 3012 // find center of pixel for this scanline 3013 float scan_y = y + 0.5f; 3014 stbtt__active_edge **step = &active; 3015 3016 // update all active edges; 3017 // remove all active edges that terminate before the center of this scanline 3018 while (*step) { 3019 stbtt__active_edge * z = *step; 3020 if (z.ey <= scan_y) { 3021 *step = z.next; // delete from list 3022 assert(z.direction); 3023 z.direction = 0; 3024 stbtt__hheap_free(&hh, z); 3025 } else { 3026 z.x += z.dx; // advance to position for current scanline 3027 step = &((*step).next); // advance through list 3028 } 3029 } 3030 3031 // resort the list if needed 3032 for(;;) { 3033 int changed=0; 3034 step = &active; 3035 while (*step && (*step).next) { 3036 if ((*step).x > (*step).next.x) { 3037 stbtt__active_edge *t = *step; 3038 stbtt__active_edge *q = t.next; 3039 3040 t.next = q.next; 3041 q.next = t; 3042 *step = q; 3043 changed = 1; 3044 } 3045 step = &(*step).next; 3046 } 3047 if (!changed) break; 3048 } 3049 3050 // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 3051 while (e.y0 <= scan_y) { 3052 if (e.y1 > scan_y) { 3053 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); 3054 if (z != null) { 3055 // find insertion point 3056 if (active == null) 3057 active = z; 3058 else if (z.x < active.x) { 3059 // insert at front 3060 z.next = active; 3061 active = z; 3062 } else { 3063 // find thing to insert AFTER 3064 stbtt__active_edge *p = active; 3065 while (p.next && p.next.x < z.x) 3066 p = p.next; 3067 // at this point, p->next->x is NOT < z->x 3068 z.next = p.next; 3069 p.next = z; 3070 } 3071 } 3072 } 3073 ++e; 3074 } 3075 3076 // now process all active edges in XOR fashion 3077 if (active) 3078 stbtt__fill_active_edges(scanline, result.w, active, max_weight); 3079 3080 ++y; 3081 } 3082 STBTT_memcpy(result.pixels + j * result.stride, scanline, result.w); 3083 ++j; 3084 } 3085 3086 stbtt__hheap_cleanup(&hh, userdata); 3087 3088 if (scanline != scanline_data) 3089 STBTT_free(scanline, userdata); 3090 } 3091 3092 } else static if (STBTT_RASTERIZER_VERSION == 2) { 3093 3094 // the edge passed in here does not cross the vertical line at x or the vertical line at x+1 3095 // (i.e. it has already been clipped to those) 3096 private void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) 3097 { 3098 if (y0 == y1) return; 3099 assert(y0 < y1); 3100 assert(e.sy <= e.ey); 3101 if (y0 > e.ey) return; 3102 if (y1 < e.sy) return; 3103 if (y0 < e.sy) { 3104 x0 += (x1-x0) * (e.sy - y0) / (y1-y0); 3105 y0 = e.sy; 3106 } 3107 if (y1 > e.ey) { 3108 x1 += (x1-x0) * (e.ey - y1) / (y1-y0); 3109 y1 = e.ey; 3110 } 3111 3112 if (x0 == x) 3113 assert(x1 <= x+1); 3114 else if (x0 == x+1) 3115 assert(x1 >= x); 3116 else if (x0 <= x) 3117 assert(x1 <= x); 3118 else if (x0 >= x+1) 3119 assert(x1 >= x+1); 3120 else 3121 assert(x1 >= x && x1 <= x+1); 3122 3123 if (x0 <= x && x1 <= x) 3124 scanline[x] += e.direction * (y1-y0); 3125 else if (x0 >= x+1 && x1 >= x+1) 3126 {} 3127 else { 3128 assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); 3129 scanline[x] += e.direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position 3130 } 3131 } 3132 3133 private void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) 3134 { 3135 float y_bottom = y_top+1; 3136 3137 while (e) { 3138 // brute force every pixel 3139 3140 // compute intersection points with top & bottom 3141 assert(e.ey >= y_top); 3142 3143 if (e.fdx == 0) { 3144 float x0 = e.fx; 3145 if (x0 < len) { 3146 if (x0 >= 0) { 3147 stbtt__handle_clipped_edge(scanline,cast(int) x0,e, x0,y_top, x0,y_bottom); 3148 stbtt__handle_clipped_edge(scanline_fill-1,cast(int) x0+1,e, x0,y_top, x0,y_bottom); 3149 } else { 3150 stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); 3151 } 3152 } 3153 } else { 3154 float x0 = e.fx; 3155 float dx = e.fdx; 3156 float xb = x0 + dx; 3157 float x_top, x_bottom; 3158 float sy0,sy1; 3159 float dy = e.fdy; 3160 assert(e.sy <= y_bottom && e.ey >= y_top); 3161 3162 // compute endpoints of line segment clipped to this scanline (if the 3163 // line segment starts on this scanline. x0 is the intersection of the 3164 // line with y_top, but that may be off the line segment. 3165 if (e.sy > y_top) { 3166 x_top = x0 + dx * (e.sy - y_top); 3167 sy0 = e.sy; 3168 } else { 3169 x_top = x0; 3170 sy0 = y_top; 3171 } 3172 if (e.ey < y_bottom) { 3173 x_bottom = x0 + dx * (e.ey - y_top); 3174 sy1 = e.ey; 3175 } else { 3176 x_bottom = xb; 3177 sy1 = y_bottom; 3178 } 3179 3180 if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { 3181 // from here on, we don't have to range check x values 3182 3183 if (cast(int) x_top == cast(int) x_bottom) { 3184 float height; 3185 // simple case, only spans one pixel 3186 int x = cast(int) x_top; 3187 height = sy1 - sy0; 3188 assert(x >= 0 && x < len); 3189 scanline[x] += e.direction * (1-((x_top - x) + (x_bottom-x))/2) * height; 3190 scanline_fill[x] += e.direction * height; // everything right of this pixel is filled 3191 } else { 3192 int x,x1,x2; 3193 float y_crossing, step, sign, area; 3194 // covers 2+ pixels 3195 if (x_top > x_bottom) { 3196 // flip scanline vertically; signed area is the same 3197 float t; 3198 sy0 = y_bottom - (sy0 - y_top); 3199 sy1 = y_bottom - (sy1 - y_top); 3200 t = sy0, sy0 = sy1, sy1 = t; 3201 t = x_bottom, x_bottom = x_top, x_top = t; 3202 dx = -dx; 3203 dy = -dy; 3204 t = x0, x0 = xb, xb = t; 3205 } 3206 3207 x1 = cast(int) x_top; 3208 x2 = cast(int) x_bottom; 3209 // compute intersection with y axis at x1+1 3210 y_crossing = (x1+1 - x0) * dy + y_top; 3211 3212 sign = e.direction; 3213 // area of the rectangle covered from y0..y_crossing 3214 area = sign * (y_crossing-sy0); 3215 // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) 3216 scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); 3217 3218 step = sign * dy; 3219 for (x = x1+1; x < x2; ++x) { 3220 scanline[x] += area + step/2; 3221 area += step; 3222 } 3223 y_crossing += dy * (x2 - (x1+1)); 3224 3225 assert(STBTT_fabs(area) <= 1.01f); 3226 3227 scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); 3228 3229 scanline_fill[x2] += sign * (sy1-sy0); 3230 } 3231 } else { 3232 // if edge goes outside of box we're drawing, we require 3233 // clipping logic. since this does not match the intended use 3234 // of this library, we use a different, very slow brute 3235 // force implementation 3236 int x; 3237 for (x=0; x < len; ++x) { 3238 // cases: 3239 // 3240 // there can be up to two intersections with the pixel. any intersection 3241 // with left or right edges can be handled by splitting into two (or three) 3242 // regions. intersections with top & bottom do not necessitate case-wise logic. 3243 // 3244 // the old way of doing this found the intersections with the left & right edges, 3245 // then used some simple logic to produce up to three segments in sorted order 3246 // from top-to-bottom. however, this had a problem: if an x edge was epsilon 3247 // across the x border, then the corresponding y position might not be distinct 3248 // from the other y segment, and it might ignored as an empty segment. to avoid 3249 // that, we need to explicitly produce segments based on x positions. 3250 3251 // rename variables to clearly-defined pairs 3252 float y0 = y_top; 3253 float x1 = cast(float) (x); 3254 float x2 = cast(float) (x+1); 3255 float x3 = xb; 3256 float y3 = y_bottom; 3257 3258 // x = e->x + e->dx * (y-y_top) 3259 // (y-y_top) = (x - e->x) / e->dx 3260 // y = (x - e->x) / e->dx + y_top 3261 float y1 = (x - x0) / dx + y_top; 3262 float y2 = (x+1 - x0) / dx + y_top; 3263 3264 if (x0 < x1 && x3 > x2) { // three segments descending down-right 3265 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 3266 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); 3267 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 3268 } else if (x3 < x1 && x0 > x2) { // three segments descending down-left 3269 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 3270 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); 3271 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 3272 } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right 3273 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 3274 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 3275 } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left 3276 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 3277 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 3278 } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right 3279 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 3280 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 3281 } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left 3282 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 3283 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 3284 } else { // one segment 3285 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); 3286 } 3287 } 3288 } 3289 } 3290 e = e.next; 3291 } 3292 } 3293 3294 // directly AA rasterize edges w/o supersampling 3295 private void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) 3296 { 3297 stbtt__hheap hh = stbtt__hheap( null, null, 0 ); 3298 stbtt__active_edge *active = null; 3299 int y,j=0, i; 3300 float[129] scanline_data = void; 3301 float *scanline, scanline2; 3302 3303 //STBTT__NOTUSED(vsubsample); 3304 3305 if (result.w > 64) 3306 scanline = cast(float *) STBTT_malloc((result.w*2+1) * cast(uint)float.sizeof, userdata); 3307 else 3308 scanline = scanline_data.ptr; 3309 3310 scanline2 = scanline + result.w; 3311 3312 y = off_y; 3313 e[n].y0 = cast(float) (off_y + result.h) + 1; 3314 3315 while (j < result.h) { 3316 // find center of pixel for this scanline 3317 float scan_y_top = y + 0.0f; 3318 float scan_y_bottom = y + 1.0f; 3319 stbtt__active_edge **step = &active; 3320 3321 STBTT_memset(scanline , 0, result.w*cast(uint)scanline[0].sizeof); 3322 STBTT_memset(scanline2, 0, (result.w+1)*cast(uint)scanline[0].sizeof); 3323 3324 // update all active edges; 3325 // remove all active edges that terminate before the top of this scanline 3326 while (*step) { 3327 stbtt__active_edge * z = *step; 3328 if (z.ey <= scan_y_top) { 3329 *step = z.next; // delete from list 3330 assert(z.direction); 3331 z.direction = 0; 3332 stbtt__hheap_free(&hh, z); 3333 } else { 3334 step = &((*step).next); // advance through list 3335 } 3336 } 3337 3338 // insert all edges that start before the bottom of this scanline 3339 while (e.y0 <= scan_y_bottom) { 3340 if (e.y0 != e.y1) { 3341 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); 3342 if (z != null) { 3343 assert(z.ey >= scan_y_top); 3344 // insert at front 3345 z.next = active; 3346 active = z; 3347 } 3348 } 3349 ++e; 3350 } 3351 3352 // now process all active edges 3353 if (active) 3354 stbtt__fill_active_edges_new(scanline, scanline2+1, result.w, active, scan_y_top); 3355 3356 { 3357 float sum = 0; 3358 for (i=0; i < result.w; ++i) { 3359 float k; 3360 int m; 3361 sum += scanline2[i]; 3362 k = scanline[i] + sum; 3363 k = cast(float) STBTT_fabs(k)*255 + 0.5f; 3364 m = cast(int) k; 3365 if (m > 255) m = 255; 3366 result.pixels[j*result.stride + i] = cast(ubyte) m; 3367 } 3368 } 3369 // advance all the edges 3370 step = &active; 3371 while (*step) { 3372 stbtt__active_edge *z = *step; 3373 z.fx += z.fdx; // advance to position for current scanline 3374 step = &((*step).next); // advance through list 3375 } 3376 3377 ++y; 3378 ++j; 3379 } 3380 3381 stbtt__hheap_cleanup(&hh, userdata); 3382 3383 if (scanline !is scanline_data.ptr) 3384 STBTT_free(scanline, userdata); 3385 } 3386 } else { 3387 static assert(0, "Unrecognized value of STBTT_RASTERIZER_VERSION"); 3388 } 3389 3390 //#define STBTT__COMPARE(a,b) ((a).y0 < (b).y0) 3391 bool STBTT__COMPARE (stbtt__edge *a, stbtt__edge *b) pure { pragma(inline, true); return (a.y0 < b.y0); } 3392 3393 private void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) 3394 { 3395 int i,j; 3396 for (i=1; i < n; ++i) { 3397 stbtt__edge t = p[i]; 3398 stbtt__edge *a = &t; 3399 j = i; 3400 while (j > 0) { 3401 stbtt__edge *b = &p[j-1]; 3402 int c = STBTT__COMPARE(a,b); 3403 if (!c) break; 3404 p[j] = p[j-1]; 3405 --j; 3406 } 3407 if (i != j) 3408 p[j] = t; 3409 } 3410 } 3411 3412 private void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) 3413 { 3414 /* threshhold for transitioning to insertion sort */ 3415 while (n > 12) { 3416 stbtt__edge t; 3417 int c01,c12,c,m,i,j; 3418 3419 /* compute median of three */ 3420 m = n >> 1; 3421 c01 = STBTT__COMPARE(&p[0],&p[m]); 3422 c12 = STBTT__COMPARE(&p[m],&p[n-1]); 3423 /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ 3424 if (c01 != c12) { 3425 /* otherwise, we'll need to swap something else to middle */ 3426 int z; 3427 c = STBTT__COMPARE(&p[0],&p[n-1]); 3428 /* 0>mid && mid<n: 0>n => n; 0<n => 0 */ 3429 /* 0<mid && mid>n: 0>n => 0; 0<n => n */ 3430 z = (c == c12) ? 0 : n-1; 3431 t = p[z]; 3432 p[z] = p[m]; 3433 p[m] = t; 3434 } 3435 /* now p[m] is the median-of-three */ 3436 /* swap it to the beginning so it won't move around */ 3437 t = p[0]; 3438 p[0] = p[m]; 3439 p[m] = t; 3440 3441 /* partition loop */ 3442 i=1; 3443 j=n-1; 3444 for(;;) { 3445 /* handling of equality is crucial here */ 3446 /* for sentinels & efficiency with duplicates */ 3447 for (;;++i) { 3448 if (!STBTT__COMPARE(&p[i], &p[0])) break; 3449 } 3450 for (;;--j) { 3451 if (!STBTT__COMPARE(&p[0], &p[j])) break; 3452 } 3453 /* make sure we haven't crossed */ 3454 if (i >= j) break; 3455 t = p[i]; 3456 p[i] = p[j]; 3457 p[j] = t; 3458 3459 ++i; 3460 --j; 3461 } 3462 /* recurse on smaller side, iterate on larger */ 3463 if (j < (n-i)) { 3464 stbtt__sort_edges_quicksort(p,j); 3465 p = p+i; 3466 n = n-i; 3467 } else { 3468 stbtt__sort_edges_quicksort(p+i, n-i); 3469 n = j; 3470 } 3471 } 3472 } 3473 3474 private void stbtt__sort_edges(stbtt__edge *p, int n) 3475 { 3476 stbtt__sort_edges_quicksort(p, n); 3477 stbtt__sort_edges_ins_sort(p, n); 3478 } 3479 3480 struct stbtt__point { 3481 float x,y; 3482 } 3483 3484 private 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, void *userdata) 3485 { 3486 float y_scale_inv = invert ? -scale_y : scale_y; 3487 stbtt__edge *e; 3488 int n,i,j,k,m; 3489 static if (STBTT_RASTERIZER_VERSION == 1) { 3490 int vsubsample = result.h < 8 ? 15 : 5; 3491 } else static if (STBTT_RASTERIZER_VERSION == 2) { 3492 int vsubsample = 1; 3493 } else { 3494 static assert(0, "Unrecognized value of STBTT_RASTERIZER_VERSION"); 3495 } 3496 // vsubsample should divide 255 evenly; otherwise we won't reach full opacity 3497 3498 // now we have to blow out the windings into explicit edge lists 3499 n = 0; 3500 for (i=0; i < windings; ++i) 3501 n += wcount[i]; 3502 3503 e = cast(stbtt__edge *) STBTT_malloc(cast(uint)(*e).sizeof * (n+1), userdata); // add an extra one as a sentinel 3504 if (e is null) return; 3505 n = 0; 3506 3507 m=0; 3508 for (i=0; i < windings; ++i) { 3509 stbtt__point *p = pts + m; 3510 m += wcount[i]; 3511 j = wcount[i]-1; 3512 for (k=0; k < wcount[i]; j=k++) { 3513 int a=k,b=j; 3514 // skip the edge if horizontal 3515 if (p[j].y == p[k].y) 3516 continue; 3517 // add edge from j to k to the list 3518 e[n].invert = 0; 3519 if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { 3520 e[n].invert = 1; 3521 a=j,b=k; 3522 } 3523 e[n].x0 = p[a].x * scale_x + shift_x; 3524 e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; 3525 e[n].x1 = p[b].x * scale_x + shift_x; 3526 e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; 3527 ++n; 3528 } 3529 } 3530 3531 // now sort the edges by their highest point (should snap to integer, and then by x) 3532 //STBTT_sort(e, n, e[0].sizeof, stbtt__edge_compare); 3533 stbtt__sort_edges(e, n); 3534 3535 // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule 3536 stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); 3537 3538 STBTT_free(e, userdata); 3539 } 3540 3541 private void stbtt__add_point(stbtt__point *points, int n, float x, float y) 3542 { 3543 if (!points) return; // during first pass, it's unallocated 3544 points[n].x = x; 3545 points[n].y = y; 3546 } 3547 3548 // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 3549 private 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) 3550 { 3551 // midpoint 3552 float mx = (x0 + 2*x1 + x2)/4; 3553 float my = (y0 + 2*y1 + y2)/4; 3554 // versus directly drawn line 3555 float dx = (x0+x2)/2 - mx; 3556 float dy = (y0+y2)/2 - my; 3557 if (n > 16) // 65536 segments on one curve better be enough! 3558 return 1; 3559 if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA 3560 stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); 3561 stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); 3562 } else { 3563 stbtt__add_point(points, *num_points,x2,y2); 3564 *num_points = *num_points+1; 3565 } 3566 return 1; 3567 } 3568 3569 private void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) 3570 { 3571 // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough 3572 float dx0 = x1-x0; 3573 float dy0 = y1-y0; 3574 float dx1 = x2-x1; 3575 float dy1 = y2-y1; 3576 float dx2 = x3-x2; 3577 float dy2 = y3-y2; 3578 float dx = x3-x0; 3579 float dy = y3-y0; 3580 float longlen = cast(float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); 3581 float shortlen = cast(float) STBTT_sqrt(dx*dx+dy*dy); 3582 float flatness_squared = longlen*longlen-shortlen*shortlen; 3583 3584 if (n > 16) // 65536 segments on one curve better be enough! 3585 return; 3586 3587 if (flatness_squared > objspace_flatness_squared) { 3588 float x01 = (x0+x1)/2; 3589 float y01 = (y0+y1)/2; 3590 float x12 = (x1+x2)/2; 3591 float y12 = (y1+y2)/2; 3592 float x23 = (x2+x3)/2; 3593 float y23 = (y2+y3)/2; 3594 3595 float xa = (x01+x12)/2; 3596 float ya = (y01+y12)/2; 3597 float xb = (x12+x23)/2; 3598 float yb = (y12+y23)/2; 3599 3600 float mx = (xa+xb)/2; 3601 float my = (ya+yb)/2; 3602 3603 stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); 3604 stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); 3605 } else { 3606 stbtt__add_point(points, *num_points,x3,y3); 3607 *num_points = *num_points+1; 3608 } 3609 } 3610 3611 // returns number of contours 3612 private stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) 3613 { 3614 stbtt__point *points = null; 3615 int num_points=0; 3616 3617 float objspace_flatness_squared = objspace_flatness * objspace_flatness; 3618 int i,n=0,start=0, pass; 3619 3620 // count how many "moves" there are to get the contour count 3621 for (i=0; i < num_verts; ++i) 3622 if (vertices[i].type == STBTT_vmove) 3623 ++n; 3624 3625 *num_contours = n; 3626 if (n == 0) return null; 3627 3628 *contour_lengths = cast(int *) STBTT_malloc(cast(uint)(**contour_lengths).sizeof * n, userdata); 3629 3630 if (*contour_lengths is null) { 3631 *num_contours = 0; 3632 return null; 3633 } 3634 3635 // make two passes through the points so we don't need to realloc 3636 for (pass=0; pass < 2; ++pass) { 3637 float x=0,y=0; 3638 if (pass == 1) { 3639 points = cast(stbtt__point *) STBTT_malloc(num_points * cast(uint)points[0].sizeof, userdata); 3640 if (points == null) goto error; 3641 } 3642 num_points = 0; 3643 n= -1; 3644 for (i=0; i < num_verts; ++i) { 3645 switch (vertices[i].type) { 3646 case STBTT_vmove: 3647 // start the next contour 3648 if (n >= 0) 3649 (*contour_lengths)[n] = num_points - start; 3650 ++n; 3651 start = num_points; 3652 3653 x = vertices[i].x, y = vertices[i].y; 3654 stbtt__add_point(points, num_points++, x,y); 3655 break; 3656 case STBTT_vline: 3657 x = vertices[i].x, y = vertices[i].y; 3658 stbtt__add_point(points, num_points++, x, y); 3659 break; 3660 case STBTT_vcurve: 3661 stbtt__tesselate_curve(points, &num_points, x,y, 3662 vertices[i].cx, vertices[i].cy, 3663 vertices[i].x, vertices[i].y, 3664 objspace_flatness_squared, 0); 3665 x = vertices[i].x, y = vertices[i].y; 3666 break; 3667 case STBTT_vcubic: 3668 stbtt__tesselate_cubic(points, &num_points, x,y, 3669 vertices[i].cx, vertices[i].cy, 3670 vertices[i].cx1, vertices[i].cy1, 3671 vertices[i].x, vertices[i].y, 3672 objspace_flatness_squared, 0); 3673 x = vertices[i].x, y = vertices[i].y; 3674 break; 3675 default: 3676 } 3677 } 3678 (*contour_lengths)[n] = num_points - start; 3679 } 3680 3681 return points; 3682 error: 3683 STBTT_free(points, userdata); 3684 STBTT_free(*contour_lengths, userdata); 3685 *contour_lengths = null; 3686 *num_contours = 0; 3687 return null; 3688 } 3689 3690 public 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, void *userdata) 3691 { 3692 float scale = scale_x > scale_y ? scale_y : scale_x; 3693 int winding_count = 0; 3694 int *winding_lengths = null; 3695 stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); 3696 if (windings) { 3697 stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); 3698 STBTT_free(winding_lengths, userdata); 3699 STBTT_free(windings, userdata); 3700 } 3701 } 3702 3703 public void stbtt_FreeBitmap(ubyte *bitmap, void *userdata) 3704 { 3705 STBTT_free(bitmap, userdata); 3706 } 3707 3708 public ubyte *stbtt_GetGlyphBitmapSubpixel(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) 3709 { 3710 int ix0,iy0,ix1,iy1; 3711 stbtt__bitmap gbm; 3712 stbtt_vertex *vertices; 3713 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3714 3715 if (scale_x == 0) scale_x = scale_y; 3716 if (scale_y == 0) { 3717 if (scale_x == 0) { 3718 STBTT_free(vertices, info.userdata); 3719 return null; 3720 } 3721 scale_y = scale_x; 3722 } 3723 3724 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); 3725 3726 // now we get the size 3727 gbm.w = (ix1 - ix0); 3728 gbm.h = (iy1 - iy0); 3729 gbm.pixels = null; // in case we error 3730 3731 if (width ) *width = gbm.w; 3732 if (height) *height = gbm.h; 3733 if (xoff ) *xoff = ix0; 3734 if (yoff ) *yoff = iy0; 3735 3736 if (gbm.w && gbm.h) { 3737 gbm.pixels = cast(ubyte *) STBTT_malloc(gbm.w * gbm.h, info.userdata); 3738 if (gbm.pixels) { 3739 gbm.stride = gbm.w; 3740 3741 stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info.userdata); 3742 } 3743 } 3744 STBTT_free(vertices, info.userdata); 3745 return gbm.pixels; 3746 } 3747 3748 public ubyte *stbtt_GetGlyphBitmap(stbtt_fontinfo* info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) 3749 { 3750 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); 3751 } 3752 3753 public 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) 3754 { 3755 int ix0,iy0; 3756 stbtt_vertex *vertices; 3757 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3758 stbtt__bitmap gbm; 3759 3760 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0, null, null); 3761 gbm.pixels = output; 3762 gbm.w = out_w; 3763 gbm.h = out_h; 3764 gbm.stride = out_stride; 3765 3766 if (gbm.w && gbm.h) 3767 stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info.userdata); 3768 3769 STBTT_free(vertices, info.userdata); 3770 } 3771 3772 public 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) 3773 { 3774 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); 3775 } 3776 3777 public ubyte *stbtt_GetCodepointBitmapSubpixel(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) 3778 { 3779 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 3780 } 3781 3782 public void stbtt_MakeCodepointBitmapSubpixelPrefilter(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 oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) 3783 { 3784 stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); 3785 } 3786 3787 public 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) 3788 { 3789 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); 3790 } 3791 3792 public ubyte *stbtt_GetCodepointBitmap(stbtt_fontinfo* info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3793 { 3794 return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 3795 } 3796 3797 public void stbtt_MakeCodepointBitmap(stbtt_fontinfo* info, ubyte *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) 3798 { 3799 stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); 3800 } 3801 3802 ////////////////////////////////////////////////////////////////////////////// 3803 // 3804 // bitmap baking 3805 // 3806 // This is SUPER-CRAPPY packing to keep source code small 3807 3808 private int stbtt_BakeFontBitmap_internal(ubyte *data, int offset, // font location (use offset=0 for plain .ttf) 3809 float pixel_height, // height of font in pixels 3810 ubyte *pixels, int pw, int ph, // bitmap to be filled in 3811 int first_char, int num_chars, // characters to bake 3812 stbtt_bakedchar *chardata, 3813 int* ascent, int* descent, int* line_gap 3814 ) 3815 { 3816 float scale; 3817 int x,y,bottom_y, i; 3818 stbtt_fontinfo f; 3819 f.userdata = null; 3820 if (!stbtt_InitFont(&f, data, offset)) 3821 return -1; 3822 STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 3823 x=y=1; 3824 bottom_y = 1; 3825 3826 scale = stbtt_ScaleForPixelHeight(&f, pixel_height); 3827 3828 stbtt_GetFontVMetrics(&f, ascent, descent, line_gap); 3829 3830 if(ascent) *ascent = cast(int) (*ascent * scale); 3831 if(descent) *descent = cast(int) (*descent * scale); 3832 if(line_gap) *line_gap = cast(int) (*line_gap * scale); 3833 3834 for (i=0; i < num_chars; ++i) { 3835 int advance, lsb, x0,y0,x1,y1,gw,gh; 3836 int g = stbtt_FindGlyphIndex(&f, first_char + i); 3837 stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); 3838 stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); 3839 gw = x1-x0; 3840 gh = y1-y0; 3841 if (x + gw + 1 >= pw) 3842 y = bottom_y, x = 1; // advance to next row 3843 if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row 3844 return -i; 3845 assert(x+gw < pw); 3846 assert(y+gh < ph); 3847 stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); 3848 chardata[i].x0 = cast(stbtt_int16) x; 3849 chardata[i].y0 = cast(stbtt_int16) y; 3850 chardata[i].x1 = cast(stbtt_int16) (x + gw); 3851 chardata[i].y1 = cast(stbtt_int16) (y + gh); 3852 chardata[i].xadvance = scale * advance; 3853 chardata[i].xoff = cast(float) x0; 3854 chardata[i].yoff = cast(float) y0; 3855 x = x + gw + 1; 3856 if (y+gh+1 > bottom_y) 3857 bottom_y = y+gh+1; 3858 } 3859 return bottom_y; 3860 } 3861 3862 public void stbtt_GetBakedQuad(const(stbtt_bakedchar)* chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) 3863 { 3864 float d3d_bias = opengl_fillrule ? 0 : -0.5f; 3865 float ipw = 1.0f / pw, iph = 1.0f / ph; 3866 const(stbtt_bakedchar)* b = chardata + char_index; 3867 int round_x = STBTT_ifloor((*xpos + b.xoff) + 0.5f); 3868 int round_y = STBTT_ifloor((*ypos + b.yoff) + 0.5f); 3869 3870 q.x0 = round_x + d3d_bias; 3871 q.y0 = round_y + d3d_bias; 3872 q.x1 = round_x + b.x1 - b.x0 + d3d_bias; 3873 q.y1 = round_y + b.y1 - b.y0 + d3d_bias; 3874 3875 q.s0 = b.x0 * ipw; 3876 q.t0 = b.y0 * iph; 3877 q.s1 = b.x1 * ipw; 3878 q.t1 = b.y1 * iph; 3879 3880 *xpos += b.xadvance; 3881 } 3882 3883 ////////////////////////////////////////////////////////////////////////////// 3884 // 3885 // rectangle packing replacement routines if you don't have stb_rect_pack.h 3886 // 3887 3888 version(STB_RECT_PACK_VERSION) { 3889 3890 alias stbrp_coord = int; 3891 3892 // ////////////////////////////////////////////////////////////////////////////////// 3893 // // 3894 // // 3895 // COMPILER WARNING ?!?!? // 3896 // // 3897 // // 3898 // if you get a compile warning due to these symbols being defined more than // 3899 // once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // 3900 // // 3901 // ////////////////////////////////////////////////////////////////////////////////// 3902 3903 struct stbrp_context { 3904 int width,height; 3905 int x,y,bottom_y; 3906 } 3907 3908 struct stbrp_node { 3909 ubyte x; 3910 } 3911 3912 struct stbrp_rect { 3913 stbrp_coord x,y; 3914 int id,w,h,was_packed; 3915 } 3916 3917 private void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) 3918 { 3919 con.width = pw; 3920 con.height = ph; 3921 con.x = 0; 3922 con.y = 0; 3923 con.bottom_y = 0; 3924 //STBTT__NOTUSED(nodes); 3925 //STBTT__NOTUSED(num_nodes); 3926 } 3927 3928 private void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) 3929 { 3930 int i; 3931 for (i=0; i < num_rects; ++i) { 3932 if (con.x + rects[i].w > con.width) { 3933 con.x = 0; 3934 con.y = con.bottom_y; 3935 } 3936 if (con.y + rects[i].h > con.height) 3937 break; 3938 rects[i].x = con.x; 3939 rects[i].y = con.y; 3940 rects[i].was_packed = 1; 3941 con.x += rects[i].w; 3942 if (con.y + rects[i].h > con.bottom_y) 3943 con.bottom_y = con.y + rects[i].h; 3944 } 3945 for ( ; i < num_rects; ++i) 3946 rects[i].was_packed = 0; 3947 } 3948 } 3949 3950 3951 // //////////////////////////////////////////////////////////////////////////// 3952 // 3953 // bitmap baking 3954 // 3955 // This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If 3956 // stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. 3957 3958 public int stbtt_PackBegin(stbtt_pack_context *spc, ubyte *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) 3959 { 3960 stbrp_context *context = void; 3961 context = cast(stbrp_context *) STBTT_malloc(cast(uint)(*context).sizeof ,alloc_context); 3962 int num_nodes = pw - padding; 3963 stbrp_node *nodes = void; 3964 nodes = cast(stbrp_node *) STBTT_malloc(cast(uint)(*nodes ).sizeof * num_nodes,alloc_context); 3965 3966 if (context == null || nodes == null) { 3967 if (context != null) STBTT_free(context, alloc_context); 3968 if (nodes != null) STBTT_free(nodes , alloc_context); 3969 return 0; 3970 } 3971 3972 spc.user_allocator_context = alloc_context; 3973 spc.width = pw; 3974 spc.height = ph; 3975 spc.pixels = pixels; 3976 spc.pack_info = context; 3977 spc.nodes = nodes; 3978 spc.padding = padding; 3979 spc.stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 3980 spc.h_oversample = 1; 3981 spc.v_oversample = 1; 3982 3983 stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 3984 3985 if (pixels) 3986 STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 3987 3988 return 1; 3989 } 3990 3991 public void stbtt_PackEnd (stbtt_pack_context *spc) 3992 { 3993 STBTT_free(spc.nodes , spc.user_allocator_context); 3994 STBTT_free(spc.pack_info, spc.user_allocator_context); 3995 } 3996 3997 public void stbtt_PackSetOversampling(stbtt_pack_context *spc, uint h_oversample, uint v_oversample) 3998 { 3999 assert(h_oversample <= STBTT_MAX_OVERSAMPLE); 4000 assert(v_oversample <= STBTT_MAX_OVERSAMPLE); 4001 if (h_oversample <= STBTT_MAX_OVERSAMPLE) 4002 spc.h_oversample = h_oversample; 4003 if (v_oversample <= STBTT_MAX_OVERSAMPLE) 4004 spc.v_oversample = v_oversample; 4005 } 4006 4007 enum STBTT__OVER_MASK = (STBTT_MAX_OVERSAMPLE-1); 4008 4009 private void stbtt__h_prefilter(ubyte *pixels, int w, int h, int stride_in_bytes, uint kernel_width) 4010 { 4011 ubyte[STBTT_MAX_OVERSAMPLE] buffer = void; 4012 int safe_w = w - kernel_width; 4013 int j; 4014 STBTT_memset(buffer.ptr, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze 4015 for (j=0; j < h; ++j) { 4016 int i; 4017 uint total; 4018 STBTT_memset(buffer.ptr, 0, kernel_width); 4019 4020 total = 0; 4021 4022 // make kernel_width a constant in common cases so compiler can optimize out the divide 4023 switch (kernel_width) { 4024 case 2: 4025 for (i=0; i <= safe_w; ++i) { 4026 total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 4027 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 4028 pixels[i] = cast(ubyte) (total / 2); 4029 } 4030 break; 4031 case 3: 4032 for (i=0; i <= safe_w; ++i) { 4033 total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 4034 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 4035 pixels[i] = cast(ubyte) (total / 3); 4036 } 4037 break; 4038 case 4: 4039 for (i=0; i <= safe_w; ++i) { 4040 total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 4041 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 4042 pixels[i] = cast(ubyte) (total / 4); 4043 } 4044 break; 4045 case 5: 4046 for (i=0; i <= safe_w; ++i) { 4047 total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 4048 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 4049 pixels[i] = cast(ubyte) (total / 5); 4050 } 4051 break; 4052 default: 4053 for (i=0; i <= safe_w; ++i) { 4054 total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 4055 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 4056 pixels[i] = cast(ubyte) (total / kernel_width); 4057 } 4058 break; 4059 } 4060 4061 for (; i < w; ++i) { 4062 assert(pixels[i] == 0); 4063 total -= buffer[i & STBTT__OVER_MASK]; 4064 pixels[i] = cast(ubyte) (total / kernel_width); 4065 } 4066 4067 pixels += stride_in_bytes; 4068 } 4069 } 4070 4071 private void stbtt__v_prefilter(ubyte *pixels, int w, int h, int stride_in_bytes, uint kernel_width) 4072 { 4073 ubyte[STBTT_MAX_OVERSAMPLE] buffer = void; 4074 int safe_h = h - kernel_width; 4075 int j; 4076 STBTT_memset(buffer.ptr, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze 4077 for (j=0; j < w; ++j) { 4078 int i; 4079 uint total; 4080 STBTT_memset(buffer.ptr, 0, kernel_width); 4081 4082 total = 0; 4083 4084 // make kernel_width a constant in common cases so compiler can optimize out the divide 4085 switch (kernel_width) { 4086 case 2: 4087 for (i=0; i <= safe_h; ++i) { 4088 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 4089 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 4090 pixels[i*stride_in_bytes] = cast(ubyte) (total / 2); 4091 } 4092 break; 4093 case 3: 4094 for (i=0; i <= safe_h; ++i) { 4095 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 4096 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 4097 pixels[i*stride_in_bytes] = cast(ubyte) (total / 3); 4098 } 4099 break; 4100 case 4: 4101 for (i=0; i <= safe_h; ++i) { 4102 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 4103 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 4104 pixels[i*stride_in_bytes] = cast(ubyte) (total / 4); 4105 } 4106 break; 4107 case 5: 4108 for (i=0; i <= safe_h; ++i) { 4109 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 4110 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 4111 pixels[i*stride_in_bytes] = cast(ubyte) (total / 5); 4112 } 4113 break; 4114 default: 4115 for (i=0; i <= safe_h; ++i) { 4116 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 4117 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 4118 pixels[i*stride_in_bytes] = cast(ubyte) (total / kernel_width); 4119 } 4120 break; 4121 } 4122 4123 for (; i < h; ++i) { 4124 assert(pixels[i*stride_in_bytes] == 0); 4125 total -= buffer[i & STBTT__OVER_MASK]; 4126 pixels[i*stride_in_bytes] = cast(ubyte) (total / kernel_width); 4127 } 4128 4129 pixels += 1; 4130 } 4131 } 4132 4133 private float stbtt__oversample_shift(int oversample) 4134 { 4135 if (!oversample) 4136 return 0.0f; 4137 4138 // The prefilter is a box filter of width "oversample", 4139 // which shifts phase by (oversample - 1)/2 pixels in 4140 // oversampled space. We want to shift in the opposite 4141 // direction to counter this. 4142 return cast(float)-(oversample - 1) / (2.0f * cast(float)oversample); 4143 } 4144 4145 // rects array must be big enough to accommodate all characters in the given ranges 4146 public int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo* info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 4147 { 4148 int i,j,k; 4149 4150 k=0; 4151 for (i=0; i < num_ranges; ++i) { 4152 float fh = ranges[i].font_size; 4153 float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); 4154 ranges[i].h_oversample = cast(ubyte) spc.h_oversample; 4155 ranges[i].v_oversample = cast(ubyte) spc.v_oversample; 4156 for (j=0; j < ranges[i].num_chars; ++j) { 4157 int x0,y0,x1,y1; 4158 int codepoint = ranges[i].array_of_unicode_codepoints == null ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 4159 int glyph = stbtt_FindGlyphIndex(info, codepoint); 4160 stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, 4161 scale * spc.h_oversample, 4162 scale * spc.v_oversample, 4163 0,0, 4164 &x0,&y0,&x1,&y1); 4165 rects[k].w = cast(stbrp_coord) (x1-x0 + spc.padding + spc.h_oversample-1); 4166 rects[k].h = cast(stbrp_coord) (y1-y0 + spc.padding + spc.v_oversample-1); 4167 ++k; 4168 } 4169 } 4170 4171 return k; 4172 } 4173 4174 public void stbtt_MakeGlyphBitmapSubpixelPrefilter(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 prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) 4175 { 4176 stbtt_MakeGlyphBitmapSubpixel(info, 4177 output, 4178 out_w - (prefilter_x - 1), 4179 out_h - (prefilter_y - 1), 4180 out_stride, 4181 scale_x, 4182 scale_y, 4183 shift_x, 4184 shift_y, 4185 glyph); 4186 4187 if (prefilter_x > 1) 4188 stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); 4189 4190 if (prefilter_y > 1) 4191 stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); 4192 4193 *sub_x = stbtt__oversample_shift(prefilter_x); 4194 *sub_y = stbtt__oversample_shift(prefilter_y); 4195 } 4196 4197 // rects array must be big enough to accommodate all characters in the given ranges 4198 public int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo* info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 4199 { 4200 int i,j,k, return_value = 1; 4201 4202 // save current values 4203 int old_h_over = spc.h_oversample; 4204 int old_v_over = spc.v_oversample; 4205 4206 k = 0; 4207 for (i=0; i < num_ranges; ++i) { 4208 float fh = ranges[i].font_size; 4209 float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); 4210 float recip_h,recip_v,sub_x,sub_y; 4211 spc.h_oversample = ranges[i].h_oversample; 4212 spc.v_oversample = ranges[i].v_oversample; 4213 recip_h = 1.0f / spc.h_oversample; 4214 recip_v = 1.0f / spc.v_oversample; 4215 sub_x = stbtt__oversample_shift(spc.h_oversample); 4216 sub_y = stbtt__oversample_shift(spc.v_oversample); 4217 for (j=0; j < ranges[i].num_chars; ++j) { 4218 stbrp_rect *r = &rects[k]; 4219 if (r.was_packed) { 4220 stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; 4221 int advance, lsb, x0,y0,x1,y1; 4222 int codepoint = ranges[i].array_of_unicode_codepoints == null ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 4223 int glyph = stbtt_FindGlyphIndex(info, codepoint); 4224 stbrp_coord pad = cast(stbrp_coord) spc.padding; 4225 4226 // pad on left and top 4227 r.x += pad; 4228 r.y += pad; 4229 r.w -= pad; 4230 r.h -= pad; 4231 stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); 4232 stbtt_GetGlyphBitmapBox(info, glyph, 4233 scale * spc.h_oversample, 4234 scale * spc.v_oversample, 4235 &x0,&y0,&x1,&y1); 4236 stbtt_MakeGlyphBitmapSubpixel(info, 4237 spc.pixels + r.x + r.y*spc.stride_in_bytes, 4238 r.w - spc.h_oversample+1, 4239 r.h - spc.v_oversample+1, 4240 spc.stride_in_bytes, 4241 scale * spc.h_oversample, 4242 scale * spc.v_oversample, 4243 0,0, 4244 glyph); 4245 4246 if (spc.h_oversample > 1) 4247 stbtt__h_prefilter(spc.pixels + r.x + r.y*spc.stride_in_bytes, 4248 r.w, r.h, spc.stride_in_bytes, 4249 spc.h_oversample); 4250 4251 if (spc.v_oversample > 1) 4252 stbtt__v_prefilter(spc.pixels + r.x + r.y*spc.stride_in_bytes, 4253 r.w, r.h, spc.stride_in_bytes, 4254 spc.v_oversample); 4255 4256 bc.x0 = cast(stbtt_int16) r.x; 4257 bc.y0 = cast(stbtt_int16) r.y; 4258 bc.x1 = cast(stbtt_int16) (r.x + r.w); 4259 bc.y1 = cast(stbtt_int16) (r.y + r.h); 4260 bc.xadvance = scale * advance; 4261 bc.xoff = cast(float) x0 * recip_h + sub_x; 4262 bc.yoff = cast(float) y0 * recip_v + sub_y; 4263 bc.xoff2 = (x0 + r.w) * recip_h + sub_x; 4264 bc.yoff2 = (y0 + r.h) * recip_v + sub_y; 4265 } else { 4266 return_value = 0; // if any fail, report failure 4267 } 4268 4269 ++k; 4270 } 4271 } 4272 4273 // restore original values 4274 spc.h_oversample = old_h_over; 4275 spc.v_oversample = old_v_over; 4276 4277 return return_value; 4278 } 4279 4280 public void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) 4281 { 4282 stbrp_pack_rects(cast(stbrp_context *) spc.pack_info, rects, num_rects); 4283 } 4284 4285 public int stbtt_PackFontRanges(stbtt_pack_context *spc, const(ubyte)* fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) 4286 { 4287 stbtt_fontinfo info; 4288 int i,j,n, return_value = 1; 4289 //stbrp_context *context = (stbrp_context *) spc->pack_info; 4290 stbrp_rect *rects; 4291 4292 // flag all characters as NOT packed 4293 for (i=0; i < num_ranges; ++i) 4294 for (j=0; j < ranges[i].num_chars; ++j) 4295 ranges[i].chardata_for_range[j].x0 = 4296 ranges[i].chardata_for_range[j].y0 = 4297 ranges[i].chardata_for_range[j].x1 = 4298 ranges[i].chardata_for_range[j].y1 = 0; 4299 4300 n = 0; 4301 for (i=0; i < num_ranges; ++i) 4302 n += ranges[i].num_chars; 4303 4304 rects = cast(stbrp_rect *) STBTT_malloc(cast(uint)(*rects).sizeof * n, spc.user_allocator_context); 4305 if (rects == null) 4306 return 0; 4307 4308 info.userdata = spc.user_allocator_context; 4309 stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); 4310 4311 n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); 4312 4313 stbtt_PackFontRangesPackRects(spc, rects, n); 4314 4315 return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); 4316 4317 STBTT_free(rects, spc.user_allocator_context); 4318 return return_value; 4319 } 4320 4321 public int stbtt_PackFontRange(stbtt_pack_context *spc, const(ubyte)* fontdata, int font_index, float font_size, 4322 int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) 4323 { 4324 stbtt_pack_range range; 4325 range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; 4326 range.array_of_unicode_codepoints = null; 4327 range.num_chars = num_chars_in_range; 4328 range.chardata_for_range = chardata_for_range; 4329 range.font_size = font_size; 4330 return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 4331 } 4332 4333 public void stbtt_GetPackedQuad(const(stbtt_packedchar)* chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) 4334 { 4335 float ipw = 1.0f / pw, iph = 1.0f / ph; 4336 const(stbtt_packedchar)* b = chardata + char_index; 4337 4338 if (align_to_integer) { 4339 float x = cast(float) STBTT_ifloor((*xpos + b.xoff) + 0.5f); 4340 float y = cast(float) STBTT_ifloor((*ypos + b.yoff) + 0.5f); 4341 q.x0 = x; 4342 q.y0 = y; 4343 q.x1 = x + b.xoff2 - b.xoff; 4344 q.y1 = y + b.yoff2 - b.yoff; 4345 } else { 4346 q.x0 = *xpos + b.xoff; 4347 q.y0 = *ypos + b.yoff; 4348 q.x1 = *xpos + b.xoff2; 4349 q.y1 = *ypos + b.yoff2; 4350 } 4351 4352 q.s0 = b.x0 * ipw; 4353 q.t0 = b.y0 * iph; 4354 q.s1 = b.x1 * ipw; 4355 q.t1 = b.y1 * iph; 4356 4357 *xpos += b.xadvance; 4358 } 4359 4360 ////////////////////////////////////////////////////////////////////////////// 4361 // 4362 // sdf computation 4363 // 4364 4365 //#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) 4366 //#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) 4367 T STBTT_min(T) (in T a, in T b) pure { pragma(inline, true); return (a < b ? a : b); } 4368 T STBTT_max(T) (in T a, in T b) pure { pragma(inline, true); return (a < b ? b : a); } 4369 4370 private int stbtt__ray_intersect_bezier(in ref float[2] orig, in ref float[2] ray, in ref float[2] q0, in ref float[2] q1, in ref float[2] q2, ref float[2][2] hits) 4371 { 4372 float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; 4373 float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; 4374 float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; 4375 float roperp = orig[1]*ray[0] - orig[0]*ray[1]; 4376 4377 float a = q0perp - 2*q1perp + q2perp; 4378 float b = q1perp - q0perp; 4379 float c = q0perp - roperp; 4380 4381 float s0 = 0., s1 = 0.; 4382 int num_s = 0; 4383 4384 if (a != 0.0) { 4385 float discr = b*b - a*c; 4386 if (discr > 0.0) { 4387 float rcpna = -1 / a; 4388 float d = cast(float) STBTT_sqrt(discr); 4389 s0 = (b+d) * rcpna; 4390 s1 = (b-d) * rcpna; 4391 if (s0 >= 0.0 && s0 <= 1.0) 4392 num_s = 1; 4393 if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { 4394 if (num_s == 0) s0 = s1; 4395 ++num_s; 4396 } 4397 } 4398 } else { 4399 // 2*b*s + c = 0 4400 // s = -c / (2*b) 4401 s0 = c / (-2 * b); 4402 if (s0 >= 0.0 && s0 <= 1.0) 4403 num_s = 1; 4404 } 4405 4406 if (num_s == 0) 4407 return 0; 4408 else { 4409 float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); 4410 float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; 4411 4412 float q0d = q0[0]*rayn_x + q0[1]*rayn_y; 4413 float q1d = q1[0]*rayn_x + q1[1]*rayn_y; 4414 float q2d = q2[0]*rayn_x + q2[1]*rayn_y; 4415 float rod = orig[0]*rayn_x + orig[1]*rayn_y; 4416 4417 float q10d = q1d - q0d; 4418 float q20d = q2d - q0d; 4419 float q0rd = q0d - rod; 4420 4421 hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; 4422 hits[0][1] = a*s0+b; 4423 4424 if (num_s > 1) { 4425 hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; 4426 hits[1][1] = a*s1+b; 4427 return 2; 4428 } else { 4429 return 1; 4430 } 4431 } 4432 } 4433 4434 private int equal(float *a, float *b) 4435 { 4436 return (a[0] == b[0] && a[1] == b[1]); 4437 } 4438 4439 private int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) 4440 { 4441 int i; 4442 float[2] orig = void; 4443 float[2] ray = [ 1, 0 ]; 4444 float y_frac; 4445 int winding = 0; 4446 4447 orig[0] = x; 4448 orig[1] = y; 4449 4450 // make sure y never passes through a vertex of the shape 4451 y_frac = cast(float) STBTT_fmod(y, 1.0f); 4452 if (y_frac < 0.01f) 4453 y += 0.01f; 4454 else if (y_frac > 0.99f) 4455 y -= 0.01f; 4456 orig[1] = y; 4457 4458 // test a ray from (-infinity,y) to (x,y) 4459 for (i=0; i < nverts; ++i) { 4460 if (verts[i].type == STBTT_vline) { 4461 int x0 = cast(int) verts[i-1].x, y0 = cast(int) verts[i-1].y; 4462 int x1 = cast(int) verts[i ].x, y1 = cast(int) verts[i ].y; 4463 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4464 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4465 if (x_inter < x) 4466 winding += (y0 < y1) ? 1 : -1; 4467 } 4468 } 4469 if (verts[i].type == STBTT_vcurve) { 4470 int x0 = cast(int) verts[i-1].x , y0 = cast(int) verts[i-1].y ; 4471 int x1 = cast(int) verts[i ].cx, y1 = cast(int) verts[i ].cy; 4472 int x2 = cast(int) verts[i ].x , y2 = cast(int) verts[i ].y ; 4473 int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); 4474 int by = STBTT_max(y0,STBTT_max(y1,y2)); 4475 if (y > ay && y < by && x > ax) { 4476 float[2] q0, q1, q2; 4477 float[2][2] hits; 4478 q0[0] = cast(float)x0; 4479 q0[1] = cast(float)y0; 4480 q1[0] = cast(float)x1; 4481 q1[1] = cast(float)y1; 4482 q2[0] = cast(float)x2; 4483 q2[1] = cast(float)y2; 4484 if (equal(q0.ptr,q1.ptr) || equal(q1.ptr,q2.ptr)) { 4485 x0 = cast(int)verts[i-1].x; 4486 y0 = cast(int)verts[i-1].y; 4487 x1 = cast(int)verts[i ].x; 4488 y1 = cast(int)verts[i ].y; 4489 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4490 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4491 if (x_inter < x) 4492 winding += (y0 < y1) ? 1 : -1; 4493 } 4494 } else { 4495 int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); 4496 if (num_hits >= 1) 4497 if (hits[0][0] < 0) 4498 winding += (hits[0][1] < 0 ? -1 : 1); 4499 if (num_hits >= 2) 4500 if (hits[1][0] < 0) 4501 winding += (hits[1][1] < 0 ? -1 : 1); 4502 } 4503 } 4504 } 4505 } 4506 return winding; 4507 } 4508 4509 private float stbtt__cuberoot( float x ) 4510 { 4511 if (x<0) 4512 return -cast(float) STBTT_pow(-x,1.0f/3.0f); 4513 else 4514 return cast(float) STBTT_pow( x,1.0f/3.0f); 4515 } 4516 4517 // x^3 + c*x^2 + b*x + a = 0 4518 private int stbtt__solve_cubic(float a, float b, float c, float* r) 4519 { 4520 float s = -a / 3; 4521 float p = b - a*a / 3; 4522 float q = a * (2*a*a - 9*b) / 27 + c; 4523 float p3 = p*p*p; 4524 float d = q*q + 4*p3 / 27; 4525 if (d >= 0) { 4526 float z = cast(float) STBTT_sqrt(d); 4527 float u = (-q + z) / 2; 4528 float v = (-q - z) / 2; 4529 u = stbtt__cuberoot(u); 4530 v = stbtt__cuberoot(v); 4531 r[0] = s + u + v; 4532 return 1; 4533 } else { 4534 float u = cast(float) STBTT_sqrt(-p/3); 4535 float v = cast(float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative 4536 float m = cast(float) STBTT_cos(v); 4537 float n = cast(float) STBTT_cos(v-3.141592/2)*1.732050808f; 4538 r[0] = s + u * 2 * m; 4539 r[1] = s - u * (m + n); 4540 r[2] = s - u * (m - n); 4541 4542 //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? 4543 //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); 4544 //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); 4545 return 3; 4546 } 4547 } 4548 4549 public ubyte * stbtt_GetGlyphSDF(stbtt_fontinfo* info, float scale, int glyph, int padding, ubyte onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) 4550 { 4551 float scale_x = scale, scale_y = scale; 4552 int ix0,iy0,ix1,iy1; 4553 int w,h; 4554 ubyte *data; 4555 4556 // if one scale is 0, use same scale for both 4557 if (scale_x == 0) scale_x = scale_y; 4558 if (scale_y == 0) { 4559 if (scale_x == 0) return null; // if both scales are 0, return NULL 4560 scale_y = scale_x; 4561 } 4562 4563 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); 4564 4565 // if empty, return NULL 4566 if (ix0 == ix1 || iy0 == iy1) 4567 return null; 4568 4569 ix0 -= padding; 4570 iy0 -= padding; 4571 ix1 += padding; 4572 iy1 += padding; 4573 4574 w = (ix1 - ix0); 4575 h = (iy1 - iy0); 4576 4577 if (width ) *width = w; 4578 if (height) *height = h; 4579 if (xoff ) *xoff = ix0; 4580 if (yoff ) *yoff = iy0; 4581 4582 // invert for y-downwards bitmaps 4583 scale_y = -scale_y; 4584 4585 { 4586 int x,y,i,j; 4587 float *precompute; 4588 stbtt_vertex *verts; 4589 int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); 4590 data = cast(ubyte *) STBTT_malloc(w * h, info.userdata); 4591 precompute = cast(float *) STBTT_malloc(num_verts * cast(uint)float.sizeof, info.userdata); 4592 4593 for (i=0,j=num_verts-1; i < num_verts; j=i++) { 4594 if (verts[i].type == STBTT_vline) { 4595 float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; 4596 float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; 4597 float dist = cast(float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); 4598 precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; 4599 } else if (verts[i].type == STBTT_vcurve) { 4600 float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; 4601 float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; 4602 float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; 4603 float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; 4604 float len2 = bx*bx + by*by; 4605 if (len2 != 0.0f) 4606 precompute[i] = 1.0f / (bx*bx + by*by); 4607 else 4608 precompute[i] = 0.0f; 4609 } else 4610 precompute[i] = 0.0f; 4611 } 4612 4613 for (y=iy0; y < iy1; ++y) { 4614 for (x=ix0; x < ix1; ++x) { 4615 float val; 4616 float min_dist = 999999.0f; 4617 float sx = cast(float) x + 0.5f; 4618 float sy = cast(float) y + 0.5f; 4619 float x_gspace = (sx / scale_x); 4620 float y_gspace = (sy / scale_y); 4621 4622 int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path 4623 4624 for (i=0; i < num_verts; ++i) { 4625 float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; 4626 4627 // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve 4628 float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); 4629 if (dist2 < min_dist*min_dist) 4630 min_dist = cast(float) STBTT_sqrt(dist2); 4631 4632 if (verts[i].type == STBTT_vline) { 4633 float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; 4634 4635 // coarse culling against bbox 4636 //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && 4637 // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) 4638 float dist = cast(float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; 4639 assert(i != 0); 4640 if (dist < min_dist) { 4641 // check position along line 4642 // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) 4643 // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) 4644 float dx = x1-x0, dy = y1-y0; 4645 float px = x0-sx, py = y0-sy; 4646 // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy 4647 // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve 4648 float t = -(px*dx + py*dy) / (dx*dx + dy*dy); 4649 if (t >= 0.0f && t <= 1.0f) 4650 min_dist = dist; 4651 } 4652 } else if (verts[i].type == STBTT_vcurve) { 4653 float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; 4654 float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; 4655 float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); 4656 float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); 4657 float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); 4658 float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); 4659 // coarse culling against bbox to avoid computing cubic unnecessarily 4660 if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { 4661 int num=0; 4662 float ax = x1-x0, ay = y1-y0; 4663 float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; 4664 float mx = x0 - sx, my = y0 - sy; 4665 float[3] res; 4666 float px,py,t,it; 4667 float a_inv = precompute[i]; 4668 if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula 4669 float a = 3*(ax*bx + ay*by); 4670 float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); 4671 float c = mx*ax+my*ay; 4672 if (a == 0.0) { // if a is 0, it's linear 4673 if (b != 0.0) { 4674 res[num++] = -c/b; 4675 } 4676 } else { 4677 float discriminant = b*b - 4*a*c; 4678 if (discriminant < 0) 4679 num = 0; 4680 else { 4681 float root = cast(float) STBTT_sqrt(discriminant); 4682 res[0] = (-b - root)/(2*a); 4683 res[1] = (-b + root)/(2*a); 4684 num = 2; // don't bother distinguishing 1-solution case, as code below will still work 4685 } 4686 } 4687 } else { 4688 float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point 4689 float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; 4690 float d = (mx*ax+my*ay) * a_inv; 4691 num = stbtt__solve_cubic(b, c, d, res.ptr); 4692 } 4693 if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { 4694 t = res[0], it = 1.0f - t; 4695 px = it*it*x0 + 2*t*it*x1 + t*t*x2; 4696 py = it*it*y0 + 2*t*it*y1 + t*t*y2; 4697 dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); 4698 if (dist2 < min_dist * min_dist) 4699 min_dist = cast(float) STBTT_sqrt(dist2); 4700 } 4701 if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { 4702 t = res[1], it = 1.0f - t; 4703 px = it*it*x0 + 2*t*it*x1 + t*t*x2; 4704 py = it*it*y0 + 2*t*it*y1 + t*t*y2; 4705 dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); 4706 if (dist2 < min_dist * min_dist) 4707 min_dist = cast(float) STBTT_sqrt(dist2); 4708 } 4709 if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { 4710 t = res[2], it = 1.0f - t; 4711 px = it*it*x0 + 2*t*it*x1 + t*t*x2; 4712 py = it*it*y0 + 2*t*it*y1 + t*t*y2; 4713 dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); 4714 if (dist2 < min_dist * min_dist) 4715 min_dist = cast(float) STBTT_sqrt(dist2); 4716 } 4717 } 4718 } 4719 } 4720 if (winding == 0) 4721 min_dist = -min_dist; // if outside the shape, value is negative 4722 val = onedge_value + pixel_dist_scale * min_dist; 4723 if (val < 0) 4724 val = 0; 4725 else if (val > 255) 4726 val = 255; 4727 data[(y-iy0)*w+(x-ix0)] = cast(ubyte) val; 4728 } 4729 } 4730 STBTT_free(precompute, info.userdata); 4731 STBTT_free(verts, info.userdata); 4732 } 4733 return data; 4734 } 4735 4736 public ubyte * stbtt_GetCodepointSDF(stbtt_fontinfo* info, float scale, int codepoint, int padding, ubyte onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) 4737 { 4738 return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); 4739 } 4740 4741 public void stbtt_FreeSDF(ubyte *bitmap, void *userdata) 4742 { 4743 STBTT_free(bitmap, userdata); 4744 } 4745 4746 ////////////////////////////////////////////////////////////////////////////// 4747 // 4748 // font name matching -- recommended not to use this 4749 // 4750 4751 // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 4752 private stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) 4753 { 4754 stbtt_int32 i=0; 4755 4756 // convert utf16 to utf8 and compare the results while converting 4757 while (len2) { 4758 stbtt_uint16 ch = s2[0]*256 + s2[1]; 4759 if (ch < 0x80) { 4760 if (i >= len1) return -1; 4761 if (s1[i++] != ch) return -1; 4762 } else if (ch < 0x800) { 4763 if (i+1 >= len1) return -1; 4764 if (s1[i++] != 0xc0 + (ch >> 6)) return -1; 4765 if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; 4766 } else if (ch >= 0xd800 && ch < 0xdc00) { 4767 stbtt_uint32 c; 4768 stbtt_uint16 ch2 = s2[2]*256 + s2[3]; 4769 if (i+3 >= len1) return -1; 4770 c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; 4771 if (s1[i++] != 0xf0 + (c >> 18)) return -1; 4772 if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; 4773 if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; 4774 if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; 4775 s2 += 2; // plus another 2 below 4776 len2 -= 2; 4777 } else if (ch >= 0xdc00 && ch < 0xe000) { 4778 return -1; 4779 } else { 4780 if (i+2 >= len1) return -1; 4781 if (s1[i++] != 0xe0 + (ch >> 12)) return -1; 4782 if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; 4783 if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; 4784 } 4785 s2 += 2; 4786 len2 -= 2; 4787 } 4788 return i; 4789 } 4790 4791 private int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) 4792 { 4793 return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix(cast(stbtt_uint8*) s1, len1, cast(stbtt_uint8*) s2, len2); 4794 } 4795 4796 // returns results in whatever encoding you request... but note that 2-byte encodings 4797 // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare 4798 public const(char)* stbtt_GetFontNameString(stbtt_fontinfo* font, int *length, int platformID, int encodingID, int languageID, int nameID) 4799 { 4800 stbtt_int32 i,count,stringOffset; 4801 stbtt_uint8 *fc = font.data; 4802 stbtt_uint32 offset = font.fontstart; 4803 stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); 4804 if (!nm) return null; 4805 4806 count = ttUSHORT(fc+nm+2); 4807 stringOffset = nm + ttUSHORT(fc+nm+4); 4808 for (i=0; i < count; ++i) { 4809 stbtt_uint32 loc = nm + 6 + 12 * i; 4810 if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) 4811 && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { 4812 *length = ttUSHORT(fc+loc+8); 4813 return cast(const(char)* ) (fc+stringOffset+ttUSHORT(fc+loc+10)); 4814 } 4815 } 4816 return null; 4817 } 4818 4819 private int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) 4820 { 4821 stbtt_int32 i; 4822 stbtt_int32 count = ttUSHORT(fc+nm+2); 4823 stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); 4824 4825 for (i=0; i < count; ++i) { 4826 stbtt_uint32 loc = nm + 6 + 12 * i; 4827 stbtt_int32 id = ttUSHORT(fc+loc+6); 4828 if (id == target_id) { 4829 // find the encoding 4830 stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); 4831 4832 // is this a Unicode encoding? 4833 if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { 4834 stbtt_int32 slen = ttUSHORT(fc+loc+8); 4835 stbtt_int32 off = ttUSHORT(fc+loc+10); 4836 4837 // check if there's a prefix match 4838 stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); 4839 if (matchlen >= 0) { 4840 // check for target_id+1 immediately following, with same encoding & language 4841 if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { 4842 slen = ttUSHORT(fc+loc+12+8); 4843 off = ttUSHORT(fc+loc+12+10); 4844 if (slen == 0) { 4845 if (matchlen == nlen) 4846 return 1; 4847 } else if (matchlen < nlen && name[matchlen] == ' ') { 4848 ++matchlen; 4849 if (stbtt_CompareUTF8toUTF16_bigendian_internal(cast(char*) (name+matchlen), nlen-matchlen, cast(char*)(fc+stringOffset+off),slen)) 4850 return 1; 4851 } 4852 } else { 4853 // if nothing immediately following 4854 if (matchlen == nlen) 4855 return 1; 4856 } 4857 } 4858 } 4859 4860 // @TODO handle other encodings 4861 } 4862 } 4863 return 0; 4864 } 4865 4866 private int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) 4867 { 4868 stbtt_int32 nlen = cast(stbtt_int32) STBTT_strlen(cast(char *) name); 4869 stbtt_uint32 nm,hd; 4870 if (!stbtt__isfont(fc+offset)) return 0; 4871 4872 // check italics/bold/underline flags in macStyle... 4873 if (flags) { 4874 hd = stbtt__find_table(fc, offset, "head"); 4875 if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; 4876 } 4877 4878 nm = stbtt__find_table(fc, offset, "name"); 4879 if (!nm) return 0; 4880 4881 if (flags) { 4882 // if we checked the macStyle flags, then just check the family and ignore the subfamily 4883 if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; 4884 if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; 4885 if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 4886 } else { 4887 if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; 4888 if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; 4889 if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 4890 } 4891 4892 return 0; 4893 } 4894 4895 private int stbtt_FindMatchingFont_internal(ubyte *font_collection, char *name_utf8, stbtt_int32 flags) 4896 { 4897 stbtt_int32 i; 4898 for (i=0;;++i) { 4899 stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); 4900 if (off < 0) return off; 4901 if (stbtt__matches(cast(stbtt_uint8 *) font_collection, off, cast(stbtt_uint8*) name_utf8, flags)) 4902 return off; 4903 } 4904 } 4905 4906 public int stbtt_BakeFontBitmap(const(ubyte)* data, int offset, 4907 float pixel_height, ubyte *pixels, int pw, int ph, 4908 int first_char, int num_chars, stbtt_bakedchar *chardata, 4909 int* ascent = null, int* descent = null, int* line_gap = null 4910 ) 4911 { 4912 return stbtt_BakeFontBitmap_internal(cast(ubyte *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata, ascent, descent, line_gap); 4913 } 4914 4915 public int stbtt_GetFontOffsetForIndex(const(ubyte)* data, int index) 4916 { 4917 return stbtt_GetFontOffsetForIndex_internal(cast(ubyte *) data, index); 4918 } 4919 4920 public int stbtt_GetNumberOfFonts(const(ubyte)* data) 4921 { 4922 return stbtt_GetNumberOfFonts_internal(cast(ubyte *) data); 4923 } 4924 4925 public int stbtt_InitFont(stbtt_fontinfo *info, const(ubyte)* data, int offset) 4926 { 4927 return stbtt_InitFont_internal(info, cast(ubyte *) data, offset); 4928 } 4929 4930 public int stbtt_FindMatchingFont(const(ubyte)* fontdata, const(char)* name, int flags) 4931 { 4932 return stbtt_FindMatchingFont_internal(cast(ubyte *) fontdata, cast(char *) name, flags); 4933 } 4934 4935 public int stbtt_CompareUTF8toUTF16_bigendian(const(char)* s1, int len1, const(char)* s2, int len2) 4936 { 4937 return stbtt_CompareUTF8toUTF16_bigendian_internal(cast(char *) s1, len1, cast(char *) s2, len2); 4938 } 4939 4940 4941 // FULL VERSION HISTORY 4942 // 4943 // 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod 4944 // 1.18 (2018-01-29) add missing function 4945 // 1.17 (2017-07-23) make more arguments const; doc fix 4946 // 1.16 (2017-07-12) SDF support 4947 // 1.15 (2017-03-03) make more arguments const 4948 // 1.14 (2017-01-16) num-fonts-in-TTC function 4949 // 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts 4950 // 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual 4951 // 1.11 (2016-04-02) fix unused-variable warning 4952 // 1.10 (2016-04-02) allow user-defined fabs() replacement 4953 // fix memory leak if fontsize=0.0 4954 // fix warning from duplicate typedef 4955 // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges 4956 // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges 4957 // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; 4958 // allow PackFontRanges to pack and render in separate phases; 4959 // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); 4960 // fixed an assert() bug in the new rasterizer 4961 // replace assert() with STBTT_assert() in new rasterizer 4962 // 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) 4963 // also more precise AA rasterizer, except if shapes overlap 4964 // remove need for STBTT_sort 4965 // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC 4966 // 1.04 (2015-04-15) typo in example 4967 // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes 4968 // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ 4969 // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match 4970 // non-oversampled; STBTT_POINT_SIZE for packed case only 4971 // 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling 4972 // 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) 4973 // 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID 4974 // 0.8b (2014-07-07) fix a warning 4975 // 0.8 (2014-05-25) fix a few more warnings 4976 // 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back 4977 // 0.6c (2012-07-24) improve documentation 4978 // 0.6b (2012-07-20) fix a few more warnings 4979 // 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, 4980 // stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty 4981 // 0.5 (2011-12-09) bugfixes: 4982 // subpixel glyph renderer computed wrong bounding box 4983 // first vertex of shape can be off-curve (FreeSans) 4984 // 0.4b (2011-12-03) fixed an error in the font baking example 4985 // 0.4 (2011-12-01) kerning, subpixel rendering (tor) 4986 // bugfixes for: 4987 // codepoint-to-glyph conversion using table fmt=12 4988 // codepoint-to-glyph conversion using table fmt=4 4989 // stbtt_GetBakedQuad with non-square texture (Zer) 4990 // updated Hello World! sample to use kerning and subpixel 4991 // fixed some warnings 4992 // 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) 4993 // userdata, malloc-from-userdata, non-zero fill (stb) 4994 // 0.2 (2009-03-11) Fix unsigned/signed char warnings 4995 // 0.1 (2009-03-09) First public release 4996 // 4997 4998 /* 4999 ------------------------------------------------------------------------------ 5000 This software is available under 2 licenses -- choose whichever you prefer. 5001 ------------------------------------------------------------------------------ 5002 ALTERNATIVE A - MIT License 5003 Copyright (c) 2017 Sean Barrett 5004 Permission is hereby granted, free of charge, to any person obtaining a copy of 5005 this software and associated documentation files (the "Software"), to deal in 5006 the Software without restriction, including without limitation the rights to 5007 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5008 of the Software, and to permit persons to whom the Software is furnished to do 5009 so, subject to the following conditions: 5010 The above copyright notice and this permission notice shall be included in all 5011 copies or substantial portions of the Software. 5012 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 5013 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5014 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5015 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 5016 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 5017 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 5018 SOFTWARE. 5019 ------------------------------------------------------------------------------ 5020 ALTERNATIVE B - Public Domain (www.unlicense.org) 5021 This is free and unencumbered software released into the public domain. 5022 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 5023 software, either in source code form or as a compiled binary, for any purpose, 5024 commercial or non-commercial, and by any means. 5025 In jurisdictions that recognize copyright laws, the author or authors of this 5026 software dedicate any and all copyright interest in the software to the public 5027 domain. We make this dedication for the benefit of the public at large and to 5028 the detriment of our heirs and successors. We intend this dedication to be an 5029 overt act of relinquishment in perpetuity of all present and future rights to 5030 this software under copyright law. 5031 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 5032 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5033 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5034 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 5035 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 5036 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 5037 ------------------------------------------------------------------------------ 5038 */