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