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