From d7c708e5d49ba1942f8e61d6051470f66ea6865c Mon Sep 17 00:00:00 2001 From: PS Date: Thu, 7 Apr 2022 14:02:12 +0200 Subject: [PATCH] text rendering --- .../intel/debug/lumenarium_first_win32.obj | Bin 246446 -> 439282 bytes src_v2/editor/lumenarium_editor.cpp | 39 +- src_v2/editor/lumenarium_editor_ui.cpp | 96 +- src_v2/editor/lumenarium_editor_ui.h | 2 + src_v2/libs/stb_truetype.h | 4853 +++++++++++++++++ src_v2/lumenarium_first.cpp | 2 +- src_v2/lumenarium_texture_atlas.cpp | 140 +- src_v2/lumenarium_types.h | 1 + src_v2/platform/lumenarium_assert.h | 9 +- src_v2/platform/lumenarium_platform.h | 10 +- .../lumenarium_platform_common_includes.h | 16 +- .../platform/wasm/lumenarium_wasm_webgl.cpp | 4 +- .../win32/lumenarium_win32_graphics.cpp | 4 +- 13 files changed, 5096 insertions(+), 80 deletions(-) create mode 100644 src_v2/libs/stb_truetype.h diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index 848a54baeada618f9cba1429eba09517b66f8569..a1f03174444ba1f278b233da4c3b35ca29e4a473 100644 GIT binary patch literal 439282 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 z

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 NC!TxeDe^7&{{tGsrMUnA delta 75537 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
Hhw%`N@{41#Fh!uVUw=}fei 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 diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index 9d32da7..96edf05 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -91,9 +91,44 @@ void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Tex } internal void -ed_load_font_cb(Platform_File_Async_Job_Args result) +ed_load_font_cb(Platform_File_Async_Job_Args result, u8* user_data) { - s32 x = 5; + App_State* state = (App_State*)user_data; + UI* ui = &state->editor->ui; + + u8* f = result.data.base; + stbtt_fontinfo font; + if (!stbtt_InitFont(&font, f, stbtt_GetFontOffsetForIndex(f, 0))) + { + invalid_code_path; + } + + r32 scale = stbtt_ScaleForPixelHeight(&font, 18.0f); + s32 ascent, descent, line_gap; + stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); + ui->font_ascent = (r32)ascent * scale; + ui->font_descent = (r32)descent * scale; + ui->font_line_gap = (r32)line_gap * scale; + if (ui->font_line_gap == 0) ui->font_line_gap = 5; + + String c = lit_str("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]{}\\|;:'\",<.>/?`~"); + for (u64 i = 0; i < c.len; i++) + { + s32 w, h, xoff, yoff; + u32 id = (u32)c.str[i]; + u8* bp = stbtt_GetCodepointBitmap(&font, 0, scale, (char)c.str[i], &w, &h, &xoff, &yoff); + s32 x0, y0, x1, y1; + stbtt_GetCodepointBitmapBoxSubpixel(&font, (char)c.str[i], scale, scale, 0, 0, &x0, &y0, &x1, &y1); + + v2 offset = v2{ 0, (r32)y0 }; + texture_atlas_register(&state->editor->ui.atlas, bp, (u32)w, (u32)h, id, offset, TextureAtlasRegistration_PixelFormat_Alpha); + stbtt_FreeBitmap(bp, 0); + } + + Texture_Atlas_Sprite m_sprite = texture_atlas_sprite_get(&state->editor->ui.atlas, (u32)'m'); + ui->font_space_width = (r32)(m_sprite.max_x - m_sprite.min_x); + + platform_texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024); } internal void diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp index 75ffc24..5e397cf 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -23,7 +23,6 @@ static String ui_shader_frag_win32 = lit_str( "uniform sampler2D texture;\n" "void main(void) {\n" " FragColor = texture(texture, uv) * color;\n" - " if (FragColor.w <= 0.01f) discard;\n" "}" ); @@ -73,8 +72,13 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) result.atlas = texture_atlas_create(1024, 1024, 512, permanent); result.atlas_texture = platform_texture_create(result.atlas.pixels, 1024, 1024, 1024); - u32 white_sprite[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - ui_sprite_register(&result, (u8*)white_sprite, 2, 2, WHITE_SPRITE_ID); + u32 white_sprite[] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + }; + ui_sprite_register(&result, (u8*)white_sprite, 4, 4, WHITE_SPRITE_ID); return result; } @@ -119,14 +123,17 @@ ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c) internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id) { - texture_atlas_register(&ui->atlas, pixels, w, h, id); + texture_atlas_register(&ui->atlas, pixels, w, h, id, v2{0,0}, TextureAtlasRegistration_PixelFormat_RGBA); platform_texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width); } internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color) { - v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, id); + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, id); + v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); + pmin.XY += sprite.draw_offset; + pmax.XY += sprite.draw_offset; ui_quad_push(ui, pmin, pmax, uv.xy, uv.zw, color); } @@ -136,6 +143,37 @@ ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id) ui_sprite_push(ui, pmin, pmax, id, v4{1,1,1,1}); } +struct UI_Char_Draw_Cmd +{ + v4 uv; + v3 pmin; + v3 pmax; + v3 baseline_after; +}; + +internal UI_Char_Draw_Cmd +ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) +{ + UI_Char_Draw_Cmd result = {}; + + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, codepoint); + result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); + + v3 dim = v3{ + (r32)(sprite.max_x - sprite.min_x), + (r32)(sprite.max_y - sprite.min_y), + 0, + }; + result.pmin = at; + result.pmin.XY += sprite.draw_offset; + result.pmin = v3_floor(result.pmin); + result.pmax = result.pmin + dim; + + result.baseline_after = v3{ result.pmax.x, at.y, at.z }; + + return result; +} + internal void ui_frame_prepare(UI* ui, v2 window_dim) { @@ -184,12 +222,12 @@ ui_draw(UI* ui) UI_Widget_Result r0 = ui_widget_push(ui, d0); UI_Widget_Desc d1 = d0; - d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick; - d1.style.color_bg = PINK_V4; + d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick | UIWidgetStyle_Text; + d1.style.color_bg = PINK_V4;//{ 0.1f, 0.1f, 0.1f, 1.0f }; // d1.style.color_fg = GREEN_V4; d1.p_min = v2{ 512, 32 }; d1.p_max = v2{ 640, 128 }; - d1.string = lit_str("Hello"); + d1.string = lit_str("Hello there my friend, what's going on?"); UI_Widget_Result r1 = ui_widget_push(ui, d1); bool clicked_r1 = has_flag(r1.flags, UIWidgetResult_MouseLeft_WentUp); if (clicked_r1) show = !show; @@ -214,7 +252,7 @@ ui_draw(UI* ui) u32 widget_count = ui->widgets.free_len; r32 range_min = -10; r32 range_max = -1; - r32 range_step = (range_max - range_min) / (r32)widget_count; + r32 range_step = (range_max - range_min) / (r32)(widget_count * 4); ui_widgets_to_geometry_recursive(ui, ui->widgets.root, -10, range_step); platform_geometry_buffer_update( @@ -402,14 +440,48 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) { - bg_min.z += z_step; - bg_max.z += z_step; + z_at += z_step; + bg_min.z = z_at; + bg_max.z = z_at; ui_sprite_push(ui, bg_min, bg_max, desc.style.sprite, color_bg); } if (has_flag(child->desc.style.flags, UIWidgetStyle_Text)) { - // TODO(PS): + z_at += z_step + z_step; + r32 space_width = ui->font_space_width; + r32 to_baseline = ui->font_line_gap + ui->font_ascent; + v3 line_offset = { 5, 3 + to_baseline, 0 }; + r32 baseline_x_start = desc.p_min.x + line_offset.x; + r32 baseline_y_start = desc.p_min.y + line_offset.y; + v3 baseline = { baseline_x_start, baseline_y_start, z_at }; + for (u64 i = 0; i < child->desc.string.len; i++) + { + u8 at = child->desc.string.str[i]; + UI_Char_Draw_Cmd cmd = {}; + if (!char_is_space(at)) + { + cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); + } + else + { + cmd.baseline_after = baseline; + cmd.baseline_after.x += space_width; + } + + if (cmd.baseline_after.x >= desc.p_max.x - 5) + { + baseline.x = baseline_x_start; + baseline.y += ui->font_ascent + ui->font_descent + ui->font_line_gap; + cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); + } + + if (!char_is_space(at)) + { + ui_quad_push(ui, cmd.pmin, cmd.pmax, cmd.uv.xy, cmd.uv.zw, color_fg); + } + baseline = cmd.baseline_after; + } } if (child->child_first) diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index d2254c2..23eadc8 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -130,6 +130,7 @@ struct UI u32 indices_cap; Texture_Atlas atlas; + r32 font_ascent, font_descent, font_line_gap, font_space_width; UI_Widget_Pool widgets; UI_Style_Sheet* style_sheet; @@ -156,6 +157,7 @@ internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c); internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id); +internal v3 ui_sprite_char_push(UI* ui, v2 at, u32 codepoint, v4 color); internal void ui_draw(UI* ui); // Widgets diff --git a/src_v2/libs/stb_truetype.h b/src_v2/libs/stb_truetype.h new file mode 100644 index 0000000..3ffe4de --- /dev/null +++ b/src_v2/libs/stb_truetype.h @@ -0,0 +1,4853 @@ +// stb_truetype.h - v1.19 - public domain +// authored from 2009-2016 by Sean Barrett / RAD Game Tools +// +// 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 +// Cass Everitt Martins Mozeiko +// stoiko (Haemimont Games) Cap Petschulat +// Brian Hook Omar Cornut +// Walter van Niftrik github:aloucks +// 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. github:oyvindjam +// Brian Costabile github:vassvik +// +// VERSION HISTORY +// +// 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 neeed 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.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); 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. + + + + ////////////////////////////////////////////////////////////////////////////// + // + // 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_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; + 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 publically 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; // 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. + + + ////////////////////////////////////////////////////////////////////////////// + // + // 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 + + + ////////////////////////////////////////////////////////////////////////////// + // + // 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 countours. 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 + + ////////////////////////////////////////////////////////////////////////////// + // + // 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 threshhold 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); +} + +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; + + // 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; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + 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 == -1) { + // 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) 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 if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } 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 || b0 > 254)) + 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)); + } +} + +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: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + 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)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } 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); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +// 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; + + 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> 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; + } + } + } break; + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + + 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_assert(glyph1class < class1Count); + STBTT_assert(glyph2class < class2Count); + + // TODO: Support more formats. + STBTT_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + STBTT_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + break; + }; + } + } + break; + }; + + default: + // TODO: Implement other stuff. + break; + } + } + + 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); + + 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); +} + +////////////////////////////////////////////////////////////////////////////// +// +// 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 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; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, 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; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + 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 + 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) { + STBTT_assert(z->ey >= scan_y_top); + // 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) +{ + /* threshhold 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; +} + +// tesselate until threshhold 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; + + 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; +} + +#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; + + 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); + 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); + ++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, 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) { + 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; + } 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_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; + + orig[0] = x; + orig[1] = y; + + // 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[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 + c*x^2 + b*x + a = 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 one scale is 0, use same scale for both + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; // if both scales are 0, return NULL + scale_y = scale_x; + } + + 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; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // 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) + float 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],px,py,t,it; + 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); + } + 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.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. +------------------------------------------------------------------------------ +*/ diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index e4f4e0d..6804a52 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -44,7 +44,7 @@ lumenarium_frame_prepare(App_State* state) } incenter_frame_prepare(state); - platform_file_async_jobs_do_work(4); + platform_file_async_jobs_do_work(4, (u8*)state); } internal void diff --git a/src_v2/lumenarium_texture_atlas.cpp b/src_v2/lumenarium_texture_atlas.cpp index e56888f..c60b7fe 100644 --- a/src_v2/lumenarium_texture_atlas.cpp +++ b/src_v2/lumenarium_texture_atlas.cpp @@ -9,17 +9,19 @@ struct Texture_Atlas_Sprite u16 min_y; u16 max_x; u16 max_y; + + v2 draw_offset; }; struct Texture_Atlas { u8* pixels; - u16 width; - u16 height; + u32 width; + u32 height; - u16 next_x; - u16 next_y; - u16 y_used; + u32 next_x; + u32 next_y; + u32 y_used; u32* ids; Texture_Atlas_Sprite* sprites; @@ -36,7 +38,7 @@ texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) result.height = (u16)height; for (u32 i = 0; i < width * height; i++) { u8* base = result.pixels + (i * 4); - *(u32*)base = 0xFFFFFFFF; + *(u32*)base = 0x00FFFFFF; } result.ids = allocator_alloc_array(allocator, u32, cap); @@ -46,52 +48,109 @@ texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) return result; } -internal void -texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id) +enum Texture_Atlas_Registration_Flags { - u16 min_x = ta->next_x; - u16 min_y = ta->next_y; - u16 max_x = min_x + (u16)width; - u16 max_y = min_y + (u16)height; + TextureAtlasRegistration_None = 0, + TextureAtlasRegistration_PixelFormat_RGBA = 1, + TextureAtlasRegistration_PixelFormat_Alpha = 2, +}; + +internal void +texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id, v2 draw_offset, u32 flags) +{ + if (ta->next_x > ta->width || (ta->next_x + width + 2) > ta->width) + { + ta->next_x = 0; + ta->next_y = ta->y_used; + } - // TODO(PS): if the sprite won't fit in this row, then we need to shift it to - // the next one + u32 min_x = ta->next_x + 1; + u32 min_y = ta->next_y + 1; + u32 max_x = min_x + width; + u32 max_y = min_y + height; // copy the data - for (u16 y = 0; y < height; y++) + if (has_flag(flags, TextureAtlasRegistration_PixelFormat_RGBA)) { - u16 src_row = (y * (u16)width) * 4; - u16 dst_row = (((y + min_y) * ta->width) + min_x) * 4; - for (u16 x = 0; x < width; x++) + for (u32 y = 0; y < height; y++) { - ta->pixels[dst_row++] = pixels[src_row++]; - ta->pixels[dst_row++] = pixels[src_row++]; - ta->pixels[dst_row++] = pixels[src_row++]; - ta->pixels[dst_row++] = pixels[src_row++]; + u32 src_row = (y * width) * 4; + u32 dst_row = (((y + min_y) * ta->width) + min_x) * 4; + for (u32 x = 0; x < width; x++) + { + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + } } } + else if (has_flag(flags, TextureAtlasRegistration_PixelFormat_Alpha)) + { + for (u32 y = 0; y < height; y++) + { + u32 src_row = y * width; + u32 dst_row = (((y + min_y) * ta->width) + min_x) * 4; + for (u32 x = 0; x < width; x++) + { + ta->pixels[dst_row++] = 0xFF; + ta->pixels[dst_row++] = 0xFF; + ta->pixels[dst_row++] = 0xFF; + ta->pixels[dst_row++] = pixels[src_row++]; + } + } + } + + // copy nearest pixels to the border + u32 pi_width = ta->width; + u32 pi_stride = 4; +#define PIXEL_INDEX(x,y) ((((y) * pi_width) + (x)) * pi_stride) +#define COPY_PIXEL(db,sb) \ +ta->pixels[(db) + 0] = ta->pixels[(sb) + 0]; \ +ta->pixels[(db) + 1] = ta->pixels[(sb) + 1]; \ +ta->pixels[(db) + 2] = ta->pixels[(sb) + 2]; \ +ta->pixels[(db) + 3] = ta->pixels[(sb) + 3]; + + for (u32 x = 0; x < width; x++) + { + u32 top = PIXEL_INDEX(min_x + x, min_y - 1); + u32 top_near = PIXEL_INDEX(min_x + x, min_y); + u32 bot = PIXEL_INDEX(min_x + x, max_y); + u32 bot_near = PIXEL_INDEX(min_x + x, max_y - 1); + COPY_PIXEL(top, top_near); + COPY_PIXEL(bot, bot_near); + } + + for (u32 y = 0; y < height + 2; y++) + { + u32 left = PIXEL_INDEX(min_x - 1, min_y + y - 1); + u32 left_near = PIXEL_INDEX(min_x, min_y + y - 1); + u32 right = PIXEL_INDEX(max_x, min_y + y - 1); + u32 right_near = PIXEL_INDEX(max_x - 1, min_y + y - 1); + COPY_PIXEL(left, left_near); + COPY_PIXEL(right, right_near); + } + +#undef PIXEL_INDEX +#undef COPY_PIXEL // register a new slot u32 index = hash_table_register(ta->ids, ta->cap, id); Texture_Atlas_Sprite* sprite = ta->sprites + index; - sprite->min_x = min_x; - sprite->min_y = min_y; - sprite->max_x = max_x; - sprite->max_y = max_y; + sprite->min_x = (u16)min_x; + sprite->min_y = (u16)min_y; + sprite->max_x = (u16)max_x; + sprite->max_y = (u16)max_y; + sprite->draw_offset = draw_offset; // Prepare for next registration if (max_y > ta->y_used) { - ta->y_used = max_y; + ta->y_used = max_y + 2; } - ta->next_x = max_x + 1; - if (ta->next_x > ta->width) - { - ta->next_x = 0; - ta->next_y = ta->y_used; - } + ta->next_x = max_x + 2; } internal Texture_Atlas_Sprite @@ -105,9 +164,8 @@ texture_atlas_sprite_get(Texture_Atlas* ta, u32 id) } internal v4 -texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +texture_atlas_sprite_get_uvs(Texture_Atlas* ta, Texture_Atlas_Sprite sprite) { - Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); v4 result = {}; // uv min @@ -118,11 +176,13 @@ texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) result.z = (r32)sprite.max_x / (r32)ta->width; result.w = (r32)sprite.max_y / (r32)ta->height; - // inset - v2 half_texel = v2{1.0f / ta->width, 1.0f / ta->height}; - result.xy += half_texel; - result.zw -= half_texel; - return result; } + +internal v4 +texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +{ + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); + return texture_atlas_sprite_get_uvs(ta, sprite); +} #endif //LUMENARIUM_TEXTURE_ATLAS_CPP diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index d26977a..bf829e9 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -244,6 +244,7 @@ hash_table_find(u32* ids, u32 cap, u32 value) // Vector Extensions #define v2_to_v3(xy,z) v3{(xy).x, (xy).y, (z)} +#define v3_floor(v) v3{ floorf(v.x), floorf(v.y), floorf(v.z) } ////////////////////////////////////////////// // Color Constants diff --git a/src_v2/platform/lumenarium_assert.h b/src_v2/platform/lumenarium_assert.h index be84e78..9210788 100644 --- a/src_v2/platform/lumenarium_assert.h +++ b/src_v2/platform/lumenarium_assert.h @@ -26,7 +26,7 @@ void close_err_file() {} // this assert works by simply trying to write to an invalid address // (in this case, 0x0), which will crash in most debuggers -# define assert_always (*((volatile s32*)0) = 0xFFFF) +# define assert_always (*((volatile int*)0) = 0xFFFF) #else WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned int line); @@ -34,12 +34,13 @@ WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned #endif // defined(PLATFORM_WASM) #ifdef USE_ASSERTS -# define assert(c) \ +# define assert(c) do { \ if (!(c)) { \ -err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); \ +err_write("Assert Hit: %s:%u\n", __FILE__, (u32)__LINE__); \ close_err_file(); \ assert_always; \ -} +} \ +} while(false) // useful for catching cases that you aren't sure you'll hit, but // want to be alerted when they happen diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index 0f50ddf..4f06e9c 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -101,7 +101,7 @@ struct Platform_File_Async_Job_Args u32 error; }; -typedef void Platform_File_Async_Cb(Platform_File_Async_Job_Args args); +typedef void Platform_File_Async_Cb(Platform_File_Async_Job_Args args, u8* user_data); struct Platform_File_Async_Job { @@ -187,9 +187,9 @@ platform_file_async_write(String path, Data data, Platform_File_Async_Cb* cb) } void -platform_file_async_job_complete(Platform_File_Async_Job* job) +platform_file_async_job_complete(Platform_File_Async_Job* job, u8* user_data) { - job->cb(job->args); + job->cb(job->args, user_data); allocator_free(platform_file_jobs_arena, job->job_memory.base, job->job_memory.size); if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) { @@ -200,7 +200,7 @@ platform_file_async_job_complete(Platform_File_Async_Job* job) void platform_file_async_work_on_job(Platform_File_Async_Job* job); void -platform_file_async_jobs_do_work(u64 max_jobs) +platform_file_async_jobs_do_work(u64 max_jobs, u8* user_data) { u64 to_do = max_jobs; if (max_jobs > platform_file_async_jobs_len) to_do = platform_file_async_jobs_len; @@ -216,7 +216,7 @@ platform_file_async_jobs_do_work(u64 max_jobs) platform_file_async_work_on_job(job); if (has_flag(job->args.flags, completed)) { - platform_file_async_job_complete(job); + platform_file_async_job_complete(job, user_data); platform_file_async_job_rem(i); } } diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h index 802889c..1b6799a 100644 --- a/src_v2/platform/lumenarium_platform_common_includes.h +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -13,6 +13,10 @@ #include "lumenarium_assert.h" +#define STB_TRUETYPE_IMPLEMENTATION +#define STBTT_assert(x) assert(x) +#include "../libs/stb_truetype.h" + // NOTE(PS): only need the opengl extension headers // when running on a platform that is using opengl 3.3+ #if !defined(PLATFORM_wasm) @@ -21,18 +25,6 @@ # include "wglext.h" #endif -#if 0 -#define HMM_SINF sin -#define HMM_COSF cos -#define HMM_TANF tan -#define HMM_SQRTF sqrt -#define HMM_EXPF exp -#define HMM_LOGF log -#define HMM_ACOSF acos -#define HMM_ATANF atan -#define HMM_ATAN2F atan2 -#endif - #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_CPP_MODE #define HANDMADE_MATH_STATIC diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index 0d04110..55a8efd 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -206,8 +206,8 @@ platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride) glGenTextures(1, &result.id, sizeof(u32)); glBindTexture(GL_TEXTURE_2D, result.id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index cc9bd19..cb02e6b 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -255,9 +255,9 @@ platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride) glBindTexture(GL_TEXTURE_2D, result.id); win32_gl_no_error(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); win32_gl_no_error();