From 18decf3e468fec0899321f577fe385f046a5fb93 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 10 Jan 2025 12:13:45 -0500 Subject: [PATCH] Add local version of stb_truetype --- thirdparty/stb/lib/.gitkeep | 0 thirdparty/stb/lib/darwin/stb_truetype.a | Bin 0 -> 138088 bytes thirdparty/stb/lib/stb_truetype.lib | Bin 0 -> 170652 bytes thirdparty/stb/lib/stb_truetype_wasm.o | Bin 0 -> 46482 bytes thirdparty/stb/src/Makefile | 60 + thirdparty/stb/src/build.bat | 8 + thirdparty/stb/src/stb_truetype.c | 2 + thirdparty/stb/src/stb_truetype.h | 5077 +++++++++++++++++ thirdparty/stb/truetype/stb_truetype.odin | 629 ++ .../stb/truetype/stb_truetype_wasm.odin | 4 + 10 files changed, 5780 insertions(+) create mode 100644 thirdparty/stb/lib/.gitkeep create mode 100644 thirdparty/stb/lib/darwin/stb_truetype.a create mode 100644 thirdparty/stb/lib/stb_truetype.lib create mode 100644 thirdparty/stb/lib/stb_truetype_wasm.o create mode 100644 thirdparty/stb/src/Makefile create mode 100644 thirdparty/stb/src/build.bat create mode 100644 thirdparty/stb/src/stb_truetype.c create mode 100644 thirdparty/stb/src/stb_truetype.h create mode 100644 thirdparty/stb/truetype/stb_truetype.odin create mode 100644 thirdparty/stb/truetype/stb_truetype_wasm.odin diff --git a/thirdparty/stb/lib/.gitkeep b/thirdparty/stb/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/stb/lib/darwin/stb_truetype.a b/thirdparty/stb/lib/darwin/stb_truetype.a new file mode 100644 index 0000000000000000000000000000000000000000..b55fbe5d3ea3866a28687d16a21f11f6bb211a38 GIT binary patch literal 138088 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 CtB$Y$ literal 0 HcmV?d00001 diff --git a/thirdparty/stb/lib/stb_truetype_wasm.o b/thirdparty/stb/lib/stb_truetype_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..e545ef8e7f2ae4527168f2c970ef30248bb5fb44 GIT binary patch literal 46482 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 literal 0 HcmV?d00001 diff --git a/thirdparty/stb/src/Makefile b/thirdparty/stb/src/Makefile new file mode 100644 index 0000000..194ea5e --- /dev/null +++ b/thirdparty/stb/src/Makefile @@ -0,0 +1,60 @@ +OS=$(shell uname) + +ifeq ($(OS), Darwin) +all: darwin +else +all: unix +endif + +wasm: + mkdir -p ../lib + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_image.c -o ../lib/stb_image_wasm.o -DSTBI_NO_STDIO + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_image_write.c -o ../lib/stb_image_write_wasm.o -DSTBI_WRITE_NO_STDIO + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_image_resize.c -o ../lib/stb_image_resize_wasm.o + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_truetype.c -o ../lib/stb_truetype_wasm.o + # $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_vorbis.c -o ../lib/stb_vorbis_wasm.o -DSTB_VORBIS_NO_STDIO + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_rect_pack.c -o ../lib/stb_rect_pack_wasm.o + $(CC) -c -Os --target=wasm32 stb_sprintf.c -o ../lib/stb_sprintf_wasm.o + +unix: + mkdir -p ../lib + $(CC) -c -O2 -Os -fPIC stb_image.c stb_image_write.c stb_image_resize.c stb_truetype.c stb_rect_pack.c stb_vorbis.c stb_sprintf.c + $(AR) rcs ../lib/stb_image.a stb_image.o + $(AR) rcs ../lib/stb_image_write.a stb_image_write.o + $(AR) rcs ../lib/stb_image_resize.a stb_image_resize.o + $(AR) rcs ../lib/stb_truetype.a stb_truetype.o + $(AR) rcs ../lib/stb_rect_pack.a stb_rect_pack.o + $(AR) rcs ../lib/stb_vorbis.a stb_vorbis.o + $(AR) rcs ../lib/stb_sprintf.a stb_sprintf.o + #$(CC) -fPIC -shared -Wl,-soname=stb_image.so -o ../lib/stb_image.so stb_image.o + #$(CC) -fPIC -shared -Wl,-soname=stb_image_write.so -o ../lib/stb_image_write.so stb_image_write.o + #$(CC) -fPIC -shared -Wl,-soname=stb_image_resize.so -o ../lib/stb_image_resize.so stb_image_resize.o + #$(CC) -fPIC -shared -Wl,-soname=stb_truetype.so -o ../lib/stb_truetype.so stb_image_truetype.o + #$(CC) -fPIC -shared -Wl,-soname=stb_rect_pack.so -o ../lib/stb_rect_pack.so stb_rect_packl.o + #$(CC) -fPIC -shared -Wl,-soname=stb_vorbis.so -o ../lib/stb_vorbis.so stb_vorbisl.o + rm *.o + +darwin: + mkdir -p ../lib + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_image.c -o stb_image-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_image.c -o stb_image-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_image-x86_64.o stb_image-arm64.o -output ../lib/darwin/stb_image.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_image_write.c -o stb_image_write-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_image_write.c -o stb_image_write-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_image_write-x86_64.o stb_image_write-arm64.o -output ../lib/darwin/stb_image_write.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_image_resize.c -o stb_image_resize-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_image_resize.c -o stb_image_resize-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_image_resize-x86_64.o stb_image_resize-arm64.o -output ../lib/darwin/stb_image_resize.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_truetype.c -o stb_truetype-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_truetype.c -o stb_truetype-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_truetype-x86_64.o stb_truetype-arm64.o -output ../lib/darwin/stb_truetype.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_rect_pack.c -o stb_rect_pack-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_rect_pack.c -o stb_rect_pack-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_rect_pack-x86_64.o stb_rect_pack-arm64.o -output ../lib/darwin/stb_rect_pack.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_vorbis.c -o stb_vorbis-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_vorbis.c -o stb_vorbis-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_vorbis-x86_64.o stb_vorbis-arm64.o -output ../lib/darwin/stb_vorbis.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_sprintf.c -o stb_sprintf-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_sprintf.c -o stb_sprintf-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_sprintf-x86_64.o stb_sprintf-arm64.o -output ../lib/darwin/stb_sprintf.a + rm *.o diff --git a/thirdparty/stb/src/build.bat b/thirdparty/stb/src/build.bat new file mode 100644 index 0000000..e83d765 --- /dev/null +++ b/thirdparty/stb/src/build.bat @@ -0,0 +1,8 @@ +@echo off + +if not exist "..\lib" mkdir ..\lib + +cl -nologo -MT -TC -O2 -c stb_image.c stb_truetype.c +lib -nologo stb_truetype.obj -out:..\lib\stb_truetype.lib + +del *.obj diff --git a/thirdparty/stb/src/stb_truetype.c b/thirdparty/stb/src/stb_truetype.c new file mode 100644 index 0000000..05c23f5 --- /dev/null +++ b/thirdparty/stb/src/stb_truetype.c @@ -0,0 +1,2 @@ +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" \ No newline at end of file diff --git a/thirdparty/stb/src/stb_truetype.h b/thirdparty/stb/src/stb_truetype.h new file mode 100644 index 0000000..5e2a2e4 --- /dev/null +++ b/thirdparty/stb/src/stb_truetype.h @@ -0,0 +1,5077 @@ +// stb_truetype.h - v1.26 - public domain +// authored from 2009-2021 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.26 (2021-08-28) fix broken rasterizer +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *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); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *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); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +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); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +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); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *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); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *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); +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); +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); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start, last; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + last = ttUSHORT(data + endCount + 2*item); + if (unicode_codepoint < start || unicode_codepoint > last) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static 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) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + 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); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + 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]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // FALLTHROUGH + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && b0 < 32) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch (coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + break; + } + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + break; + } + + default: return -1; // unsupported + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch (classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + break; + } + + default: + return -1; // Unsupported definition type, return an error. + } + + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i= pairSetCount) return 0; + + needle=glyph2; + r=pairValueCount-1; + l=0; + + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } else + return 0; + break; + } + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + stbtt_uint8 *class1Records, *class2Records; + stbtt_int16 xAdvance; + + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + + class1Records = table + 16; + class2Records = class1Records + 2 * (glyph1class * class2Count); + xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } + + default: + return 0; // Unsupported position format + } + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +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) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +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) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +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) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +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) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + 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 + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) +{ + STBTT_assert(top_width >= 0); + STBTT_assert(bottom_width >= 0); + return (top_width + bottom_width) / 2.0f * height; +} + +static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) +{ + return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); +} + +static float stbtt__sized_triangle_area(float height, float width) +{ + return height * width / 2; +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = (sy1 - sy0) * e->direction; + STBTT_assert(x >= 0 && x < len); + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); + scanline_fill[x] += height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, y_final, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + STBTT_assert(dy >= 0); + STBTT_assert(dx >= 0); + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = y_top + dy * (x1+1 - x0); + + // compute intersection with y axis at x2 + y_final = y_top + dy * (x2 - x0); + + // x1 x_top x2 x_bottom + // y_top +------|-----+------------+------------+--------|---+------------+ + // | | | | | | + // | | | | | | + // sy0 | Txxxxx|............|............|............|............| + // y_crossing | *xxxxx.......|............|............|............| + // | | xxxxx..|............|............|............| + // | | /- xx*xxxx........|............|............| + // | | dy < | xxxxxx..|............|............| + // y_final | | \- | xx*xxx.........|............| + // sy1 | | | | xxxxxB...|............| + // | | | | | | + // | | | | | | + // y_bottom +------------+------------+------------+------------+------------+ + // + // goal is to measure the area covered by '.' in each pixel + + // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 + // @TODO: maybe test against sy1 rather than y_bottom? + if (y_crossing > y_bottom) + y_crossing = y_bottom; + + sign = e->direction; + + // area of the rectangle covered from sy0..y_crossing + area = sign * (y_crossing-sy0); + + // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) + scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); + + // check if final y_crossing is blown up; no test case for this + if (y_final > y_bottom) { + y_final = y_bottom; + dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } + + // in second pixel, area covered by line segment found in first pixel + // is always a rectangle 1 wide * the height of that line segment; this + // is exactly what the variable 'area' stores. it also gets a contribution + // from the line segment within it. the THIRD pixel will get the first + // pixel's rectangle contribution, the second pixel's rectangle contribution, + // and its own contribution. the 'own contribution' is the same in every pixel except + // the leftmost and rightmost, a trapezoid that slides down in each pixel. + // the second pixel's contribution to the third pixel will be the + // rectangle 1 wide times the height change in the second pixel, which is dy. + + step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, + // which multiplied by 1-pixel-width is how much pixel area changes for each step in x + // so the area advances by 'step' every time + + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; // area of trapezoid is 1*step/2 + area += step; + } + STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down + STBTT_assert(sy1 > y_final-0.01f); + + // area covered in the last pixel is the rectangle from all the pixels to the left, + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge + scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); + + // the rest of the line is filled based on the total height of the line segment in this pixel + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + // note though that this does happen some of the time because + // x_top and x_bottom can be extrapolated at the top & bottom of + // the shape and actually lie outside the bounding box + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static 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) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static 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) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static 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) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF 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) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *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) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *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) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *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) +{ + 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)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF 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) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *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) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF 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) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + + orig[0] = x; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + a*x^2 + b*x + c = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //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? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + 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 + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // 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 + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + 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) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/thirdparty/stb/truetype/stb_truetype.odin b/thirdparty/stb/truetype/stb_truetype.odin new file mode 100644 index 0000000..bd521c6 --- /dev/null +++ b/thirdparty/stb/truetype/stb_truetype.odin @@ -0,0 +1,629 @@ +package stb_truetype + +import c "core:c" +import stbrp "vendor:stb/rect_pack" + +@(private) +LIB :: ( + "../lib/stb_truetype.lib" when ODIN_OS == .Windows + else "../lib/stb_truetype.a" when ODIN_OS == .Linux + else "../lib/darwin/stb_truetype.a" when ODIN_OS == .Darwin + else "../lib/stb_truetype_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 + else "" +) + +when LIB != "" { + when !#exists(LIB) { + #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") + } +} + +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import stbtt "../lib/stb_truetype_wasm.o" +} else when LIB != "" { + foreign import stbtt { LIB } +} else { + foreign import stbtt "system:stb_truetype" +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#assert(size_of(c.int) == size_of(rune)) +#assert(size_of(c.int) == size_of(b32)) + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +bakedchar :: struct { + x0, y0, x1, y1: u16, // coordinates of bbox in bitmap + xoff, yoff, xadvance: f32, +} + +aligned_quad :: struct { + x0, y0, s0, t0: f32, // top-left + x1, y1, s1, t1: f32, // bottom-right +} + + + +// bindings +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // if return is positive, the first unused row of the bitmap + // if return is negative, returns the negative of the number of characters that fit + // if return is 0, no characters fit and no rows were used + // This uses a very crappy packing. + BakeFontBitmap :: proc(data: [^]byte, offset: c.int, // font location (use offset=0 for plain .ttf) + pixel_height: f32, // height of font in pixels + pixels: [^]byte, pw, ph: c.int, // bitmap to be filled in + first_char, num_chars: c.int, // characters to bake + chardata: [^]bakedchar, // you allocate this, it's num_chars long + ) -> c.int --- + + // Call GetBakedQuad with char_index = 'character - first_char', and it + // creates the quad you need to draw and advances the current position. + // + // The coordinate system used assumes y increases downwards. + // + // Characters will extend both above and below the current position; + // see discussion of "BASELINE" above. + // + // It's inefficient; you might want to c&p it and optimize it. + GetBakedQuad :: proc(chardata: ^bakedchar, pw, ph: c.int, // same data as above + char_index: c.int, // character to display + xpos, ypos: ^f32, // pointers to current position in screen pixel space + q: ^aligned_quad, // output: quad to draw + opengl_fillrule: b32, // true if opengl fill rule; false if DX9 or earlier + ) --- + + // Query the font vertical metrics without having to create a font first. + GetScaledFontVMetrics :: proc(fontdata: [^]byte, index: c.int, size: f32, ascent, descent, lineGap: ^f32) --- + +} + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +packedchar :: struct { + x0, y0, x1, y1: u16, + xoff, yoff, xadvance: f32, + xoff2, yoff2: f32, +} + +pack_range :: struct { + font_size: f32, + first_unicode_codepoint_in_range: c.int, + array_of_unicode_codepoints: [^]rune, + num_chars: c.int, + chardata_for_range: ^packedchar, + _, _: u8, // used internally to store oversample info +} + +pack_context :: struct { + user_allocator_context, pack_info: rawptr, + width, height, stride_in_bytes, padding: c.int, + skip_missing: b32, + h_oversample, v_oversample: u32, + pixels: [^]byte, + nodes: rawptr, +} + +POINT_SIZE :: #force_inline proc(x: $T) -> T { return -x } // @NOTE: this was a macro + +// bindings +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // Initializes a packing context stored in the passed-in stbtt_pack_context. + // Future calls using this context will pack characters into the bitmap passed + // in here: a 1-channel bitmap that is width * height. stride_in_bytes is + // the distance from one row to the next (or 0 to mean they are packed tightly + // together). "padding" is the amount of padding to leave between each + // character (normally you want '1' for bitmaps you'll use as textures with + // bilinear filtering). + // + // Returns 0 on failure, 1 on success. + PackBegin :: proc(spc: ^pack_context, pixels: [^]byte, width, height, stride_in_bytes, padding: c.int, alloc_context: rawptr) -> c.int --- + + // Cleans up the packing context and frees all memory. + PackEnd :: proc(spc: ^pack_context) --- + + // Creates character bitmaps from the font_index'th font found in fontdata (use + // font_index=0 if you don't know what that is). It creates num_chars_in_range + // bitmaps for characters with unicode values starting at first_unicode_char_in_range + // and increasing. Data for how to render them is stored in chardata_for_range; + // pass these to stbtt_GetPackedQuad to get back renderable quads. + // + // font_size is the full height of the character from ascender to descender, + // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed + // by stbtt_ScaleForMappingEmToPixels, wrap the point size in POINT_SIZE() + // and pass that result as 'font_size': + // ..., 20 , ... // font max minus min y is 20 pixels tall + // ..., POINT_SIZE(20), ... // 'M' is 20 pixels tall + PackFontRange :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, font_size: f32, first_unicode_char_in_range, num_chars_in_range: c.int, chardata_for_range: ^packedchar) -> c.int --- + + // Creates character bitmaps from multiple ranges of characters stored in + // ranges. This will usually create a better-packed bitmap than multiple + // calls to stbtt_PackFontRange. Note that you can call this multiple + // times within a single PackBegin/PackEnd. + PackFontRanges :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, ranges: [^]pack_range, num_ranges: c.int) -> c.int --- + + // Oversampling a font increases the quality by allowing higher-quality subpixel + // positioning, and is especially valuable at smaller text sizes. + // + // This function sets the amount of oversampling for all following calls to + // stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given + // pack context. The default (no oversampling) is achieved by h_oversample=1 + // and v_oversample=1. The total number of pixels required is + // h_oversample*v_oversample larger than the default; for example, 2x2 + // oversampling requires 4x the storage of 1x1. For best results, render + // oversampled textures with bilinear filtering. Look at the readme in + // stb/tests/oversample for information about oversampled fonts + // + // To use with PackFontRangesGather etc., you must set it before calls + // call to PackFontRangesGatherRects. + PackSetOversampling :: proc(spc: ^pack_context, h_oversample, v_oversample: c.uint) --- + + // If skip != false, this tells stb_truetype to skip any codepoints for which + // there is no corresponding glyph. If skip=false, which is the default, then + // codepoints without a glyph recived the font's "missing character" glyph, + // typically an empty box by convention. + PackSetSkipMissingCodepoints :: proc(spc: ^pack_context, skip: b32) --- + + GetPackedQuad :: proc(chardata: ^packedchar, pw, ph: c.int, // same data as above + char_index: c.int, // character to display + xpos, ypos: ^f32, // pointers to current position in screen pixel space + q: ^aligned_quad, // output: quad to draw + align_to_integer: b32, + ) --- + + // Calling these functions in sequence is roughly equivalent to calling + // stbtt_PackFontRanges(). If you more control over the packing of multiple + // fonts, or if you want to pack custom data into a font texture, take a look + // at the source to of stbtt_PackFontRanges() and create a custom version + // using these functions, e.g. call GatherRects multiple times, + // building up a single array of rects, then call PackRects once, + // then call RenderIntoRects repeatedly. This may result in a + // better packing than calling PackFontRanges multiple times + // (or it may not). + PackFontRangesGatherRects :: proc(spc: ^pack_context, #by_ptr info: fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- + PackFontRangesPackRects :: proc(spc: ^pack_context, rects: [^]stbrp.Rect, num_rects: c.int) --- + PackFontRangesRenderIntoRects :: proc(spc: ^pack_context, #by_ptr info: fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- +} + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +fontinfo :: struct { + userdata: rawptr, + data: [^]byte, + fontstart: c.int, + + numGlyphs: c.int, + + loca, head, glyf, hhea, hmtx, kern, gpos, svg: c.int, + index_map: c.int, + indexToLocFormat: c.int, + + cff: _buf, + charstrings: _buf, + gsubrs: _buf, + subrs: _buf, + fontdicts: _buf, + fdselect: _buf, +} + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // Given an offset into the file that defines a font, this function builds + // the necessary cached info for the rest of the system. You must allocate + // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't + // need to do anything special to free it, because the contents are pure + // value data with no additional data structures. Returns 0 on failure. + InitFont :: proc(info: ^fontinfo, data: [^]byte, offset: c.int) -> b32 --- + + // This function will determine the number of fonts in a font file. TrueType + // collection (.ttc) files may contain multiple fonts, while TrueType font + // (.ttf) files only contain one font. The number of fonts can be used for + // indexing with the previous function where the index is between zero and one + // less than the total fonts. If an error occurs, -1 is returned. + GetNumberOfFonts :: proc(data: [^]byte) -> c.int --- + + // Each .ttf/.ttc file may have more than one font. Each font has a sequential + // index number starting from 0. Call this function to get the font offset for + // a given index; it returns -1 if the index is out of range. A regular .ttf + // file will only define one font and it always be at offset 0, so it will + // return '0' for index 0, and -1 for all other indices. + GetFontOffsetForIndex :: proc(data: [^]byte, index: c.int) -> c.int --- +} + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSION + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // If you're going to perform multiple operations on the same character + // and you want a speed-up, call this function with the character you're + // going to process, then use glyph-based functions instead of the + // codepoint-based functions. + // Returns 0 if the character codepoint is not defined in the font. + FindGlyphIndex :: proc(#by_ptr info: fontinfo, unicode_codepoint: rune) -> c.int --- +} + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // computes a scale factor to produce a font whose "height" is 'pixels' tall. + // Height is measured as the distance from the highest ascender to the lowest + // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics + // and computing: + // scale = pixels / (ascent - descent) + // so if you prefer to measure height by the ascent only, use a similar calculation. + ScaleForPixelHeight :: proc(#by_ptr info: fontinfo, pixels: f32) -> f32 --- + + // computes a scale factor to produce a font whose EM size is mapped to + // 'pixels' tall. This is probably what traditional APIs compute, but + // I'm not positive. + ScaleForMappingEmToPixels :: proc(#by_ptr info: fontinfo, pixels: f32) -> f32 --- + + // ascent is the coordinate above the baseline the font extends; descent + // is the coordinate below the baseline the font extends (i.e. it is typically negative) + // lineGap is the spacing between one row's descent and the next row's ascent... + // so you should advance the vertical position by "*ascent - *descent + *lineGap" + // these are expressed in unscaled coordinates, so you must multiply by + // the scale factor for a given size + GetFontVMetrics :: proc(#by_ptr info: fontinfo, ascent, descent, lineGap: ^c.int) --- + + // analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 + // table (specific to MS/Windows TTF files). + // + // Returns 1 on success (table present), 0 on failure. + GetFontVMetricsOS2 :: proc(#by_ptr info: fontinfo, typoAscent, typoDescent, typoLineGap: ^c.int) -> b32 --- + + // the bounding box around all possible characters + GetFontBoundingBox :: proc(#by_ptr info: fontinfo, x0, y0, x1, y1: ^c.int) --- + + // leftSideBearing is the offset from the current horizontal position to the left edge of the character + // advanceWidth is the offset from the current horizontal position to the next horizontal position + // these are expressed in unscaled coordinates + GetCodepointHMetrics :: proc(#by_ptr info: fontinfo, codepoint: rune, advanceWidth, leftSideBearing: ^c.int) --- + + // an additional amount to add to the 'advance' value between ch1 and ch2 + GetCodepointKernAdvance :: proc(#by_ptr info: fontinfo, ch1, ch2: rune) -> (advance: c.int) --- + + // Gets the bounding box of the visible part of the glyph, in unscaled coordinates + GetCodepointBox :: proc(#by_ptr info: fontinfo, codepoint: rune, x0, y0, x1, y1: ^c.int) -> c.int --- + + // as above, but takes one or more glyph indices for greater efficiency + GetGlyphHMetrics :: proc(#by_ptr info: fontinfo, glyph_index: c.int, advanceWidth, leftSideBearing: ^c.int) --- + GetGlyphKernAdvance :: proc(#by_ptr info: fontinfo, glyph1, glyph2: c.int) -> c.int --- + GetGlyphBox :: proc(#by_ptr info : fontinfo, glyph_index: c.int, x0, y0, x1, y1: ^c.int) -> c.int --- +} + +kerningentry :: struct { + glyph1: rune, // use FindGlyphIndex + glyph2: rune, + advance: c.int, +} + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // Retrieves a complete list of all of the kerning pairs provided by the font + // stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. + // The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + GetKerningTableLength :: proc(#by_ptr info: fontinfo) -> c.int --- + GetKerningTable :: proc(#by_ptr info: fontinfo, table: [^]kerningentry, table_length: c.int) -> c.int --- +} + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +vmove :: enum c.int { + none, + vmove=1, + vline, + vcurve, + vcubic, +} + +vertex_type :: distinct c.short // can't use stbtt_int16 because that's not visible in the header file +vertex :: struct { + x, y, cx, cy, cx1, cy1: vertex_type, + type, padding: byte, +} + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // returns true if nothing is drawn for this glyph + IsGlyphEmpty :: proc(#by_ptr info: fontinfo, glyph_index: c.int) -> b32 --- + + // returns # of vertices and fills *vertices with the pointer to them + // these are expressed in "unscaled" coordinates + // + // The shape is a series of contours. Each one starts with + // a STBTT_moveto, then consists of a series of mixed + // STBTT_lineto and STBTT_curveto segments. A lineto + // draws a line from previous endpoint to its x,y; a curveto + // draws a quadratic bezier from previous endpoint to + // its x,y, using cx,cy as the bezier control point. + GetCodepointShape :: proc(#by_ptr info: fontinfo, unicode_codepoint: rune, vertices: ^[^]vertex) -> c.int --- + GetGlyphShape :: proc(#by_ptr info: fontinfo, glyph_index: c.int, vertices: ^[^]vertex) -> c.int --- + + // frees the data allocated above + FreeShape :: proc(#by_ptr info: fontinfo, vertices: [^]vertex) --- + + // fills svg with the character's SVG data. + // returns data size or 0 if SVG not found. + FindSVGDoc :: proc(#by_ptr info: fontinfo, gl: b32) -> [^]byte --- + GetCodepointSVG :: proc(#by_ptr info: fontinfo, unicode_codepoint: rune, svg: ^cstring) -> c.int --- + GetGlyphSVG :: proc(#by_ptr info: fontinfo, gl: b32, svg: ^cstring) -> c.int --- +} + + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +_bitmap :: struct { + w, h, stride: c.int, + pixels: [^]byte, +} + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // frees the bitmap allocated below + FreeBitmap :: proc(bitmap: [^]byte, userdata: rawptr) --- + + // allocates a large-enough single-channel 8bpp bitmap and renders the + // specified character/glyph at the specified scale into it, with + // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). + // *width & *height are filled out with the width & height of the bitmap, + // which is stored left-to-right, top-to-bottom. + // + // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + GetCodepointBitmap :: proc(#by_ptr info: fontinfo, scale_x, scale_y: f32, codepoint: rune, width, height, xoff, yoff: ^c.int) -> [^]byte --- + + // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel + // shift for the character + GetCodepointBitmapSubpixel :: proc(#by_ptr info: fontinfo, scale_x, scale_y, shift_x, shift_y: f32, codepoint: rune, width, height, xoff, yoff: ^c.int) -> [^]byte --- + + // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap + // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap + // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the + // width and height and positioning info for it first. + MakeCodepointBitmap :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y: f32, codepoint: rune) --- + + // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel + // shift for the character + MakeCodepointBitmapSubpixel :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, codepoint: rune) --- + + // same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering + // is performed (see stbtt_PackSetOversampling) + MakeCodepointBitmapSubpixelPrefilter :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, oversample_x, oversample_y: b32, sub_x, sub_y: ^f32, codepoint: rune) --- + + // get the bbox of the bitmap centered around the glyph origin; so the + // bitmap width is ix1-ix0, height is iy1-iy0, and location to place + // the bitmap top left is (leftSideBearing*scale,iy0). + // (Note that the bitmap uses y-increases-down, but the shape uses + // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + GetCodepointBitmapBox :: proc(#by_ptr font: fontinfo, codepoint: rune, scale_x, scale_y: f32, ix0, iy0, ix1, iy1: ^c.int) --- + + // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel + // shift for the character + GetCodepointBitmapBoxSubpixel :: proc(#by_ptr font: fontinfo, codepoint: rune, scale_x, scale_y, shift_x, shift_y: f32, ix0, iy0, ix1, iy1: ^c.int) --- + + // the following functions are equivalent to the above functions, but operate + // on glyph indices instead of Unicode codepoints (for efficiency) + GetGlyphBitmap :: proc(#by_ptr info: fontinfo, scale_x, scale_y: f32, glyph: c.int, width, height, xoff, yoff: ^c.int) -> [^]byte --- + GetGlyphBitmapSubpixel :: proc(#by_ptr info: fontinfo, scale_x, scale_y, shift_x, shift_y: f32, glyph: c.int, width, height, xoff, yoff: ^c.int) -> [^]byte --- + MakeGlyphBitmap :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y: f32, glyph: c.int) --- + MakeGlyphBitmapSubpixel :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, glyph: c.int) --- + MakeGlyphBitmapSubpixelPrefilter :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, oversample_x, oversample_y: c.int, sub_x, sub_y: ^f32, glyph: c.int) --- + GetGlyphBitmapBox :: proc(#by_ptr font: fontinfo, glyph: c.int, scale_x, scale_y: f32, ix0, iy0, ix1, iy1: ^c.int) --- + GetGlyphBitmapBoxSubpixel :: proc(#by_ptr font: fontinfo, glyph: c.int, scale_x, scale_y, shift_x, shift_y: f32, ix0, iy0, ix1, iy1: ^c.int) --- + + // rasterize a shape with quadratic beziers into a bitmap + Rasterize :: proc(result: ^_bitmap, // 1-channel bitmap to draw into + flatness_in_pixels: f32, // allowable error of curve in pixels + vertices: [^]vertex, // array of vertices defining shape + num_verts: c.int, // number of vertices in above array + scale_x, scale_y: f32, // scale applied to input vertices + shift_x, shift_y: f32, // translation applied to input vertices + x_off, y_off: c.int, // another translation applied to input + invert: b32, // if non-zero, vertically flip shape + userdata: rawptr, // context for to STBTT_MALLOC + ) --- + +} + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering +// + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // frees the SDF bitmap allocated below + FreeSDF :: proc(bitmap: [^]byte, userdata: rawptr) --- + + // These functions compute a discretized SDF field for a single character, suitable for storing + // in a single-channel texture, sampling with bilinear filtering, and testing against + // larger than some threshold to produce scalable fonts. + // info -- the font + // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap + // glyph/codepoint -- the character to generate the SDF for + // padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), + // which allows effects like bit outlines + // onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) + // pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) + // if positive, > onedge_value is inside; if negative, < onedge_value is inside + // width,height -- output height & width of the SDF bitmap (including padding) + // xoff,yoff -- output origin of the character + // return value -- a 2D array of bytes 0..255, width*height in size + // + // pixel_dist_scale & onedge_value are a scale & bias that allows you to make + // optimal use of the limited 0..255 for your application, trading off precision + // and special effects. SDF values outside the range 0..255 are clamped to 0..255. + // + // Example: + // scale = stbtt_ScaleForPixelHeight(22) + // padding = 5 + // onedge_value = 180 + // pixel_dist_scale = 180/5.0 = 36.0 + // + // This will create an SDF bitmap in which the character is about 22 pixels + // high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled + // shape, sample the SDF at each pixel and fill the pixel if the SDF value + // is greater than or equal to 180/255. (You'll actually want to antialias, + // which is beyond the scope of this example.) Additionally, you can compute + // offset outlines (e.g. to stroke the character border inside & outside, + // or only outside). For example, to fill outside the character up to 3 SDF + // pixels, you would compare against (180-36.0*3)/255 = 72/255. The above + // choice of variables maps a range from 5 pixels outside the shape to + // 2 pixels inside the shape to 0..255; this is intended primarily for apply + // outside effects only (the interior range is needed to allow proper + // antialiasing of the font at *smaller* sizes) + // + // The function computes the SDF analytically at each SDF pixel, not by e.g. + // building a higher-res bitmap and approximating it. In theory the quality + // should be as high as possible for an SDF of this size & representation, but + // unclear if this is true in practice (perhaps building a higher-res bitmap + // and computing from that can allow drop-out prevention). + // + // The algorithm has not been optimized at all, so expect it to be slow + // if computing lots of characters or very large sizes. + + GetGlyphSDF :: proc(#by_ptr info: fontinfo, scale: f32, glyph, padding: c.int, onedge_value: u8, pixel_dist_scale: f32, width, height, xoff, yoff: ^c.int) -> [^]byte --- + GetCodepointSDF :: proc(#by_ptr info: fontinfo, scale: f32, codepoint, padding: c.int, onedge_value: u8, pixel_dist_scale: f32, width, height, xoff, yoff: ^c.int) -> [^]byte --- +} + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + +MACSTYLE_DONTCARE :: 0 +MACSTYLE_BOLD :: 1 +MACSTYLE_ITALIC :: 2 +MACSTYLE_UNDERSCORE :: 4 +MACSTYLE_NONE :: 8 // <= not same as 0, this makes us check the bitfield is 0 + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + // returns the offset (not index) of the font that matches, or -1 if none + // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". + // if you use any other flag, use a font name like "Arial"; this checks + // the 'macStyle' header field; i don't know if fonts set this consistently + FindMatchingFont :: proc(fontdata: [^]byte, name: cstring, flags: c.int) -> c.int --- + + // returns 1/0 whether the first string interpreted as utf8 is identical to + // the second string interpreted as big-endian utf16... useful for strings from next func + CompareUTF8toUTF16_bigendian :: proc(s1: cstring, len1: c.int, s2: cstring, len2: c.int) -> c.int --- + + // returns the string (which may be big-endian double byte, e.g. for unicode) + // and puts the length in bytes in *length. + // + // some of the values for the IDs are below; for more see the truetype spec: + // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html + // http://www.microsoft.com/typography/otspec/name.htm + GetFontNameString :: proc(#by_ptr font: fontinfo, length: ^c.int, platformID: PLATFORM_ID, encodingID, languageID, nameID: c.int) -> cstring --- +} + + +PLATFORM_ID :: enum c.int { // platformID + PLATFORM_ID_UNICODE = 0, + PLATFORM_ID_MAC = 1, + PLATFORM_ID_ISO = 2, + PLATFORM_ID_MICROSOFT = 3, +} + +// encodingID for PLATFORM_ID_UNICODE +UNICODE_EID_UNICODE_1_0 :: 0 +UNICODE_EID_UNICODE_1_1 :: 1 +UNICODE_EID_ISO_10646 :: 2 +UNICODE_EID_UNICODE_2_0_BMP :: 3 +UNICODE_EID_UNICODE_2_0_FULL :: 4 + +// encodingID for PLATFORM_ID_MICROSOFT +MS_EID_SYMBOL :: 0 +MS_EID_UNICODE_BMP :: 1 +MS_EID_SHIFTJIS :: 2 +MS_EID_UNICODE_FULL :: 10 + + +// encodingID for PLATFORM_ID_MAC; same as Script Manager codes +MAC_EID_ROMAN, MAC_EID_ARABIC :: 0, 4 +MAC_EID_JAPANESE, MAC_EID_HEBREW :: 1, 5 +MAC_EID_CHINESE_TRAD, MAC_EID_GREEK :: 2, 6 +MAC_EID_KOREAN, MAC_EID_RUSSIAN :: 3, 7 + +// languageID for PLATFORM_ID_MICROSOFT; same as LCID... +// problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs +MS_LANG_ENGLISH, MS_LANG_ITALIAN :: 0x0409, 0x0410 +MS_LANG_CHINESE, MS_LANG_JAPANESE :: 0x0804, 0x0411 +MS_LANG_DUTCH, MS_LANG_KOREAN :: 0x0413, 0x0412 +MS_LANG_FRENCH, MS_LANG_RUSSIAN :: 0x040c, 0x0419 +MS_LANG_GERMAN, MS_LANG_SPANISH :: 0x0407, 0x0409 +MS_LANG_HEBREW, MS_LANG_SWEDISH :: 0x040d, 0x041D + + +// languageID for PLATFORM_ID_MAC +MAC_LANG_ENGLISH, MAC_LANG_JAPANESE :: 0, 11 +MAC_LANG_ARABIC, MAC_LANG_KOREAN :: 12, 23 +MAC_LANG_DUTCH, MAC_LANG_RUSSIAN :: 4, 32 +MAC_LANG_FRENCH, MAC_LANG_SPANISH :: 1, 6 +MAC_LANG_GERMAN, MAC_LANG_SWEDISH :: 2, 5 +MAC_LANG_HEBREW, MAC_LANG_CHINESE_SIMPLIFIED :: 10, 33 +MAC_LANG_ITALIAN, MAC_LANG_CHINESE_TRAD :: 3, 19 + +// private structure +_buf :: struct { + data: [^]byte, + cursor: c.int, + size: c.int, +} diff --git a/thirdparty/stb/truetype/stb_truetype_wasm.odin b/thirdparty/stb/truetype/stb_truetype_wasm.odin new file mode 100644 index 0000000..f362390 --- /dev/null +++ b/thirdparty/stb/truetype/stb_truetype_wasm.odin @@ -0,0 +1,4 @@ +#+build wasm32, wasm64p32 +package stb_truetype + +@(require) import _ "vendor:libc"