commit 9dc9cde0a54aad9af7cffbbad65fd02a44744e63
parent ad9178d234b881315ca3142184d54816c09471f4
Author: Michael Savage <mikejsavage@gmail.com>
Date: Fri, 2 Aug 2019 14:34:50 +0300
Add msdf
Diffstat:
8 files changed, 1137 insertions(+), 80 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -15,6 +15,7 @@ bsp
sm
gltf
fullscreen
+msdf
sound
srv
test_lockfree
diff --git a/libs/stb/stb_truetype.h b/libs/stb/stb_truetype.h
@@ -1,4 +1,4 @@
-// stb_truetype.h - v1.15 - public domain
+// stb_truetype.h - v1.21 - public domain
// authored from 2009-2016 by Sean Barrett / RAD Game Tools
//
// This library processes TrueType files:
@@ -6,6 +6,7 @@
// 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
@@ -21,39 +22,39 @@
// 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 (with fix)
-// Cass Everitt
-// stoiko (Haemimont Games)
-// Brian Hook
-// Walter van Niftrik
-// David Gow
-// David Given
-// Ivan-Assen Ivanov
-// Anthony Pesch
-// Johan Duparc
-// Hou Qiming
-// Fabian "ryg" Giesen
-// Martins Mozeiko
-// Cap Petschulat
-// Omar Cornut
-// github:aloucks
-// Peter LaValle
-// Sergey Popov
-// Giumo X. Clanjor
-// Higor Euripedes
-// Thomas Fields
-// Derek Vinyard
-// Cort Stratton
-//
+// "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.21 (2019-02-25) fix warning
+// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
+// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
+// 1.18 (2018-01-29) add missing function
+// 1.17 (2017-07-23) make more arguments const; doc fix
+// 1.16 (2017-07-12) SDF support
// 1.15 (2017-03-03) make more arguments const
// 1.14 (2017-01-16) num-fonts-in-TTC function
// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
@@ -76,7 +77,7 @@
//
// USAGE
//
-// Include this file in whatever places neeed to refer to it. In ONE C/C++
+// Include this file in whatever places need to refer to it. In ONE C/C++
// file, write:
// #define STB_TRUETYPE_IMPLEMENTATION
// before the #include of this file. This expands out the actual
@@ -92,7 +93,7 @@
// Improved 3D API (more shippable):
// #include "stb_rect_pack.h" -- optional, but you really want it
// stbtt_PackBegin()
-// stbtt_PackSetOversample() -- for improved quality on small fonts
+// stbtt_PackSetOversampling() -- for improved quality on small fonts
// stbtt_PackFontRanges() -- pack and renders
// stbtt_PackEnd()
// stbtt_GetPackedQuad()
@@ -110,6 +111,7 @@
// Character advance/positioning
// stbtt_GetCodepointHMetrics()
// stbtt_GetFontVMetrics()
+// stbtt_GetFontVMetricsOS2()
// stbtt_GetCodepointKernAdvance()
//
// Starting with version 1.06, the rasterizer was replaced with a new,
@@ -165,7 +167,7 @@
// 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 they different monitors have different number of pixels per
+// 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
@@ -175,6 +177,39 @@
// 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 <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
+// then the character should be displayed in the rectangle from
+// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
+//
+// Advancing for the next character:
+// Call GlyphHMetrics, and compute 'current_point += SF * advance'.
+//
+//
// ADVANCED USAGE
//
// Quality:
@@ -209,19 +244,6 @@
// recommend it.
//
//
-// SOURCE STATISTICS (based on v0.6c, 2050 LOC)
-//
-// Documentation & header file 520 LOC \___ 660 LOC documentation
-// Sample code 140 LOC /
-// Truetype parsing 620 LOC ---- 620 LOC TrueType
-// Software rasterization 240 LOC \ .
-// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation
-// Bitmap management 100 LOC /
-// Baked bitmap interface 70 LOC /
-// Font name matching & access 150 LOC ---- 150
-// C runtime library abstraction 60 LOC ---- 60
-//
-//
// PERFORMANCE MEASUREMENTS FOR 1.06:
//
// 32-bit 64-bit
@@ -381,9 +403,8 @@ int main(int arg, char **argv)
//// INTEGRATION WITH YOUR CODEBASE
////
//// The following sections allow you to supply alternate definitions
-//// of C library functions used by stb_truetype.
-
-#include "stb_rect_pack.h"
+//// 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
@@ -399,7 +420,7 @@ int main(int arg, char **argv)
typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
+ // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
#ifndef STBTT_ifloor
#include <math.h>
#define STBTT_ifloor(x) ((int) floor(x))
@@ -409,6 +430,18 @@ int main(int arg, char **argv)
#ifndef STBTT_sqrt
#include <math.h>
#define STBTT_sqrt(x) sqrt(x)
+ #define STBTT_pow(x,y) pow(x,y)
+ #endif
+
+ #ifndef STBTT_fmod
+ #include <math.h>
+ #define STBTT_fmod(x,y) fmod(x,y)
+ #endif
+
+ #ifndef STBTT_cos
+ #include <math.h>
+ #define STBTT_cos(x) cos(x)
+ #define STBTT_acos(x) acos(x)
#endif
#ifndef STBTT_fabs
@@ -434,7 +467,7 @@ int main(int arg, char **argv)
#endif
#ifndef STBTT_memcpy
- #include <memory.h>
+ #include <string.h>
#define STBTT_memcpy memcpy
#define STBTT_memset memset
#endif
@@ -512,6 +545,8 @@ STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int p
//
// It's inefficient; you might want to c&p it and optimize it.
+STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);
+// Query the font vertical metrics without having to create a font first.
//////////////////////////////////////////////////////////////////////////////
@@ -550,7 +585,7 @@ STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
#define STBTT_POINT_SIZE(x) (-(x))
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+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
@@ -575,7 +610,7 @@ typedef struct
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, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
+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
@@ -597,6 +632,12 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
// To use with PackFontRangesGather etc., you must set it before calls
// call to PackFontRangesGatherRects.
+STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
+// If skip != 0, this tells stb_truetype to skip any codepoints for which
+// there is no corresponding glyph. If skip=0, which is the default, then
+// codepoints without a glyph recived the font's "missing character" glyph,
+// typically an empty box by convention.
+
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
int char_index, // character to display
float *xpos, float *ypos, // pointers to current position in screen pixel space
@@ -625,6 +666,7 @@ struct stbtt_pack_context {
int height;
int stride_in_bytes;
int padding;
+ int skip_missing;
unsigned int h_oversample, v_oversample;
unsigned char *pixels;
void *nodes;
@@ -650,7 +692,7 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
// 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 following structure is defined publicly so you can declare one on
// the stack or as a global or etc, but you should treat it as opaque.
struct stbtt_fontinfo
{
@@ -660,7 +702,7 @@ struct stbtt_fontinfo
int numGlyphs; // number of glyphs, needed for range checking
- int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
+ 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
@@ -689,6 +731,7 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep
// and you want a speed-up, call this function with the character you're
// going to process, then use glyph-based functions instead of the
// codepoint-based functions.
+// Returns 0 if the character codepoint is not defined in the font.
//////////////////////////////////////////////////////////////////////////////
@@ -717,6 +760,12 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in
// 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
@@ -770,7 +819,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
// 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
+// The shape is a series of contours. Each one starts with
// a STBTT_moveto, then consists of a series of mixed
// STBTT_lineto and STBTT_curveto segments. A lineto
// draws a line from previous endpoint to its x,y; a curveto
@@ -811,6 +860,10 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns
// 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
@@ -828,6 +881,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float
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);
@@ -852,6 +906,64 @@ STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap
//////////////////////////////////////////////////////////////////////////////
//
+// Signed Distance Function (or Field) rendering
+
+STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);
+// frees the SDF bitmap allocated below
+
+STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
+STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
+// These functions compute a discretized SDF field for a single character, suitable for storing
+// in a single-channel texture, sampling with bilinear filtering, and testing against
+// larger than some threshold to produce scalable fonts.
+// info -- the font
+// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
+// glyph/codepoint -- the character to generate the SDF for
+// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0),
+// which allows effects like bit outlines
+// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
+// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
+// if positive, > onedge_value is inside; if negative, < onedge_value is inside
+// width,height -- output height & width of the SDF bitmap (including padding)
+// xoff,yoff -- output origin of the character
+// return value -- a 2D array of bytes 0..255, width*height in size
+//
+// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
+// optimal use of the limited 0..255 for your application, trading off precision
+// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
+//
+// Example:
+// scale = stbtt_ScaleForPixelHeight(22)
+// padding = 5
+// onedge_value = 180
+// pixel_dist_scale = 180/5.0 = 36.0
+//
+// This will create an SDF bitmap in which the character is about 22 pixels
+// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
+// shape, sample the SDF at each pixel and fill the pixel if the SDF value
+// is greater than or equal to 180/255. (You'll actually want to antialias,
+// which is beyond the scope of this example.) Additionally, you can compute
+// offset outlines (e.g. to stroke the character border inside & outside,
+// or only outside). For example, to fill outside the character up to 3 SDF
+// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
+// choice of variables maps a range from 5 pixels outside the shape to
+// 2 pixels inside the shape to 0..255; this is intended primarily for apply
+// outside effects only (the interior range is needed to allow proper
+// antialiasing of the font at *smaller* sizes)
+//
+// The function computes the SDF analytically at each SDF pixel, not by e.g.
+// building a higher-res bitmap and approximating it. In theory the quality
+// should be as high as possible for an SDF of this size & representation, but
+// unclear if this is true in practice (perhaps building a higher-res bitmap
+// and computing from that can allow drop-out prevention).
+//
+// The algorithm has not been optimized at all, so expect it to be slow
+// if computing lots of characters or very large sizes.
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
// Finding the right font...
//
// You should really just solve this offline, keep your own tables
@@ -1234,6 +1346,7 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
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;
@@ -2087,7 +2200,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
// push immediate
if (b0 == 255) {
- f = (float)stbtt__buf_get32(&b) / 0x10000;
+ f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
} else {
stbtt__buf_skip(&b, -1);
f = (float)(stbtt_int16)stbtt__cff_int(&b);
@@ -2125,12 +2238,10 @@ static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, in
{
stbtt__csctx c = STBTT__CSCTX_INIT(1);
int r = stbtt__run_charstring(info, glyph_index, &c);
- if (x0) {
- *x0 = r ? c.min_x : 0;
- *y0 = r ? c.min_y : 0;
- *x1 = r ? c.max_x : 0;
- *y1 = r ? c.max_y : 0;
- }
+ 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;
}
@@ -2154,7 +2265,7 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde
}
}
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@@ -2184,9 +2295,261 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1,
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<lookupCount; ++i) {
+ stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
+ stbtt_uint8 *lookupTable = lookupList + lookupOffset;
+
+ stbtt_uint16 lookupType = ttUSHORT(lookupTable);
+ stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
+ stbtt_uint8 *subTableOffsets = lookupTable + 6;
+ switch(lookupType) {
+ case 2: { // Pair Adjustment Positioning Subtable
+ stbtt_int32 sti;
+ for (sti=0; sti<subTableCount; sti++) {
+ stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
+ stbtt_uint8 *table = lookupTable + subtableOffset;
+ stbtt_uint16 posFormat = ttUSHORT(table);
+ stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
+ stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
+ if (coverageIndex == -1) continue;
+
+ switch (posFormat) {
+ case 1: {
+ stbtt_int32 l, r, m;
+ int straw, needle;
+ stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
+ stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
+ stbtt_int32 valueRecordPairSizeInBytes = 2;
+ stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
+ stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
+ stbtt_uint8 *pairValueTable = table + pairPosOffset;
+ stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
+ stbtt_uint8 *pairValueArray = pairValueTable + 2;
+ // 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;
+
+ STBTT_assert(coverageIndex < pairSetCount);
+ STBTT__NOTUSED(pairSetCount);
+
+ needle=glyph2;
+ r=pairValueCount-1;
+ l=0;
+
+ // Binary search.
+ while (l <= r) {
+ stbtt_uint16 secondGlyph;
+ stbtt_uint8 *pairValue;
+ m = (l + r) >> 1;
+ pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
+ secondGlyph = ttUSHORT(pairValue);
+ straw = secondGlyph;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
+ return xAdvance;
+ }
+ }
+ } 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) // if no kerning table, don't waste time looking up both codepoint->glyphs
+ 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));
}
@@ -2203,6 +2566,17 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in
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);
@@ -2299,7 +2673,7 @@ static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
hh->num_remaining_in_head_chunk = count;
}
--hh->num_remaining_in_head_chunk;
- return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;
+ return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
}
}
@@ -2695,19 +3069,18 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// 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 clear pairs
+ // 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;
- float y1,y2;
// x = e->x + e->dx * (y-y_top)
// (y-y_top) = (x - e->x) / e->dx
// y = (x - e->x) / e->dx + y_top
- y1 = (x - x0) / dx + y_top;
- y2 = (x+1 - x0) / 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);
@@ -2787,7 +3160,13 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
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);
+ if (j == 0 && off_y != 0) {
+ if (z->ey < scan_y_top) {
+ // this can happen due to subpixel positioning and some kind of fp rounding error i think
+ z->ey = scan_y_top;
+ }
+ }
+ STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds
// insert at front
z->next = active;
active = z;
@@ -2856,7 +3235,7 @@ static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
{
- /* threshhold for transitioning to insertion sort */
+ /* threshold for transitioning to insertion sort */
while (n > 12) {
stbtt__edge t;
int c01,c12,c,m,i,j;
@@ -2991,7 +3370,7 @@ static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
points[n].y = y;
}
-// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
+// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
{
// midpoint
@@ -3134,8 +3513,9 @@ error:
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, *winding_lengths;
+ 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);
@@ -3223,6 +3603,11 @@ STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *
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));
@@ -3410,6 +3795,7 @@ STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, in
spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
spc->h_oversample = 1;
spc->v_oversample = 1;
+ spc->skip_missing = 0;
stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
@@ -3435,6 +3821,11 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
spc->v_oversample = v_oversample;
}
+STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)
+{
+ spc->skip_missing = skip;
+}
+
#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
@@ -3588,13 +3979,17 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb
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);
+ if (glyph == 0 && spc->skip_missing) {
+ rects[k].w = rects[k].h = 0;
+ } else {
+ stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ 0,0,
+ &x0,&y0,&x1,&y1);
+ rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
+ rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+ }
++k;
}
}
@@ -3602,6 +3997,29 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb
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)
{
@@ -3624,7 +4042,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const
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) {
+ if (r->was_packed && r->w != 0 && r->h != 0) {
stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
int advance, lsb, x0,y0,x1,y1;
int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
@@ -3690,7 +4108,7 @@ STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect
stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
}
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
+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;
@@ -3726,7 +4144,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd
return return_value;
}
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+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;
@@ -3738,6 +4156,19 @@ STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontda
return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
}
+STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)
+{
+ int i_ascent, i_descent, i_lineGap;
+ float scale;
+ stbtt_fontinfo info;
+ stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));
+ scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);
+ stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);
+ *ascent = (float) i_ascent * scale;
+ *descent = (float) i_descent * scale;
+ *lineGap = (float) i_lineGap * scale;
+}
+
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
{
float ipw = 1.0f / pw, iph = 1.0f / ph;
@@ -3765,6 +4196,387 @@ STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int
*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);
+}
//////////////////////////////////////////////////////////////////////////////
//
@@ -3972,6 +4784,13 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const
// 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
diff --git a/linear_algebra.h b/linear_algebra.h
@@ -326,6 +326,10 @@ forceinline v2 operator+( v2 lhs, v2 rhs ) {
return v2( lhs.x + rhs.x, lhs.y + rhs.y );
}
+forceinline v2 operator-( v2 v, float x ) {
+ return v2( v.x - x, v.y - x );
+}
+
forceinline v2 operator-( v2 lhs, v2 rhs ) {
return v2( lhs.x - rhs.x, lhs.y - rhs.y );
}
diff --git a/make.lua b/make.lua
@@ -53,6 +53,7 @@ require( "libs/squish" )
-- bin( "bsp", { "main", "bsp", "bsp_renderer", game_objs }, { game_libs } )
-- bin( "sm", { "main", "shadow_map", game_objs }, { game_libs } )
bin( "gltf", { "main", "gltf", game_objs }, { "cgltf", game_libs } )
+bin( "msdf", { "main", "msdf", game_objs }, game_libs )
bin( "fullscreen", { "main", "fullscreen", game_objs }, { game_libs } )
msvc_bin_ldflags( "bsp", "opengl32.lib gdi32.lib" )
@@ -61,6 +62,8 @@ msvc_bin_ldflags( "sm", "opengl32.lib gdi32.lib" )
gcc_bin_ldflags( "sm", game_ldflags )
msvc_bin_ldflags( "gltf", "opengl32.lib gdi32.lib" )
gcc_bin_ldflags( "gltf", game_ldflags )
+msvc_bin_ldflags( "msdf", "opengl32.lib gdi32.lib" )
+gcc_bin_ldflags( "msdf", game_ldflags )
msvc_bin_ldflags( "fullscreen", "opengl32.lib gdi32.lib" )
gcc_bin_ldflags( "fullscreen", game_ldflags )
diff --git a/msdf.cc b/msdf.cc
@@ -0,0 +1,141 @@
+#include "game.h"
+#include "intrinsics.h"
+#include "linear_algebra.h"
+#include "gl.h"
+#include "renderer.h"
+#include "shaders.h"
+#include "immediate.h"
+#include "log.h"
+#include "obj.h"
+
+#include "bitmap_font_desc.c"
+
+#include "liberation.h"
+#include "libs/stb/stb_truetype.h"
+
+static Texture atlas;
+static stbtt_fontinfo font_info;
+
+GAME_INIT( game_init ) {
+ atlas = load_png( "msdf.png", TEXFMT_RGB_U8, 3 );
+
+ u8 * ttf = file_get_contents( "Montserrat-Bold.ttf" );
+ int offset = stbtt_GetFontOffsetForIndex( ttf, 0 );
+ int ok = stbtt_InitFont( &font_info, ttf, offset );
+ ASSERT( ok != 0 );
+}
+
+v3 to_clip( v2 v ) {
+ v2u32 ws = get_window_size();
+ v2 clip = v2( v.x / ws.x, v.y / ws.y ) * 2.0f - 1.0f;
+ return v3( clip.x, clip.y, 0 );
+}
+
+static v2 glyph_bounds( u8 glyph, float scale ) {
+ return v2();
+}
+
+static void msdf_text( const char * str, int x, int y, float scale ) {
+ const v4 white( 1, 1, 1, 1 );
+
+ float height = font_information.max_y - font_information.min_y;
+ scale /= height;
+
+ float pad = font_information.glyph_padding;
+
+ v2u32 ws = get_window_size();
+ float left = float( x );
+ float top = ws.y - y - font_information.max_y * scale;
+
+ while( *str != '\0' ) {
+ // todo: way too much padding, should be 2 or 3px to allow for antialiasing/border
+ const bitmap_glyph & glyph = font_codepoint_infos[ u8( *str ) ];
+ // printf( "%c: %f\n", *str, glyph.advance );
+
+ ImmediateVertex tl = {
+ to_clip( v2( left + scale * ( glyph.minx - pad ), top + scale * ( glyph.miny - pad ) ) ),
+ v3( 0 ),
+ white,
+ v2( ( glyph.atlas_x + 0.5f ) / 1024.0f, 1.0f - ( glyph.atlas_y + 0.5f ) / 512.0f ),
+ };
+ ImmediateVertex tr = {
+ to_clip( v2( left + scale * ( glyph.maxx + pad ), top + scale * ( glyph.miny - pad ) ) ),
+ v3( 0 ),
+ white,
+ v2( ( glyph.atlas_x + glyph.atlas_w + 0.5f ) / 1024.0f, 1.0f - ( glyph.atlas_y + 0.5f ) / 512.0f ),
+ };
+ ImmediateVertex bl = {
+ to_clip( v2( left + scale * ( glyph.minx - pad ), top + scale * ( glyph.maxy + pad ) ) ),
+ v3( 0 ),
+ white,
+ v2( ( glyph.atlas_x + 0.5f ) / 1024.0f, 1.0f - ( glyph.atlas_y + glyph.atlas_h + 0.5f ) / 512.0f ),
+ };
+ ImmediateVertex br = {
+ to_clip( v2( left + scale * ( glyph.maxx + pad ), top + scale * ( glyph.maxy + pad ) ) ),
+ v3( 0 ),
+ white,
+ v2( ( glyph.atlas_x + glyph.atlas_w + 0.5f ) / 1024.0f, 1.0f - ( glyph.atlas_y + glyph.atlas_h + 0.5f ) / 512.0f ),
+ };
+
+ immediate_triangle( tr, bl, tl );
+ immediate_triangle( bl, tr, br );
+
+ left += glyph.advance * scale;
+ if( str[ 1 ] != '\0' ) {
+ int kern = stbtt_GetCodepointKernAdvance( &font_info, str[ 0 ], str[ 1 ] );
+ // left += kern / 24.0f;
+ }
+
+ str++;
+ }
+
+ RenderState render_state;
+ render_state.shader = get_shader( SHADER_MSDF );
+ render_state.textures[ 0 ] = atlas;
+ render_state.depth_func = DEPTHFUNC_DISABLED;
+ render_state.enable_alpha_blending = true;
+ render_state.disable_depth_writes = true;
+
+ immediate_render( render_state );
+}
+
+float lol = 0.5;
+
+GAME_FRAME( game_frame ) {
+ RenderPassConfig pass;
+ pass.clear_colour = true;
+ pass.colour = v4( 0.1, 0.1, 0.1, 0 );
+
+ if( input->keys[ KEY_MINUS ] )
+ lol -= 0.1 * dt;
+ if( input->keys[ KEY_EQUALS ] )
+ lol += 0.1 * dt;
+
+ renderer_begin_frame();
+ renderer_begin_pass( pass );
+
+ {
+ // for( int i = 0; i < 8; i++ ) {
+ // msdf_text( "DEFEND COCAINE DIESEL AVA Ty fr fi \xc4\xe4\xc5\xe5\xdf", 2, i * 30, i / 2.45f );
+ // }
+ //
+ // float scale = sinf( current_time ) + 2.0;
+ // msdf_text( "COCAINE DIESEL", 2, -50, scale );
+ //
+ // msdf_text( "Couldn't execute: autoexec_postinit.cfg", 2, -250 / lol, lol );
+
+ int y = 0;
+ for( int i = 0; i < 6; i++ ) {
+ y += 2;
+ // msdf_text( "\xc5rt of Respect", 2, y, 16 << i );
+ float x = 2.0f + 0.015f * ( 16 << i ) * sinf( current_time * 40 + i );
+ // msdf_text( "\xc5rt of Respect", x, y, 16 << i );
+ y += 16 << i;
+ }
+
+ msdf_text( "Thanks again!", 10, 200, 50 );
+ }
+
+ renderer_end_pass();
+ renderer_end_frame();
+}
diff --git a/shaders.cc b/shaders.cc
@@ -39,6 +39,9 @@ void shaders_init() {
shaders[ SHADER_TEXT ].path = "shaders/text.glsl";
shaders[ SHADER_TEXT ].texture_uniform_names[ 0 ] = "atlas";
+ shaders[ SHADER_MSDF ].path = "shaders/msdf.glsl";
+ shaders[ SHADER_MSDF ].texture_uniform_names[ 0 ] = "atlas";
+
shaders[ SHADER_SKYBOX ].path = "shaders/skybox.glsl";
shaders[ SHADER_SKYBOX ].texture_uniform_names[ 0 ] = "blue_noise";
diff --git a/shaders.h b/shaders.h
@@ -4,6 +4,8 @@
enum ShaderID {
SHADER_TEXT,
+ SHADER_MSDF,
+
SHADER_SKYBOX,
SHADER_FLAT_VERTEX_COLOURS,
SHADER_FLAT_TEXTURED,
diff --git a/shaders/msdf.glsl b/shaders/msdf.glsl
@@ -0,0 +1,84 @@
+struct VSOut {
+ vec4 colour;
+ vec2 uv;
+};
+
+#ifdef VERTEX_SHADER
+
+in vec3 position;
+in vec4 colour;
+in vec2 tex_coord0;
+
+out VSOut v2f;
+
+void main() {
+ gl_Position = vec4( position, 1.0 );
+ v2f.colour = colour;
+ v2f.uv = tex_coord0;
+}
+
+#else
+
+in VSOut v2f;
+
+uniform sampler2D atlas;
+
+out vec4 screen_colour;
+
+float median( vec3 v ) {
+ return max( min( v.x, v.y ), min( max( v.x, v.y ), v.z ) );
+}
+
+float sample_msdf( vec2 uv ) {
+ vec2 msdfUnit = 2.0 / vec2( textureSize( atlas, 0 ) );
+ vec3 sample = texture( atlas, uv ).rgb;
+ float sigDist = median(sample) - 0.5;
+ sigDist *= dot( msdfUnit, 0.5 / fwidth( uv ) );
+ return clamp( sigDist + 0.5, 0.0, 1.0 );
+
+ /* sigDist *= ( 2.0 / atlas.w ) * ( 0.5 / fwidth( uv.x ) ) */
+ /* + ( 2.0 / atlas.h ) * ( 0.5 / fwidth( uv.y ) ) */
+}
+
+float linearstep( float lo, float hi, float x ) {
+ return ( clamp( x, lo, hi ) - lo ) / ( hi - lo );
+}
+
+vec4 sample_msdf( vec2 uv, float half_pixel_size, bool border ) {
+ vec3 sample = texture( atlas, uv ).rgb;
+ float d = 2.0 * median( sample ) - 1.0; // rescale to [-1,1], positive being inside
+
+ vec4 border_colour = vec4( 0.3, 0.3, 0.3, 1.0 );
+ vec4 text_colour = vec4( 0.0, 0.0, 0.0, 1.0 );
+
+ float alpha = step( -d, 0.0 );
+ return vec4( vec3( 1.0, 0.0, 0.0 ), text_colour.a * alpha );
+
+ // if( border ) {
+ // float border_amount = linearstep( -half_pixel_size, half_pixel_size, d );
+ // vec4 colour = mix( border_colour, text_colour, border_amount );
+ //
+ // float alpha = linearstep( -3.0 * half_pixel_size, -half_pixel_size, d );
+ // return vec4( colour.rgb, colour.a * alpha );
+ // }
+ //
+ // float alpha = linearstep( -half_pixel_size, half_pixel_size, d );
+ // return vec4( text_colour.rgb, text_colour.a * alpha );
+}
+
+void main() {
+ bool border = true;
+
+ vec2 fw = fwidth( v2f.uv );
+ float half_pixel_size = 0.5 * ( 1.0 / 14.0 ) * dot( fw, textureSize( atlas, 0 ) );
+
+ screen_colour = sample_msdf( v2f.uv, half_pixel_size, border );
+ screen_colour += 0.5 * sample_msdf( v2f.uv - vec2( 0.354 * fw.x, 0.0 ), half_pixel_size, border );
+ screen_colour += 0.5 * sample_msdf( v2f.uv + vec2( 0.354 * fw.x, 0.0 ), half_pixel_size, border );
+ screen_colour += 0.5 * sample_msdf( v2f.uv - vec2( 0.0, 0.354 * fw.y ), half_pixel_size, border );
+ screen_colour += 0.5 * sample_msdf( v2f.uv + vec2( 0.0, 0.354 * fw.y ), half_pixel_size, border );
+
+ screen_colour *= 1.0 / 3.0;
+}
+
+#endif