From 69e1a47511781abdc2cd14d9d43c625bb588f25a Mon Sep 17 00:00:00 2001 From: Olli Wang Date: Mon, 10 Feb 2020 14:54:27 +0800 Subject: [PATCH 01/19] Fixes the issue that font size is inconsistent. This commit fixes the issue that font size is inconsistent between different font files. `stb_truetype.h` is also updated to the latest version to support additional font types such as OpenType fonts. --- src/fontstash.h | 6 +- src/stb_truetype.h | 2092 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 1930 insertions(+), 168 deletions(-) diff --git a/src/fontstash.h b/src/fontstash.h index 9df68b3..74cb175 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -194,7 +194,7 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) { - return size / (font->font->ascender - font->font->descender); + return size / font->font->units_per_EM; } int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) @@ -210,7 +210,7 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float FT_Fixed advFixed; FONS_NOTUSED(scale); - ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); + ftError = FT_Set_Pixel_Sizes(font->font, 0, size); if (ftError) return 0; ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); if (ftError) return 0; @@ -295,7 +295,7 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) { - return stbtt_ScaleForPixelHeight(&font->font, size); + return stbtt_ScaleForMappingEmToPixels(&font->font, size); } int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) diff --git a/src/stb_truetype.h b/src/stb_truetype.h index bfb1841..f6ab5b0 100644 --- a/src/stb_truetype.h +++ b/src/stb_truetype.h @@ -1,11 +1,21 @@ -// stb_truetype.h - v1.09 - public domain -// authored from 2009-2015 by Sean Barrett / RAD Game Tools +// stb_truetype.h - v1.24 - public domain +// authored from 2009-2020 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= // // This library processes TrueType files: // parse files // extract glyph metrics // extract glyph shapes // render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) // // Todo: // non-MS cmaps @@ -20,36 +30,49 @@ // // Mikko Mononen: compound shape support, more cmap formats // Tor Andersson: kerning, subpixel rendering -// -// 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 +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning // // Misc other: // Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) // // VERSION HISTORY // +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; @@ -57,24 +80,16 @@ // 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 // // Full history can be found at the end of this file. // // LICENSE // -// This software is in the public domain. Where that dedication is not -// recognized, you are granted a perpetual, irrevocable license to copy, -// distribute, and modify this file as you see fit. +// See end of file for license information. // // 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 @@ -90,14 +105,15 @@ // 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() // // "Load" a font file from a memory buffer (you have to keep the buffer loaded) // stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// 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 @@ -107,6 +123,7 @@ // Character advance/positioning // stbtt_GetCodepointHMetrics() // stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() // stbtt_GetCodepointKernAdvance() // // Starting with version 1.06, the rasterizer was replaced with a new, @@ -162,7 +179,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 @@ -172,6 +189,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 . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to #define STBTT_ifloor(x) ((int) floor(x)) @@ -404,6 +442,23 @@ int main(int arg, char **argv) #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 @@ -424,7 +479,7 @@ int main(int arg, char **argv) #endif #ifndef STBTT_memcpy - #include + #include #define STBTT_memcpy memcpy #define STBTT_memset memset #endif @@ -450,6 +505,14 @@ int main(int arg, char **argv) extern "C" { #endif +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + ////////////////////////////////////////////////////////////////////////////// // // TEXTURE BAKING API @@ -479,7 +542,7 @@ typedef struct float x1,y1,s1,t1; // bottom-right } stbtt_aligned_quad; -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above +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 @@ -494,6 +557,8 @@ STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // // // 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. ////////////////////////////////////////////////////////////////////////////// @@ -519,7 +584,7 @@ typedef struct stbrp_rect stbrp_rect; 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 weight x height. stride_in_bytes is +// 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 @@ -532,7 +597,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 @@ -557,7 +622,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 @@ -579,19 +644,25 @@ 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_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space stbtt_aligned_quad *q, // output: quad to draw int align_to_integer); -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +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, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *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 +// 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 @@ -607,6 +678,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; @@ -618,18 +690,23 @@ struct stbtt_pack_context { // // +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. You can just skip -// this step if you know it's that kind of font. +// 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. -typedef struct stbtt_fontinfo +struct stbtt_fontinfo { void * userdata; unsigned char * data; // pointer to .ttf file @@ -637,10 +714,17 @@ typedef 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,svg; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph -} stbtt_fontinfo; + + 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 @@ -659,6 +743,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. ////////////////////////////////////////////////////////////////////////////// @@ -687,6 +772,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 @@ -706,6 +797,18 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); // as above, but takes one or more glyph indices for greater efficiency +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) ////////////////////////////////////////////////////////////////////////////// // @@ -717,7 +820,8 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in enum { STBTT_vmove=1, STBTT_vline, - STBTT_vcurve + STBTT_vcurve, + STBTT_vcubic }; #endif @@ -726,7 +830,7 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in #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; + stbtt_vertex_type x,y,cx,cy,cx1,cy1; unsigned char type,padding; } stbtt_vertex; #endif @@ -739,7 +843,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 @@ -749,6 +853,11 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); // frees the data allocated above +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + ////////////////////////////////////////////////////////////////////////////// // // BITMAP RENDERING @@ -780,6 +889,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 @@ -797,6 +910,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); @@ -819,6 +933,64 @@ STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap int invert, // if non-zero, vertically flip shape void *userdata); // context for to STBTT_MALLOC +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + ////////////////////////////////////////////////////////////////////////////// // // Finding the right font... @@ -942,6 +1114,158 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS #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 @@ -954,32 +1278,22 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS #define ttCHAR(p) (* (stbtt_int8 *) (p)) #define ttFixed(p) ttLONG(p) -#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) - - #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) - #define ttSHORT(p) (* (stbtt_int16 *) (p)) - #define ttULONG(p) (* (stbtt_uint32 *) (p)) - #define ttLONG(p) (* (stbtt_int32 *) (p)) - -#else - - static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#endif +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(const stbtt_uint8 *font) +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; } @@ -997,7 +1311,7 @@ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, return 0; } -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +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)) @@ -1016,14 +1330,59 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, return -1; } -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) { - stbtt_uint8 *data = (stbtt_uint8 *) data2; 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 @@ -1032,8 +1391,62 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, i 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 - if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + 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) @@ -1041,6 +1454,8 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, i else info->numGlyphs = 0xffff; + info->svg = -1; + // find a cmap encoding table we understand *now* to avoid searching // later. (todo: could make this installable) // the same regardless of glyph. @@ -1184,6 +1599,8 @@ 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 @@ -1198,15 +1615,21 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) 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) { - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; + 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); + 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; } @@ -1218,7 +1641,10 @@ STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, i STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) { stbtt_int16 numberOfContours; - int g = stbtt__GetGlyfOffset(info, glyph_index); + 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; @@ -1240,7 +1666,7 @@ static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_ return num_vertices; } -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { stbtt_int16 numberOfContours; stbtt_uint8 *endPtsOfContours; @@ -1336,7 +1762,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s 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 + // 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 @@ -1378,7 +1804,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s } } num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours == -1) { + } else if (numberOfContours < 0) { // Compound shapes. int more = 1; stbtt_uint8 *comp = data + g + 10; @@ -1389,7 +1815,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s 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; @@ -1419,7 +1845,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s 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]); @@ -1455,9 +1881,6 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s // More components ? more = flags & (1<<5); } - } else if (numberOfContours < 0) { - // @TODO other compound variations? - STBTT_assert(0); } else { // numberOfCounters == 0, do nothing } @@ -1466,6 +1889,414 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s 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); @@ -1478,7 +2309,49 @@ 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) +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint8 *data = info->data + info->kern; stbtt_uint32 needle, straw; @@ -1508,9 +2381,260 @@ 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> 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); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) { - if (!info->kern) // 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)); } @@ -1527,6 +2651,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); @@ -1552,6 +2687,45 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) STBTT_free(v, info->userdata); } +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + ////////////////////////////////////////////////////////////////////////////// // // antialiasing software rasterizer @@ -1623,7 +2797,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; } } @@ -1677,7 +2851,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i 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); @@ -1755,7 +2929,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac } } } - + e = e->next; } } @@ -1993,7 +3167,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, } y_crossing += dy * (x2 - (x1+1)); - STBTT_assert(fabs(area) <= 1.01f); + STBTT_assert(STBTT_fabs(area) <= 1.01f); scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); @@ -2019,19 +3193,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); @@ -2071,6 +3244,8 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, 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 @@ -2109,7 +3284,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; @@ -2129,7 +3310,7 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int m; sum += scanline2[i]; k = scanline[i] + sum; - k = (float) fabs(k)*255 + 0.5f; + 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; @@ -2178,7 +3359,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; @@ -2313,7 +3494,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 @@ -2334,6 +3515,48 @@ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x 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) { @@ -2390,6 +3613,14 @@ static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, 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; @@ -2406,8 +3637,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); @@ -2425,12 +3657,15 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; - stbtt_vertex *vertices; + 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) return NULL; + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } scale_y = scale_x; } @@ -2445,7 +3680,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info 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) { @@ -2456,7 +3691,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info } 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) { @@ -2468,7 +3703,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigne int ix0,iy0; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; + stbtt__bitmap gbm; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); gbm.pixels = output; @@ -2490,7 +3725,12 @@ STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char * 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) { @@ -2500,7 +3740,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns 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) { @@ -2513,7 +3753,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned ch // // This is SUPER-CRAPPY packing to keep source code small -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +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 @@ -2559,11 +3799,11 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // fo return bottom_y; } -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +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; - stbtt_bakedchar *b = chardata + char_index; + 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); @@ -2586,11 +3826,6 @@ STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int // #ifndef STB_RECT_PACK_VERSION -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif typedef int stbrp_coord; @@ -2630,7 +3865,7 @@ static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *no con->y = 0; con->bottom_y = 0; STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); + STBTT__NOTUSED(num_nodes); } static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) @@ -2684,6 +3919,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); @@ -2709,6 +3945,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) @@ -2848,9 +4089,10 @@ static float stbtt__oversample_shift(int oversample) } // rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; + int missing_glyph_added = 0; k=0; for (i=0; i < num_ranges; ++i) { @@ -2862,13 +4104,19 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon 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 || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } ++k; } } @@ -2876,10 +4124,33 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon 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, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +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; + int i,j,k, missing_glyph = -1, return_value = 1; // save current values int old_h_over = spc->h_oversample; @@ -2898,7 +4169,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt 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]; @@ -2944,6 +4215,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt bc->yoff = (float) y0 * recip_v + sub_y; bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; } else { return_value = 0; // if any fail, report failure } @@ -2964,7 +4242,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; @@ -2982,7 +4260,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd 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; @@ -2993,14 +4271,14 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd 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, 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; @@ -3012,10 +4290,23 @@ 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_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +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; - stbtt_packedchar *b = chardata + char_index; + const stbtt_packedchar *b = chardata + char_index; if (align_to_integer) { float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); @@ -3039,6 +4330,382 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i *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 (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // 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); +} ////////////////////////////////////////////////////////////////////////////// // @@ -3046,7 +4713,7 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i // // 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(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; @@ -3085,9 +4752,9 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 return i; } -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, 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 @@ -3143,7 +4810,7 @@ static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, return 1; } else if (matchlen < nlen && name[matchlen] == ' ') { ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) return 1; } } else { @@ -3189,7 +4856,7 @@ static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *nam return 0; } -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) { stbtt_int32 i; for (i=0;;++i) { @@ -3200,11 +4867,64 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const } } +#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; @@ -3247,3 +4967,45 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const // 0.2 (2009-03-11) Fix unsigned/signed char warnings // 0.1 (2009-03-09) First public release // + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file From 8851f025e4b7ad05246a2a9c0bf453a0fa2b2ca0 Mon Sep 17 00:00:00 2001 From: Olli Wang Date: Mon, 17 Feb 2020 04:58:22 +0800 Subject: [PATCH 02/19] Support for loading a certain font face from a font file. This commit allows loading a certain font face from a font collection file such as TTC. --- src/fontstash.h | 27 ++++++++++++++++----------- src/nanovg.c | 16 +++++++++++++--- src/nanovg.h | 6 ++++++ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/fontstash.h b/src/fontstash.h index 74cb175..0b07889 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -102,8 +102,8 @@ int fonsExpandAtlas(FONScontext* s, int width, int height); int fonsResetAtlas(FONScontext* stash, int width, int height); // Add fonts -int fonsAddFont(FONScontext* s, const char* name, const char* path); -int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData); +int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex); +int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex); int fonsGetFontByName(FONScontext* s, const char* name); // State handling @@ -175,13 +175,13 @@ int fons__tt_done(FONScontext *context) return ftError == 0; } -int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) +int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) { FT_Error ftError; FONS_NOTUSED(context); //font->font.userdata = stash; - ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); + ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); return ftError == 0; } @@ -278,13 +278,18 @@ int fons__tt_done(FONScontext *context) return 1; } -int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) +int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) { - int stbError; + int offset, stbError; FONS_NOTUSED(dataSize); font->font.userdata = context; - stbError = stbtt_InitFont(&font->font, data, 0); + offset = stbtt_GetFontOffsetForIndex(data, fontIndex); + if (offset == -1) { + stbError = 0; + } else { + stbError = stbtt_InitFont(&font->font, data, offset); + } return stbError; } @@ -890,7 +895,7 @@ error: return FONS_INVALID; } -int fonsAddFont(FONScontext* stash, const char* name, const char* path) +int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) { FILE* fp = 0; int dataSize = 0; @@ -910,7 +915,7 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path) fp = 0; if (readed != dataSize) goto error; - return fonsAddFontMem(stash, name, data, dataSize, 1); + return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); error: if (data) free(data); @@ -918,7 +923,7 @@ error: return FONS_INVALID; } -int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) +int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) { int i, ascent, descent, fh, lineGap; FONSfont* font; @@ -943,7 +948,7 @@ int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, in // Init font stash->nscratch = 0; - if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; + if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; // Store normalized line height. The real line height is got // by multiplying the lineh by font size. diff --git a/src/nanovg.c b/src/nanovg.c index a28804f..cfdb8d2 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2287,14 +2287,24 @@ void nvgStroke(NVGcontext* ctx) } // Add fonts -int nvgCreateFont(NVGcontext* ctx, const char* name, const char* path) +int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) { - return fonsAddFont(ctx->fs, name, path); + return fonsAddFont(ctx->fs, name, filename, 0); +} + +int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) +{ + return fonsAddFont(ctx->fs, name, filename, fontIndex); } int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) { - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData); + return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); +} + +int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) +{ + return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); } int nvgFindFont(NVGcontext* ctx, const char* name) diff --git a/src/nanovg.h b/src/nanovg.h index 2255d6a..2c01ff9 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -546,10 +546,16 @@ void nvgStroke(NVGcontext* ctx); // Returns handle to the font. int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); +// fontIndex specifies which font face to load from a .ttf/.ttc file. +int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); + // Creates font by loading it from the specified memory chunk. // Returns handle to the font. int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); +// fontIndex specifies which font face to load from a .ttf/.ttc file. +int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); + // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. int nvgFindFont(NVGcontext* ctx, const char* name); From 5a006cdca4c95eb6b7f01325c0a7c7029bf90637 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Wed, 26 Feb 2020 12:12:31 +0000 Subject: [PATCH 03/19] Pass fringe parameter to renderTriangles, fixes bad clipping of text on high DPI screens --- src/nanovg.c | 2 +- src/nanovg.h | 2 +- src/nanovg_gl.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nanovg.c b/src/nanovg.c index cfdb8d2..d283f35 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2433,7 +2433,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) paint.innerColor.a *= state->alpha; paint.outerColor.a *= state->alpha; - ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts); + ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); ctx->drawCallCount++; ctx->textTriCount += nverts/3; diff --git a/src/nanovg.h b/src/nanovg.h index 2c01ff9..92dd219 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -664,7 +664,7 @@ struct NVGparams { void (*renderFlush)(void* uptr); void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); - void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts); + void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); void (*renderDelete)(void* uptr); }; typedef struct NVGparams NVGparams; diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 42ff696..5332da1 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -1481,7 +1481,7 @@ error: } static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, - const NVGvertex* verts, int nverts) + const NVGvertex* verts, int nverts, float fringe) { GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcall* call = glnvg__allocCall(gl); @@ -1504,7 +1504,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOper call->uniformOffset = glnvg__allocFragUniforms(gl, 1); if (call->uniformOffset == -1) goto error; frag = nvg__fragUniformPtr(gl, call->uniformOffset); - glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); + glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); frag->type = NSVG_SHADER_IMG; return; From 528dc4efb57de101c2aac2403fe1133a073a1257 Mon Sep 17 00:00:00 2001 From: Olli Wang Date: Fri, 28 Feb 2020 16:53:27 +0800 Subject: [PATCH 04/19] Fixes the issue that line gap may ruin the text layout. --- src/fontstash.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fontstash.h b/src/fontstash.h index 0b07889..c4a8f14 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -953,10 +953,11 @@ int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, in // Store normalized line height. The real line height is got // by multiplying the lineh by font size. fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); + ascent += lineGap; fh = ascent - descent; font->ascender = (float)ascent / (float)fh; font->descender = (float)descent / (float)fh; - font->lineh = (float)(fh + lineGap) / (float)fh; + font->lineh = font->ascender - font->descender; return idx; From c519ae578b26e56f8935c47456c5c8c412fc73cb Mon Sep 17 00:00:00 2001 From: Olli Wang Date: Tue, 25 Feb 2020 23:46:44 +0800 Subject: [PATCH 05/19] Support for resetting fallback fonts. This commit adds `nvgResetFallbackFontsId()` and `nvgResetFallbackFonts()` functions for resetting fallback fonts. --- src/fontstash.h | 11 +++++++++++ src/nanovg.c | 10 ++++++++++ src/nanovg.h | 6 ++++++ 3 files changed, 27 insertions(+) diff --git a/src/fontstash.h b/src/fontstash.h index 0b07889..90b354a 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -797,6 +797,17 @@ int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) return 0; } +void fonsResetFallbackFont(FONScontext* stash, int base) +{ + int i; + + FONSfont* baseFont = stash->fonts[base]; + baseFont->nfallbacks = 0; + baseFont->nglyphs = 0; + for (i = 0; i < FONS_HASH_LUT_SIZE; i++) + baseFont->lut[i] = -1; +} + void fonsSetSize(FONScontext* stash, float size) { fons__getState(stash)->size = size; diff --git a/src/nanovg.c b/src/nanovg.c index cfdb8d2..67482d0 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2325,6 +2325,16 @@ int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallba return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont)); } +void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) +{ + fonsResetFallbackFont(ctx->fs, baseFont); +} + +void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) +{ + nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont)); +} + // State setting void nvgFontSize(NVGcontext* ctx, float size) { diff --git a/src/nanovg.h b/src/nanovg.h index 2c01ff9..48cc19f 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -565,6 +565,12 @@ int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); // Adds a fallback font by name. int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); +// Resets fallback fonts by handle. +void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); + +// Resets fallback fonts by name. +void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); + // Sets the font size of current text style. void nvgFontSize(NVGcontext* ctx, float size); From 79561dc47f6704bdfd78cde1281371e5c0087b78 Mon Sep 17 00:00:00 2001 From: Olli Wang Date: Fri, 28 Feb 2020 18:27:48 +0800 Subject: [PATCH 06/19] =?UTF-8?q?Fixes=20demo=E2=80=99s=20font=20sizes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes the demo’s font sizes for the changes in #558, which makes the rendered font size matches what you saw on other native apps. The demo used bigger font sizes than they actually were. This commit deduces the font sizes to match the original demo’s look. --- example/demo.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/example/demo.c b/example/demo.c index cfad99e..90934cb 100644 --- a/example/demo.c +++ b/example/demo.c @@ -98,7 +98,7 @@ void drawWindow(NVGcontext* vg, const char* title, float x, float y, float w, fl nvgStrokeColor(vg, nvgRGBA(0,0,0,32)); nvgStroke(vg); - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgFontFace(vg, "sans-bold"); nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); @@ -137,7 +137,7 @@ void drawSearchBox(NVGcontext* vg, const char* text, float x, float y, float w, nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon), NULL); - nvgFontSize(vg, 20.0f); + nvgFontSize(vg, 17.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,32)); @@ -168,7 +168,7 @@ void drawDropDown(NVGcontext* vg, const char* text, float x, float y, float w, f nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); nvgStroke(vg); - nvgFontSize(vg, 20.0f); + nvgFontSize(vg, 17.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,160)); nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); @@ -185,7 +185,7 @@ void drawLabel(NVGcontext* vg, const char* text, float x, float y, float w, floa { NVG_NOTUSED(w); - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,128)); @@ -214,7 +214,7 @@ void drawEditBox(NVGcontext* vg, const char* text, float x, float y, float w, fl drawEditBoxBase(vg, x,y, w,h); - nvgFontSize(vg, 20.0f); + nvgFontSize(vg, 17.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,64)); nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); @@ -230,13 +230,13 @@ void drawEditBoxNum(NVGcontext* vg, uw = nvgTextBounds(vg, 0,0, units, NULL, NULL); - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,64)); nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); nvgText(vg, x+w-h*0.3f,y+h*0.5f,units, NULL); - nvgFontSize(vg, 20.0f); + nvgFontSize(vg, 17.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,128)); nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); @@ -249,7 +249,7 @@ void drawCheckBox(NVGcontext* vg, const char* text, float x, float y, float w, f char icon[8]; NVG_NOTUSED(w); - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgFontFace(vg, "sans"); nvgFillColor(vg, nvgRGBA(255,255,255,160)); @@ -262,7 +262,7 @@ void drawCheckBox(NVGcontext* vg, const char* text, float x, float y, float w, f nvgFillPaint(vg, bg); nvgFill(vg); - nvgFontSize(vg, 40); + nvgFontSize(vg, 33); nvgFontFace(vg, "icons"); nvgFillColor(vg, nvgRGBA(255,255,255,128)); nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); @@ -291,7 +291,7 @@ void drawButton(NVGcontext* vg, int preicon, const char* text, float x, float y, nvgStrokeColor(vg, nvgRGBA(0,0,0,48)); nvgStroke(vg); - nvgFontSize(vg, 20.0f); + nvgFontSize(vg, 17.0f); nvgFontFace(vg, "sans-bold"); tw = nvgTextBounds(vg, 0,0, text, NULL, NULL); if (preicon != 0) { @@ -309,7 +309,7 @@ void drawButton(NVGcontext* vg, int preicon, const char* text, float x, float y, nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon), NULL); } - nvgFontSize(vg, 20.0f); + nvgFontSize(vg, 17.0f); nvgFontFace(vg, "sans-bold"); nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); nvgFillColor(vg, nvgRGBA(0,0,0,160)); @@ -874,7 +874,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, nvgSave(vg); - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgFontFace(vg, "sans"); nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); nvgTextMetrics(vg, NULL, NULL, &lineh); @@ -928,7 +928,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, if (gutter) { char txt[16]; snprintf(txt, sizeof(txt), "%d", gutter); - nvgFontSize(vg, 13.0f); + nvgFontSize(vg, 12.0f); nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); nvgTextBounds(vg, gx,gy, txt, NULL, bounds); @@ -944,7 +944,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, y += 20.0f; - nvgFontSize(vg, 13.0f); + nvgFontSize(vg, 11.0f); nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); nvgTextLineHeight(vg, 1.2f); From d98041b4ee29585977feb9e69b403d4804af5d5c Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Mon, 2 Mar 2020 23:18:05 +0000 Subject: [PATCH 07/19] Bind a dummy texture in setUniforms when compiling with emscripten fixes the WebGL error "RENDER WARNING: there is no texture bound to the unit 0"" in Chrome --- src/nanovg_gl.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 5332da1..046648b 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -268,6 +268,10 @@ struct GLNVGcontext { GLuint stencilFuncMask; GLNVGblend blendFunc; #endif + +#ifdef __EMSCRIPTEN__ + int dummyTex; +#endif }; typedef struct GLNVGcontext GLNVGcontext; @@ -704,6 +708,10 @@ static int glnvg__renderCreate(void* uptr) glFinish(); +#ifdef __EMSCRIPTEN__ + gl->dummyTex = 0; +#endif + return 1; } @@ -985,7 +993,15 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); glnvg__checkError(gl, "tex paint tex"); } else { +#ifdef __EMSCRIPTEN__ + if(gl->dummyTex == 0) { + gl->dummyTex = glnvg__renderCreateTexture(&gl, NVG_TEXTURE_ALPHA, 2, 2, 0, NULL); + } + GLNVGtexture* tex = glnvg__findTexture(gl, gl->dummyTex); + glnvg__bindTexture(gl, tex->tex); +#else glnvg__bindTexture(gl, 0); +#endif } } From 3e986cdf4bc9ba8f411bb8d4ca911613abf9eb04 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Thu, 5 Mar 2020 22:10:53 +0000 Subject: [PATCH 08/19] call glnvg__renderCreateTexture() in glnvg__renderCreate --- src/nanovg_gl.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 046648b..768e71c 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -704,14 +704,14 @@ static int glnvg__renderCreate(void* uptr) #endif gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; +#ifdef __EMSCRIPTEN__ + gl->dummyTex = glnvg__renderCreateTexture(&gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); +#endif + glnvg__checkError(gl, "create done"); glFinish(); -#ifdef __EMSCRIPTEN__ - gl->dummyTex = 0; -#endif - return 1; } @@ -994,9 +994,6 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) glnvg__checkError(gl, "tex paint tex"); } else { #ifdef __EMSCRIPTEN__ - if(gl->dummyTex == 0) { - gl->dummyTex = glnvg__renderCreateTexture(&gl, NVG_TEXTURE_ALPHA, 2, 2, 0, NULL); - } GLNVGtexture* tex = glnvg__findTexture(gl, gl->dummyTex); glnvg__bindTexture(gl, tex->tex); #else From e281ffcabdf02b5eccb3e199f59c9e55a06b5840 Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Sat, 7 Mar 2020 23:01:56 +0200 Subject: [PATCH 09/19] Fixed nvgTextBreakLines returning bad minx - nvgTextBreakLines: wordMinX was relative to rowStartX, which caused problems when rowStartX change on line break, but wordMinX was still relative to the previous rowStartX - fixed font size on perf graphs - made demo to use row minx/maxx instead of row->width - fixed demo hover distance --- example/demo.c | 16 +++++++++------- example/perf.c | 22 +++++++++++----------- src/nanovg.c | 8 ++++---- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/example/demo.c b/example/demo.c index 90934cb..3331622 100644 --- a/example/demo.c +++ b/example/demo.c @@ -25,7 +25,7 @@ #define ICON_TRASH 0xE729 //static float minf(float a, float b) { return a < b ? a : b; } -static float maxf(float a, float b) { return a > b ? a : b; } +//static float maxf(float a, float b) { return a > b ? a : b; } //static float absf(float a) { return a >= 0.0f ? a : -a; } static float clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } @@ -868,8 +868,10 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, float caretx, px; float bounds[4]; float a; + const char* hoverText = "Hover your mouse over the text to see calculated caret position."; float gx,gy; int gutter = 0; + const char* boxText = "Testing\nsome multiline\ntext."; NVG_NOTUSED(height); nvgSave(vg); @@ -891,7 +893,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, nvgBeginPath(vg); nvgFillColor(vg, nvgRGBA(255,255,255,hit?64:16)); - nvgRect(vg, x, y, row->width, lineh); + nvgRect(vg, x + row->minx, y, row->maxx - row->minx, lineh); nvgFill(vg); nvgFillColor(vg, nvgRGBA(255,255,255,255)); @@ -948,12 +950,12 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); nvgTextLineHeight(vg, 1.2f); - nvgTextBoxBounds(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL, bounds); + nvgTextBoxBounds(vg, x,y, 150, hoverText, NULL, bounds); // Fade the tooltip out when close to it. - gx = fabsf((mx - (bounds[0]+bounds[2])*0.5f) / (bounds[0] - bounds[2])); - gy = fabsf((my - (bounds[1]+bounds[3])*0.5f) / (bounds[1] - bounds[3])); - a = maxf(gx, gy) - 0.5f; + gx = clampf(mx, bounds[0], bounds[2]) - mx; + gy = clampf(my, bounds[1], bounds[3]) - my; + a = sqrtf(gx*gx + gy*gy) / 30.0f; a = clampf(a, 0, 1); nvgGlobalAlpha(vg, a); @@ -967,7 +969,7 @@ void drawParagraph(NVGcontext* vg, float x, float y, float width, float height, nvgFill(vg); nvgFillColor(vg, nvgRGBA(0,0,0,220)); - nvgTextBox(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL); + nvgTextBox(vg, x,y, 150, hoverText, NULL); nvgRestore(vg); } diff --git a/example/perf.c b/example/perf.c index a74dc3c..e30c552 100644 --- a/example/perf.c +++ b/example/perf.c @@ -151,36 +151,36 @@ void renderGraph(NVGcontext* vg, float x, float y, PerfGraph* fps) nvgFontFace(vg, "sans"); if (fps->name[0] != '\0') { - nvgFontSize(vg, 14.0f); + nvgFontSize(vg, 12.0f); nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); nvgFillColor(vg, nvgRGBA(240,240,240,192)); - nvgText(vg, x+3,y+1, fps->name, NULL); + nvgText(vg, x+3,y+3, fps->name, NULL); } if (fps->style == GRAPH_RENDER_FPS) { - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); nvgFillColor(vg, nvgRGBA(240,240,240,255)); sprintf(str, "%.2f FPS", 1.0f / avg); - nvgText(vg, x+w-3,y+1, str, NULL); + nvgText(vg, x+w-3,y+3, str, NULL); - nvgFontSize(vg, 15.0f); - nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BOTTOM); + nvgFontSize(vg, 13.0f); + nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_BASELINE); nvgFillColor(vg, nvgRGBA(240,240,240,160)); sprintf(str, "%.2f ms", avg * 1000.0f); - nvgText(vg, x+w-3,y+h-1, str, NULL); + nvgText(vg, x+w-3,y+h-3, str, NULL); } else if (fps->style == GRAPH_RENDER_PERCENT) { - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); nvgFillColor(vg, nvgRGBA(240,240,240,255)); sprintf(str, "%.1f %%", avg * 1.0f); - nvgText(vg, x+w-3,y+1, str, NULL); + nvgText(vg, x+w-3,y+3, str, NULL); } else { - nvgFontSize(vg, 18.0f); + nvgFontSize(vg, 15.0f); nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_TOP); nvgFillColor(vg, nvgRGBA(240,240,240,255)); sprintf(str, "%.2f ms", avg * 1000.0f); - nvgText(vg, x+w-3,y+1, str, NULL); + nvgText(vg, x+w-3,y+3, str, NULL); } } diff --git a/src/nanovg.c b/src/nanovg.c index ac63f14..aa86354 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2704,7 +2704,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa rowStartX = iter.x; rowStart = iter.str; rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; + rowWidth = iter.nextx - rowStartX; rowMinX = q.x0 - rowStartX; rowMaxX = q.x1 - rowStartX; wordStart = iter.str; @@ -2734,7 +2734,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { wordStart = iter.str; wordStartX = iter.x; - wordMinX = q.x0 - rowStartX; + wordMinX = q.x0; } // Break to new line when a character is beyond break width. @@ -2771,13 +2771,13 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa nrows++; if (nrows >= maxRows) return nrows; + // Update row rowStartX = wordStartX; rowStart = wordStart; rowEnd = iter.next; rowWidth = iter.nextx - rowStartX; - rowMinX = wordMinX; + rowMinX = wordMinX - rowStartX; rowMaxX = q.x1 - rowStartX; - // No change to the word start } // Set null break point breakEnd = rowStart; From 35dbc98160f267420e5343d8b87a9f0d16b0a16f Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Fri, 6 Mar 2020 11:10:34 +0000 Subject: [PATCH 10/19] modify to use dummy texture even without emscripten --- src/nanovg_gl.h | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 768e71c..798b236 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -269,9 +269,7 @@ struct GLNVGcontext { GLNVGblend blendFunc; #endif -#ifdef __EMSCRIPTEN__ int dummyTex; -#endif }; typedef struct GLNVGcontext GLNVGcontext; @@ -504,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader) #endif } +static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); + static int glnvg__renderCreate(void* uptr) { GLNVGcontext* gl = (GLNVGcontext*)uptr; @@ -704,9 +704,9 @@ static int glnvg__renderCreate(void* uptr) #endif gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; -#ifdef __EMSCRIPTEN__ - gl->dummyTex = glnvg__renderCreateTexture(&gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); -#endif + // Some platforms does not allow to have samples to unset textures. + // Create empty one which is bound when there's no texture specified. + gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); glnvg__checkError(gl, "create done"); @@ -981,6 +981,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) { + GLNVGtexture* tex = NULL; #if NANOVG_GL_USE_UNIFORMBUFFER glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); #else @@ -989,17 +990,14 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) #endif if (image != 0) { - GLNVGtexture* tex = glnvg__findTexture(gl, image); - glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); - glnvg__checkError(gl, "tex paint tex"); - } else { -#ifdef __EMSCRIPTEN__ - GLNVGtexture* tex = glnvg__findTexture(gl, gl->dummyTex); - glnvg__bindTexture(gl, tex->tex); -#else - glnvg__bindTexture(gl, 0); -#endif + tex = glnvg__findTexture(gl, image); + } + // If no image is set, use empty texture + if (tex == NULL) { + tex = glnvg__findTexture(gl, gl->dummyTex); } + glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); + glnvg__checkError(gl, "tex paint tex"); } static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) From 426aa3f149a60a1f13fd0220c6b664e049df741e Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Wed, 18 Mar 2020 23:10:53 +0200 Subject: [PATCH 11/19] Fix for #570: round glyph positions to pixels using floor --- src/fontstash.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fontstash.h b/src/fontstash.h index b098f56..733159b 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -924,7 +924,7 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path, int font readed = fread(data, 1, dataSize, fp); fclose(fp); fp = 0; - if (readed != dataSize) goto error; + if (readed != (size_t)dataSize) goto error; return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); @@ -1225,8 +1225,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, y1 = (float)(glyph->y1-1); if (stash->params.flags & FONS_ZERO_TOPLEFT) { - rx = (float)(int)(*x + xoff); - ry = (float)(int)(*y + yoff); + rx = floorf(*x + xoff); + ry = floorf(*y + yoff); q->x0 = rx; q->y0 = ry; @@ -1238,8 +1238,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, q->s1 = x1 * stash->itw; q->t1 = y1 * stash->ith; } else { - rx = (float)(int)(*x + xoff); - ry = (float)(int)(*y - yoff); + rx = floorf(*x + xoff); + ry = floorf(*y - yoff); q->x0 = rx; q->y0 = ry; From 52265fe278a5bc38bc238cf56403e9e6eaf21e25 Mon Sep 17 00:00:00 2001 From: Geoff Date: Fri, 20 Mar 2020 09:51:00 +0100 Subject: [PATCH 12/19] Fixed Signed/Unsigned Comparison Warning In Visual Studio 17, I get warnings related to these variables: ```` fontstash.h(241): warning C4018: '<': signed/unsigned mismatch fontstash.h(242): warning C4018: '<': signed/unsigned mismatch ```` Making them unsigned fixes it. --- src/fontstash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fontstash.h b/src/fontstash.h index 733159b..a69846f 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -231,7 +231,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int { FT_GlyphSlot ftGlyph = font->font->glyph; int ftGlyphOffset = 0; - int x, y; + unsigned int x, y; FONS_NOTUSED(outWidth); FONS_NOTUSED(outHeight); FONS_NOTUSED(scaleX); From 4c7eb2f63da370b6ea894ed50d5a4bf519d4f3f2 Mon Sep 17 00:00:00 2001 From: My Name Date: Tue, 15 Sep 2020 11:41:34 +0900 Subject: [PATCH 13/19] Added a preprocessor definition to disable the use of stb_image + Added CMakeLists --- CMakeLists.txt | 19 +++++++++++++++++++ src/nanovg.c | 5 +++++ 2 files changed, 24 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7e43222 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.8) + +add_definitions(-DFONS_USE_FREETYPE) +add_definitions(-DNVG_NO_STB) + +add_library ( + nanovg + src/nanovg.c +) + +include_directories(../freetype/include) + +target_link_libraries( + nanovg + PUBLIC + freetype +) + +target_include_directories (nanovg PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/src/nanovg.c b/src/nanovg.c index aa86354..f42426a 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -24,8 +24,11 @@ #include "nanovg.h" #define FONTSTASH_IMPLEMENTATION #include "fontstash.h" + +#ifndef NVG_NO_STB #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" +#endif #ifdef _MSC_VER #pragma warning(disable: 4100) // unreferenced formal parameter @@ -788,6 +791,7 @@ void nvgFillPaint(NVGcontext* ctx, NVGpaint paint) nvgTransformMultiply(state->fill.xform, state->xform); } +#ifndef NVG_NO_STB int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) { int w, h, n, image; @@ -816,6 +820,7 @@ int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int stbi_image_free(img); return image; } +#endif int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data) { From 0fa06d96f168d0774ea5dccdccce888c1fc98bad Mon Sep 17 00:00:00 2001 From: My Name Date: Wed, 16 Sep 2020 11:11:26 +0900 Subject: [PATCH 14/19] Removed CMakeLists.txt --- CMakeLists.txt | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 7e43222..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 3.8) - -add_definitions(-DFONS_USE_FREETYPE) -add_definitions(-DNVG_NO_STB) - -add_library ( - nanovg - src/nanovg.c -) - -include_directories(../freetype/include) - -target_link_libraries( - nanovg - PUBLIC - freetype -) - -target_include_directories (nanovg PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) From 30b9f3e047bd4e0606ad324a71338b2a0e7d4c2c Mon Sep 17 00:00:00 2001 From: D4 Date: Thu, 1 Oct 2020 00:47:50 -0700 Subject: [PATCH 15/19] Squashed wonky FT kerning via FT_Load_Glyph flag FT_LOAD_TARGET_LIGHT --- src/fontstash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fontstash.h b/src/fontstash.h index a69846f..6e141a2 100644 --- a/src/fontstash.h +++ b/src/fontstash.h @@ -212,7 +212,7 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float ftError = FT_Set_Pixel_Sizes(font->font, 0, size); if (ftError) return 0; - ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); + ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT); if (ftError) return 0; ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); if (ftError) return 0; From fb785932a05fab7c2936cdb0c27cc885499371ed Mon Sep 17 00:00:00 2001 From: Dominus Iniquitatis Date: Thu, 5 Nov 2020 04:56:25 +0300 Subject: [PATCH 16/19] Single-byte typo fix --- src/nanovg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanovg.h b/src/nanovg.h index 032575f..cb63e52 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -414,7 +414,7 @@ NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, NVGcolor icol, NVGcolor ocol); -// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, +// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, From 621e0b8d35348741038bae20e7773fcefcf3f8ab Mon Sep 17 00:00:00 2001 From: Nat! Date: Sun, 20 Dec 2020 12:45:20 +0100 Subject: [PATCH 17/19] Fixed text vanishing when the transform is vertically flipped --- src/nanovg.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/nanovg.c b/src/nanovg.c index f42426a..53bd06b 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2454,6 +2454,12 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) ctx->textTriCount += nverts/3; } +static int nvg__isTransformFlipped( const float *xform) +{ + float det = xform[0] * xform[3] - xform[2] * xform[1]; + return( det < 0); +} + float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end) { NVGstate* state = nvg__getState(ctx); @@ -2464,6 +2470,8 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* float invscale = 1.0f / scale; int cverts = 0; int nverts = 0; + int isFlipped; + float tmp; if (end == NULL) end = string + strlen(string); @@ -2480,6 +2488,7 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* verts = nvg__allocTempVerts(ctx, cverts); if (verts == NULL) return x; + isFlipped = nvg__isTransformFlipped( state->xform); fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); prevIter = iter; while (fonsTextIterNext(ctx->fs, &iter, &q)) { @@ -2497,6 +2506,11 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* break; } prevIter = iter; + if( isFlipped) + { + tmp = q.y0; q.y0 = q.y1; q.y1 = tmp; + tmp = q.t0; q.t0 = q.t1; q.t1 = tmp; + } // Transform corners. nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale, q.y0*invscale); nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale, q.y0*invscale); From 0faac6f91a17db9e657dd8f7c2662cba124f70ed Mon Sep 17 00:00:00 2001 From: Nat! Date: Mon, 21 Dec 2020 10:59:15 +0100 Subject: [PATCH 18/19] less whitespace --- src/nanovg.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/nanovg.c b/src/nanovg.c index 53bd06b..a94083f 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2454,7 +2454,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) ctx->textTriCount += nverts/3; } -static int nvg__isTransformFlipped( const float *xform) +static int nvg__isTransformFlipped(const float *xform) { float det = xform[0] * xform[3] - xform[2] * xform[1]; return( det < 0); @@ -2470,8 +2470,7 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* float invscale = 1.0f / scale; int cverts = 0; int nverts = 0; - int isFlipped; - float tmp; + int isFlipped = nvg__isTransformFlipped(xform); if (end == NULL) end = string + strlen(string); @@ -2488,7 +2487,6 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* verts = nvg__allocTempVerts(ctx, cverts); if (verts == NULL) return x; - isFlipped = nvg__isTransformFlipped( state->xform); fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); prevIter = iter; while (fonsTextIterNext(ctx->fs, &iter, &q)) { @@ -2506,8 +2504,9 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* break; } prevIter = iter; - if( isFlipped) - { + if(isFlipped) { + float tmp; + tmp = q.y0; q.y0 = q.y1; q.y1 = tmp; tmp = q.t0; q.t0 = q.t1; q.t1 = tmp; } From 0bfd2c21ccf78e248cbcb5c3b5ad5c9d3be34e93 Mon Sep 17 00:00:00 2001 From: Nat! Date: Fri, 25 Dec 2020 16:20:30 +0100 Subject: [PATCH 19/19] New ci.yml. Build nanovg on every push on ubuntu. Does not build examples (yet), due to technical problems with the older ubuntu version being used by github. --- .github/workflows/ci.yml | 28 ++++++++++++++++++++++++++++ src/nanovg.c | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..04dac18 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: [push] + +env: + BUILD_TYPE: release + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest] + + steps: + - uses: actions/checkout@v2 + + - name: Install required packages + run: sudo apt-get install premake4 build-essential libglfw3-dev libglew-dev pkg-config + + - name: Premake + run: premake4 gmake + +# due to glew problems with in the current ubuntu-latest, we don't build the examples (yet) +# https://github.com/openai/mujoco-py/issues/383 has the same problem for reference +# this doesn't happen in focal + - name: Make + run: cd build && make nanovg diff --git a/src/nanovg.c b/src/nanovg.c index a94083f..5d0cb4c 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -2470,7 +2470,7 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* float invscale = 1.0f / scale; int cverts = 0; int nverts = 0; - int isFlipped = nvg__isTransformFlipped(xform); + int isFlipped = nvg__isTransformFlipped(state->xform); if (end == NULL) end = string + strlen(string);