mirror of https://github.com/ocornut/imgui
stb_truetype update (with OpenType, Type 2 font handling) (#976)
This commit is contained in:
parent
b8043d3ee5
commit
8e8117c7b1
765
stb_truetype.h
765
stb_truetype.h
|
@ -1,4 +1,4 @@
|
|||
// stb_truetype.h - v1.12 - public domain
|
||||
// stb_truetype.h - v1.14 - public domain
|
||||
// authored from 2009-2016 by Sean Barrett / RAD Game Tools
|
||||
//
|
||||
// This library processes TrueType files:
|
||||
|
@ -20,10 +20,12 @@
|
|||
//
|
||||
// Mikko Mononen: compound shape support, more cmap formats
|
||||
// Tor Andersson: kerning, subpixel rendering
|
||||
// Dougall Johnson: OpenType / Type 2 font handling
|
||||
//
|
||||
// Misc other:
|
||||
// Ryan Gordon
|
||||
// Simon Glass
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bug/warning reports/fixes:
|
||||
// "Zer" on mollyrocket (with fix)
|
||||
|
@ -51,6 +53,7 @@
|
|||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts, num-fonts-in-TTC function
|
||||
// 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
|
||||
|
@ -95,7 +98,8 @@
|
|||
//
|
||||
// "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
|
||||
|
@ -453,6 +457,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
|
||||
|
@ -522,7 +534,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
|
||||
|
@ -621,14 +633,19 @@ 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 stack or as a global or etc, but you should treat it as opaque.
|
||||
|
@ -643,6 +660,13 @@ struct stbtt_fontinfo
|
|||
int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
|
||||
int index_map; // a cmap mapping for our chosen character encoding
|
||||
int indexToLocFormat; // format needed to map from glyph index to glyph
|
||||
|
||||
stbtt__buf cff; // cff font data
|
||||
stbtt__buf charstrings; // the charstring index
|
||||
stbtt__buf gsubrs; // global charstring subroutines index
|
||||
stbtt__buf subrs; // private charstring subroutines index
|
||||
stbtt__buf fontdicts; // array of font dicts
|
||||
stbtt__buf fdselect; // map from glyph to fontdict
|
||||
};
|
||||
|
||||
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
|
||||
|
@ -720,7 +744,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
|
||||
|
||||
|
@ -729,7 +754,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
|
||||
|
@ -951,6 +976,152 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS
|
|||
#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
|
||||
|
@ -978,6 +1149,7 @@ static int stbtt__isfont(stbtt_uint8 *font)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1014,6 +1186,35 @@ static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
|
||||
{
|
||||
// if it's just a font, there's only one valid font
|
||||
if (stbtt__isfont(font_collection))
|
||||
return 1;
|
||||
|
||||
// check if it's a TTC
|
||||
if (stbtt_tag(font_collection, "ttcf")) {
|
||||
// version 1?
|
||||
if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
|
||||
return ttLONG(font_collection+8);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
|
||||
{
|
||||
stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
|
||||
stbtt__buf pdict;
|
||||
stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
|
||||
if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
|
||||
pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
|
||||
stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
|
||||
if (!subrsoff) return stbtt__new_buf(NULL, 0);
|
||||
stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
|
||||
return stbtt__cff_get_index(&cff);
|
||||
}
|
||||
|
||||
static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
|
||||
{
|
||||
stbtt_uint32 cmap, t;
|
||||
|
@ -1021,6 +1222,7 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
|
|||
|
||||
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
|
||||
|
@ -1029,8 +1231,61 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
|
|||
info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
|
||||
info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
|
||||
info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
|
||||
if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
|
||||
|
||||
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)
|
||||
|
@ -1181,6 +1436,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
|
||||
|
||||
|
@ -1195,15 +1452,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;
|
||||
}
|
||||
|
||||
|
@ -1215,7 +1478,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;
|
||||
|
@ -1237,7 +1503,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;
|
||||
|
@ -1463,6 +1729,416 @@ 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__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;
|
||||
*y0 = r ? c.min_y : 0;
|
||||
*x1 = r ? c.max_x : 0;
|
||||
*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);
|
||||
|
@ -2333,6 +3009,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)
|
||||
{
|
||||
|
@ -2389,6 +3107,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;
|
||||
|
@ -3214,6 +3940,11 @@ 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);
|
||||
|
|
Loading…
Reference in New Issue