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