Commit 72a99ba0 authored by bzt's avatar bzt

Many fixes and proper font reading in sfnedit

parent 18dff817
......@@ -233,7 +233,7 @@ Parameter defines:
| `SSFN_MODE_OUTLINE` | return the glyph's outline |
| `SSFN_MODE_BITMAP` | rasterize glyph as bitmap, 1 bit for each pixel |
| `SSFN_MODE_ALPHA` | rasterize glyph as an alpha channel, 1 byte for each pixel |
| `SSFN_MODE_CMAP` | rasterize in color map mode, 1 byte for each pixel (see `SSFN_CMAP_TO_RGBA()` macro) |
| `SSFN_MODE_CMAP` | rasterize in color map mode, 1 byte for each pixel (see `SSFN_CMAP_TO_ARGB()` macro) |
### Return value
......
......@@ -58,7 +58,7 @@ The FreeSerif font with a decent Basic Multilingual Plane coverage from the GNU
| Format | Quality | Size | Gzipped | Grid size | Compression |
| ------ | ------: | -----: | ------: | ----------: | ----------: |
| .otf | - | 2.0 M | 1.3 M | 1000 x 1000 | 0% |
| .sfn | 6 | 1.1 M | 766 K | 1024 x 1024 | -44.9% |
| .sfn | 6 | 1.1 M | 766 K | 1024 x 1024 | -45.0% |
| .sfn | 3 | 831 K | 492 K | 128 x 128 | -59.4% |
For most vector fonts, quality 4 (256 x 256 grid) is perfectly sufficient. Some simpler fonts can be
......
......@@ -29,12 +29,12 @@ Color Map Mode
Finally, if mode is `SSFN_MODE_CMAP`, then data contains one byte for each pixel. There values 0-239 select a
color from the color map provided in `cmap`. Values 240-255 encode a 16 steps alpha channel, where 240 means
transparency (alpha 0, background) and 255 means opaque (alpha 15, foreground). The color map contains 4 bytes
for each index, A,R,G,B values in order. You can decode this information with the `SSFN_CMAP_TO_RGBA()` macro,
for each index, A,R,G,B values in order. You can decode this information with the `SSFN_CMAP_TO_ARGB()` macro,
which receives three arguments: a cmap mode pixel, pointer to the color map and a foreground color.
__Example Code__
Here's a simple code on how to decode returned rasterized glyph on an SDL RGBA surface for example:
Here's a simple code on how to decode returned rasterized glyph on an SDL ARGB surface for example:
```c
#define SDL_PIXEL ((uint32_t*)(surface->pixels))[(pen_y + y) * surface->pitch/4 + (pen_x + x)]
......@@ -80,7 +80,7 @@ void my_draw_glyph(SDL_Surface *surface, ssfn_glyph_t *glyph, int pen_x, int pen
case SSFN_MODE_CMAP:
for(y = 0; y < glyph->h; y++)
for(x = 0; x < glyph->w; x++)
SDL_PIXEL = SSFN_CMAP_TO_RGBA(glyph->data[y * glyph->pitch + x], glyph->cmap, fgcolor);
SDL_PIXEL = SSFN_CMAP_TO_ARGB(glyph->data[y * glyph->pitch + x], glyph->cmap, fgcolor);
break;
}
}
......
docs/sfnedit1.png

13.1 KB | W: | H:

docs/sfnedit1.png

13 KB | W: | H:

docs/sfnedit1.png
docs/sfnedit1.png
docs/sfnedit1.png
docs/sfnedit1.png
  • 2-up
  • Swipe
  • Onion skin
docs/sfnedit2.png

8.3 KB | W: | H:

docs/sfnedit2.png

12.4 KB | W: | H:

docs/sfnedit2.png
docs/sfnedit2.png
docs/sfnedit2.png
docs/sfnedit2.png
  • 2-up
  • Swipe
  • Onion skin
docs/sfnedit3.png

11.4 KB | W: | H:

docs/sfnedit3.png

13.4 KB | W: | H:

docs/sfnedit3.png
docs/sfnedit3.png
docs/sfnedit3.png
docs/sfnedit3.png
  • 2-up
  • Swipe
  • Onion skin
features.png

35 KB | W: | H:

features.png

34.9 KB | W: | H:

features.png
features.png
features.png
features.png
  • 2-up
  • Swipe
  • Onion skin
This diff is collapsed.
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
......@@ -112,7 +112,7 @@ ssfn_font_t ssfn_hdr;
int replace=0, hinting=0, zip=0, last = -1, numchars = 0, relunderline = 0, haskern = 0, adv = 0;
char *arg_name=NULL, *arg_family=NULL, *arg_subfamily=NULL, *arg_ver=NULL;
char *arg_manufacturer=NULL, *arg_license=NULL;
unsigned char cpal[960];
unsigned char cpal[960], cpalidx[241], cpalrev[241];
int unicode, rs = 0, re = 0x10FFFF, kgrpnum = 0;
int frag_len = 0, frag_mx, frag_my, frag_ix, frag_iy, frag_ax, frag_ay, min_y, max_y, min_x, max_x;
int lx, ly, gx, gy, bx, by;
......@@ -155,6 +155,22 @@ int frgsrt(const void *a, const void *b)
return frags[((int*)a)[2]].prod - frags[((int*)b)[2]].prod;
}
/**
* Sort color map
*/
int cpalsrt(const void *a, const void *b)
{
/* zero means undefined color, send to back */
if(!cpal[*((uint8_t*)a)*4+0] && !cpal[*((uint8_t*)a)*4+1] && !cpal[*((uint8_t*)a)*4+2]) return 1;
if(!cpal[*((uint8_t*)b)*4+0] && !cpal[*((uint8_t*)b)*4+1] && !cpal[*((uint8_t*)b)*4+2]) return -1;
/* green is most significant, alpha is the least */
return
((((int)cpal[*((uint8_t*)a)*4+1]<<3) + ((int)cpal[*((uint8_t*)a)*4+0]<<1) +
((int)cpal[*((uint8_t*)a)*4+2]<<1) + (int)cpal[*((uint8_t*)a)*4+3])>>2) -
((((int)cpal[*((uint8_t*)b)*4+1]<<3) + ((int)cpal[*((uint8_t*)b)*4+0]<<1) +
((int)cpal[*((uint8_t*)b)*4+2]<<1) + (int)cpal[*((uint8_t*)b)*4+3])>>2);
}
/**
* Convert hex string to binary number. Use this lightning fast implementation
* instead of the unbeliveably crap, slower than a pregnant snail sscanf...
......@@ -206,7 +222,7 @@ int iszero(unsigned char *ptr, int chr, int len)
*/
unsigned char cpal_add(int r, int g, int b, int a)
{
int i, dr, dg, db, da, m, dm, q;
int i, dr, dg, db, m, dm, q;
if(!r && !g && !b && (!a || a==0xFF)) return 240;
for(q=1; q<=8; q++) {
......@@ -214,11 +230,9 @@ unsigned char cpal_add(int r, int g, int b, int a)
for(i=0; i<240 && (cpal[i*4+0] || cpal[i*4+1] || cpal[i*4+2]); i++) {
if(a==cpal[i*4+3] && b==cpal[i*4+0] && g==cpal[i*4+1] && r==cpal[i*4+2]) return i;
if(b>>q==cpal[i*4+0]>>q && g>>q==cpal[i*4+1]>>q && r>>q==cpal[i*4+2]>>q) {
da = a > cpal[i*4+3] ? a - cpal[i*4+3] : cpal[i*4+3] - a;
dr = r > cpal[i*4+2] ? r - cpal[i*4+2] : cpal[i*4+2] - r;
dg = g > cpal[i*4+1] ? g - cpal[i*4+1] : cpal[i*4+1] - g;
db = b > cpal[i*4+0] ? b - cpal[i*4+0] : cpal[i*4+0] - b;
if(da > dr) dr = da;
if(dg > dr) dr = dg;
if(db > dr) dr = db;
if(dr < dm) { dm = dr; m = i; }
......@@ -1028,17 +1042,18 @@ void asc(char *ptr, int size)
memset(bitmap, 0, 129*128);
i = h = 0;
while(*ptr && *ptr!='+') {
for(l = w = 0;*ptr && *ptr!='\r' && *ptr!='\n';l++,i++,ptr++) {
for(l = w = 0;*ptr && *ptr!='\r' && *ptr!='\n';l++,i++) {
if(t=='%') {
if(*ptr==' ') ptr++;
if(ptr[0]=='.') { bitmap[i] = 0xF0; while(*ptr && *ptr=='.') ptr++; }
else {
bitmap[i] = cpal_add(gethex(ptr, 2), gethex(ptr+2, 2), gethex(ptr+4,2), gethex(ptr+6,2));
ptr += 7;
bitmap[i] = cpal_add(gethex(ptr+2, 2), gethex(ptr+4, 2), gethex(ptr+6,2), gethex(ptr,2));
ptr += 8;
}
if(*ptr==' ') ptr++;
} else {
bitmap[i>>3] >>= 1;
if(*ptr!=' ' && *ptr!='.' && *ptr!='+') bitmap[i>>3] |= 0x80;
ptr++;
}
}
while(*ptr=='\r' || *ptr=='\n') ptr++;
......@@ -1089,12 +1104,11 @@ void asc(char *ptr, int size)
continue;
} else {
for(ptr+=2,p=0;*ptr && *ptr!='\n' && *ptr!='+' && p<6;ptr++) {
if(*ptr=='#' || t=='c') {
if(*ptr=='#') ptr++;
par[p++] = gethex(ptr, 2);
if(cmd=='c') {
par[p++] = gethex(ptr+2, 2);
par[p++] = gethex(ptr+4, 2);
if(t=='c') par[p++] = gethex(ptr+6, 2); else par[p++] = 0xFF;
par[p++] = gethex(ptr+6, 2);
par[p++] = gethex(ptr, 2);
} else
par[p++] = atoi(ptr);
while(*ptr!=' ' && *ptr!=',' && ptr[1] && ptr[1]!='\n' && ptr[1]!='+') ptr++;
......@@ -1153,6 +1167,8 @@ void asc(char *ptr, int size)
}
if(ssfn_hdr.bbox_left > min_x) ssfn_hdr.bbox_left = min_x;
if(ssfn_hdr.bbox_top > min_y) ssfn_hdr.bbox_top = min_y;
if(x > max_x) max_x = x;
if(y > max_y) max_y = y;
c->w = max_x; if(ssfn_hdr.bbox_right < max_x) ssfn_hdr.bbox_right = max_x;
c->h = max_y; if(ssfn_hdr.bbox_bottom < max_y) ssfn_hdr.bbox_bottom = max_y;
if(hinting) {
......@@ -1204,7 +1220,7 @@ void asc(char *ptr, int size)
while(*ptr && *ptr!='\n') ptr++;
while(*ptr=='\n') ptr++;
if(t=='K') {
while(ptr < end && *ptr && *ptr!='\n' && (*ptr!='+' || ptr[1]==':')) {
while(ptr < end && *ptr && *ptr!='\n' && ptr[1]==':') {
unicode = getutf8(&ptr);
if(!unicode || unicode==0xA || unicode==0x20 || ptr[0]!=':') break;
for(i=-1,j=0;j<(int)ssfn_hdr.characters_num;j++)
......@@ -1517,6 +1533,12 @@ int main(int argc, char **argv)
}
} while(l);
if(ssfn_hdr.features & SSFN_FEAT_HASCMAP) {
for(i=0;i<241;i++) cpalidx[i] = i;
qsort(cpalidx, 240, 1, cpalsrt);
for(i=0;i<241;i++) cpalrev[cpalidx[i]]=i;
}
for(fs=0,i=k=0;i<(int)ssfn_hdr.fragments_num;i++) {
if(frags[i].cnt<1 || frags[i].h<1) continue;
frags[i].pos = ssfn_hdr.fragments_offs + fs;
......@@ -1561,13 +1583,13 @@ int main(int argc, char **argv)
m--;
if(frg[n]) { frg[n]--; frg[fs++] = 0x80 | m; }
else frg[n] = 0x80 | m;
frg[fs++] = frags[i].data[j];
frg[fs++] = cpalrev[frags[i].data[j]];
n = fs; frg[fs++] = 0;
j += m;
continue;
}
frg[n]++;
frg[fs++] = frags[i].data[j];
frg[fs++] = cpalrev[frags[i].data[j]];
if(frg[n] > 127) { frg[n]--; n = fs; frg[fs++] = 0; }
}
if(!(frg[n] & 0x80)) { if(frg[n]) frg[n]--; else fs--; }
......@@ -2040,7 +2062,8 @@ int main(int argc, char **argv)
gzwrite(g, kern, ks);
gzwrite(g, kgrp, kg);
if(ssfn_hdr.features & SSFN_FEAT_HASCMAP)
gzwrite(g, cpal, 960);
for(i=0;i<240;i++)
gzwrite(g, cpal + cpalidx[i]*4, 4);
gzwrite(g, SSFN_ENDMAGIC, 4);
gzclose(g);
f = fopen(outfile, "rb");
......@@ -2062,7 +2085,8 @@ int main(int argc, char **argv)
fwrite(kern, ks, 1, f);
fwrite(kgrp, kg, 1, f);
if(ssfn_hdr.features & SSFN_FEAT_HASCMAP)
fwrite(cpal, 960, 1, f);
for(i=0;i<240;i++)
fwrite(cpal + cpalidx[i]*4, 4, 1, f);
fwrite(SSFN_ENDMAGIC, 4, 1, f);
fclose(f);
s = ssfn_hdr.size;
......
......@@ -973,7 +973,7 @@ int main(int argc, char **argv)
dy = 0;
} else
fprintf(f,"\n+!---%d-%d-%d", i, x, y);
fprintf(f,"---U+%04X-", i);
fprintf(f,"---U+%06X-", i);
if(i>=32) fprintf(f, "'%s'", utf8(i));
if(!i) fprintf(f,"-'UNDEF'---\n");
else if(uninames && uninames[i]) fprintf(f,"-'%s'---\n", uninames[i]);
......
TARGET = sfnedit
SRCS = $(filter-out tinflate.c $(wildcard ui_*.c),$(wildcard *.c))
ifneq ("$(wildcard ../fonts/unifont.sfn.gz)","")
FONT = ../fonts/unifont.sfn.gz
else
ifneq ("$(wildcard unifont.sfn.gz)","")
FONT = unifont.sfn.gz
else
FONT = ../fonts/unifont.sfn.gz
endif
ifneq ("$(wildcard ../../Config)","")
include ../../Config
......
......@@ -41,9 +41,10 @@
extern char gz;
ssfn_font_t *font = NULL;
char *fontfile = NULL, *fontfilebn = NULL, gzipped = 0;
char *fontfile = NULL, *fontfilebn = NULL, gzipped = 0, hascmap = 0;
char *strtable[6];
uint32_t cmap[240];
uint8_t *cpal = (uint8_t*)&cmap, cpalidx[241], cpalrev[241];
int modified = 0;
int numchars = 0;
......@@ -62,6 +63,50 @@ int file_chrsort(const void *a, const void *b)
return ((char_t*)a)->unicode - ((char_t*)b)->unicode;
}
/**
* Sort color map entries
*/
int file_cpalsrt(const void *a, const void *b)
{
/* zero means undefined color, send to back */
if(!cpal[*((uint8_t*)a)*4+0] && !cpal[*((uint8_t*)a)*4+1] && !cpal[*((uint8_t*)a)*4+2]) return 1;
if(!cpal[*((uint8_t*)b)*4+0] && !cpal[*((uint8_t*)b)*4+1] && !cpal[*((uint8_t*)b)*4+2]) return -1;
/* green is most significant, alpha is the least */
return
((((int)cpal[*((uint8_t*)a)*4+1]<<3) + ((int)cpal[*((uint8_t*)a)*4+0]<<1) +
((int)cpal[*((uint8_t*)a)*4+2]<<1) + (int)cpal[*((uint8_t*)a)*4+3])>>2) -
((((int)cpal[*((uint8_t*)b)*4+1]<<3) + ((int)cpal[*((uint8_t*)b)*4+0]<<1) +
((int)cpal[*((uint8_t*)b)*4+2]<<1) + (int)cpal[*((uint8_t*)b)*4+3])>>2);
}
/**
* Reorder color map and replace index references
*/
void file_sortcmap()
{
int i, j, s = 16 << font->quality;
uint32_t cmap2[240];
for(i=0;i<241;i++) cpalidx[i] = i;
qsort(cpalidx, 240, 1, file_cpalsrt);
for(i=0;i<241;i++) cpalrev[cpalidx[i]]=i;
memcpy(cmap2, cmap, sizeof(cmap));
for(i=0;i<240;i++) cmap[i] = cmap2[cpalidx[i]];
for(i=0;i<numfrags;i++) {
if(frags[i].type == SSFN_FRAG_CONTOUR) {
for(j=0;j<frags[i].len;j++) {
if(frags[i].data[j].type == SSFN_CONTOUR_COLOR)
frags[i].data[j].px = cpalrev[frags[i].data[j].px];
}
}
if(frags[i].type == SSFN_FRAG_PIXMAP) {
for(j=0;j<s*s;j++)
((uint8_t*)frags[i].data)[j] = cpalrev[((uint8_t*)frags[i].data)[j]];
}
}
}
/**
* Return a character for UNICODE
*/
......@@ -319,12 +364,13 @@ bitmap: for(i=0,l=-1;i<chr->len;i++)
/**
* Load a font file
*/
#define findandskip(x) do{while(ptr < end && *ptr!=x){ptr++;};while(ptr < end && *ptr==x){ptr++;};}while(0)
void file_load(char *filename)
{
unsigned int size;
int i, j, k, l, n, x, y, L, T;
int i, j, k, l, n, x, y, L, T, par[6], p, s;
char *str;
uint8_t *ptr, *end, *e;
uint8_t *ptr, *end, *e, *bitmap, cmd;
char_t *c;
fontfile = filename;
......@@ -332,7 +378,9 @@ void file_load(char *filename)
ptr = load_file(fontfile, (int*)&size);
if(!ptr || !size) error("file_load", ERR_LOAD, filename);
gzipped = gz;
memset(cmap, 0, sizeof(cmap));
/* if it's an SSFN ASCII */
if(!memcmp((char*)ptr, "## S", 4)) {
font = (ssfn_font_t*)malloc(sizeof(ssfn_font_t));
if(!font) error("file_load", ERR_MEM);
......@@ -340,6 +388,7 @@ void file_load(char *filename)
memcpy(font->magic, SSFN_MAGIC, 4);
font->bbox_top = font->bbox_left = 65535;
end = ptr + size;
/* parse header */
while(ptr < end && *ptr) {
if(*ptr=='+') {
if(ptr[1]=='#') {
......@@ -356,8 +405,7 @@ void file_load(char *filename)
} else
if(ptr[1]=='$') {
k = ptr[2];
while(*ptr && *ptr!=' ') ptr++;
while(*ptr==' ') ptr++;
findandskip(' ');
for(e=ptr;*e && *e!='\r' && *e!='\n';e++);
str = malloc(256);
if(!str) error("file_load", ERR_MEM);
......@@ -377,65 +425,249 @@ void file_load(char *filename)
}
ptr++;
}
return;
}
s = 16 << font->quality;
/* parse glyphs */
while(ptr < end) {
while(ptr < end && !(ptr[0]=='+' && (ptr[1]=='+' || ptr[1]=='=' || ptr[1]=='%' || ptr[1]=='!' || ptr[1]=='@') &&
ptr[2]=='-' && ptr[3]=='-')) ptr++;
if(ptr[0]!='+') break;
if(ptr[1]=='@') { if(ptr[5]=='K' || ptr[5]=='E') break; else { ptr++; continue; } }
x = y = -1;
cmd = ptr[1];
ptr += 5;
j = atoi((char*)ptr);
findandskip('-');
if(cmd!='+') {
x = atoi((char*)ptr);
findandskip('-');
y = atoi((char*)ptr);
}
findandskip('\n');
k = file_addchar(j, 0);
if(k < 0) continue;
c = &chars[k];
if(cmd!='!') {
i = c->len; c->len++;
c->frags = (int*)realloc(c->frags, (c->len)*sizeof(int));
if(!c->frags) error("file_load", ERR_MEM);
c->frags[i] = file_fragadd(cmd=='%' ? SSFN_FRAG_PIXMAP : SSFN_FRAG_BITMAP);
bitmap = (uint8_t*)frags[c->frags[i]].data;
j = 0; n = s; if(cmd!='%') n >>= 3;
while(ptr < end && *ptr!='+') {
for(i=0;ptr < end && *ptr!='\r' && *ptr!='\n';i++) {
if(cmd == '%') {
if(*ptr=='.') { while(ptr < end && *ptr=='.') ptr++; }
else {
if(j < s && i < s)
bitmap[j*s + i] = cpal_add(
gethex((char*)ptr+2, 2), gethex((char*)ptr+4, 2),
gethex((char*)ptr+6,2), gethex((char*)ptr,2));
ptr += 8;
}
if(*ptr==' ') ptr++;
} else {
if(*ptr!=' ' && *ptr!='.' && *ptr!='+' && j < s && i < s)
bitmap[j*n + (i>>3)] |= 1 << (i & 7);
ptr++;
}
}
j++;
while(*ptr=='\r' || *ptr=='\n') ptr++;
}
} else {
while(ptr < end && *ptr!='+') {
cmd = *ptr; par[0]=par[1]=par[2]=par[3]=par[4]=par[5]=0;
if(cmd == 'H' || cmd == 'V') {
findandskip('\n');
continue;
} else {
for(ptr+=2,p=0;ptr < end && *ptr!='\n' && *ptr!='+' && p<6;ptr++) {
if(cmd=='c') {
par[p++] = cpal_add(
gethex((char*)ptr+2, 2), gethex((char*)ptr+4, 2),
gethex((char*)ptr+6,2), gethex((char*)ptr,2));
} else
par[p++] = atoi((char*)ptr);
while(*ptr!=' ' && *ptr!=',' && ptr[1] && ptr[1]!='\n' && ptr[1]!='+') ptr++;
}
}
while(*ptr=='\n') ptr++;
switch(cmd) {
case 'm':
if(p<2) { fprintf(stderr,"Too few move arguments for U+%06X\n",j); exit(3); }
i = c->len; c->len++;
c->frags = (int*)realloc(c->frags, (c->len)*sizeof(int));
if(!c->frags) error("file_load", ERR_MEM);
c->frags[i] = file_fragadd(SSFN_FRAG_CONTOUR);
file_contadd(c->frags[i], SSFN_CONTOUR_MOVE, par[0],par[1], 0,0, 0,0);
break;
case 'l':
if(p<2) { fprintf(stderr,"Too few line arguments for U+%06X\n",j); exit(3); }
file_contadd(c->frags[i], SSFN_CONTOUR_LINE, par[0],par[1], 0,0, 0,0);
break;
case 'q':
if(p<4) { fprintf(stderr,"Too few quadratic curve arguments for U+%06X\n",j); exit(3); }
file_contadd(c->frags[i], SSFN_CONTOUR_QUAD, par[0],par[1], par[2],par[3], 0,0);
break;
case 'b':
if(p<6) { fprintf(stderr,"Too few bezier curve arguments for U+%06X\n",j); exit(3); }
file_contadd(c->frags[i], SSFN_CONTOUR_QUAD, par[0],par[1], par[2],par[3], par[4],par[5]);
break;
case 'c':
if(p<1) { fprintf(stderr,"Too few color arguments for U+%06X\n",j); exit(3); }
file_contadd(c->frags[i], SSFN_CONTOUR_COLOR, par[0],0, 0,0, 0,0);
break;
}
}
}
}
font = (ssfn_font_t*)ptr;
if(memcmp(font->magic, SSFN_MAGIC, 4) || size != font->size ||
memcmp((uint8_t*)font + font->size - 4, SSFN_ENDMAGIC, 4) ||
font->family > SSFN_FAMILY_HAND || font->fragments_offs > font->size || font->characters_offs > font->size ||
font->kerning_offs > font->size || font->fragments_offs > font->characters_offs) {
error("file_load", ERR_BADFILE, filename);
}
if(font->features & SSFN_FEAT_HASCMAP)
memcpy(cmap, (uint8_t*)font + font->size - sizeof(cmap) - 4, sizeof(cmap));
else
memset(cmap, 0, sizeof(cmap));
for(i = 0, str = (char*)font + sizeof(ssfn_font_t); i < 6; i++) {
strtable[i] = (char*)malloc(256);
if(!strtable[i]) error("file_load", ERR_MEM);
memcpy(strtable[i], str, strlen(str) + 1);
str += strlen(str) + 1;
}
ptr = (uint8_t*)font + font->characters_offs;
end = (uint8_t*)font + font->size;
l = (font->quality < 5 && font->characters_offs < 65536) ? 4 : (font->characters_offs < 1048576 ? 5 : 6);
savingmax = font->characters_num;
for(j=i=0;i<0x110000 && j<(int)font->characters_num && ptr < end;i++) {
if(ptr[0] & 0x80) {
if(ptr[0] & 0x40) { i += ptr[1] | ((ptr[0] & 0x3f) << 8); ptr += 2; }
else { i += ptr[0] & 0x3f; ptr++; }
} else {
k =file_addchar(i, 0);
if(k != -1) {
c = &chars[k];
n = (ptr[0] & 0x7F);
c->adv_x = ((ptr[2]&0x0F)<<8)|ptr[6];
c->adv_y = ((ptr[2]&0xF0)<<4)|ptr[7];
L = ((ptr[3]&0x0F)<<8)|ptr[8];
T = ((ptr[3]&0xF0)<<4)|ptr[9];
ptr += 10;
for(;n--;ptr += l) {
x = (((ptr[1] >> 4) & 3) << 8) | ptr[4];
y = (((ptr[1] >> 6) & 3) << 8) | ptr[5];
switch(l) {
case 4: k=(ptr[1] << 8) | ptr[0]; x = ptr[2]; y = ptr[3]; break;
case 5: k=((ptr[2] & 0xF) << 16) | (ptr[1] << 8) | ptr[0];
x = (((ptr[2] >> 4) & 3) << 8) | ptr[3]; y = (((ptr[2] >> 6) & 3) << 8) | ptr[4]; break;
default: k=(ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
x = ((ptr[3] & 0xF) << 8) | ptr[4]; y = (((ptr[3] >> 4) & 0xF) << 8) | ptr[5]; break;
/* parse kerning (if any) */
while(ptr < end) {
while(ptr < end && !(ptr[0]=='+' && ptr[1]=='@' && ptr[2]=='-' && ptr[3]=='-')) ptr++;
cmd = ptr[5];
findandskip('\n');
if(cmd == 'K') {
while(ptr < end && *ptr!='\n' && ptr[1]==':') {
j = getutf8((char**)&ptr);
if(!j || j == 0xA || j == 0x20 || ptr[0]!=':') break;
i = file_findchar(j);
if(i == -1) break;
ptr += 2;
while(ptr < end && *ptr!='\n') {
while(*ptr == ' ') ptr++;
x = getutf8((char**)&ptr);
ptr++;
l = atoi((char*)ptr);
while(ptr < end && *ptr!='v' && *ptr!='h') ptr++;
if(chars[i].klen > 32767)
fprintf(stderr,"Too many kerning pairs for U+%06x, truncated to 32768\n", chars[i].unicode);
else if(l) {
for(k=j=0;k<chars[i].klen;k+=3) {
if(chars[i].kern[k] == x) { j=1; break; }
}
if(!j) {
k = chars[i].klen*3;
chars[i].klen++;
chars[i].kern = (int*)realloc(chars[i].kern, (k+3)*sizeof(int));
if(!chars[i].kern) error("file_load", ERR_MEM);
chars[i].kern[k+0] = x;
chars[i].kern[k+1] = chars[i].kern[k+2] = 0;
}
chars[i].kern[k + (*ptr != 'v'? 1 : 2)] = l;
}
ptr++;
if(*ptr == ',') ptr++;
}
file_addrawfrag(c, (uint8_t*)font + k, L+x, T+y);
if(*ptr == '\n') ptr++;
}
} else
ptr += ptr[0] * l + 10;
j++;
}
if(cmd == 'E') break;