diff options
author | aiju <devnull@localhost> | 2018-06-09 14:33:19 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2018-06-09 14:33:19 +0000 |
commit | db71e19005574388bdc368081848327a5c104e5a (patch) | |
tree | db33f3a6c6aac49dd9d5bd5a04145b3acf1f0cb7 /sys/src/libttf/render.c | |
parent | 198f10bb25382882c4abd9081dac4dd74dbdbb9f (diff) |
add libttf
Diffstat (limited to 'sys/src/libttf/render.c')
-rw-r--r-- | sys/src/libttf/render.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/sys/src/libttf/render.c b/sys/src/libttf/render.c new file mode 100644 index 000000000..bea21f61f --- /dev/null +++ b/sys/src/libttf/render.c @@ -0,0 +1,274 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ttf.h> +#include "impl.h" + +static int +ttfparsekern(TTFontU *f) +{ + u16int ver, len, cov, ntab; + int i; + + if(ttfgototable(f, "kern") < 0) + return -1; + ttfunpack(f, "ww", &ver, &ntab); + if(ver != 0) + return -1; + if(ntab == 0) + return -1; + for(i = 0; i < ntab; i++){ + ttfunpack(f, "www", &ver, &len, &cov); + if((cov & 1) != 0) break; + Bseek(f->bin, len - 6, 1); + } + ttfunpack(f, "w6", &len); + f->nkern = len; + f->kern = mallocz(sizeof(TTKern) * len, 1); + for(i = 0; i < len; i++) + ttfunpack(f, "lS", &f->kern[i].idx, &f->kern[i].val); + return 0; +} + +static int +ttfkern(TTFont *f, int l, int r) +{ + u32int idx; + int a, b, c; + TTFontU *u; + + u = f->u; + if(u->nkern == 0) + return 0; + idx = l << 16 | r; + a = 0; + b = u->nkern - 1; + if(u->kern[a].idx > idx || u->kern[b].idx < idx) + return 0; + while(a <= b){ + c = (a + b) / 2; + if(u->kern[c].idx < idx){ + a = c + 1; + }else if(u->kern[c].idx > idx){ + b = c - 1; + }else + return ttfrounddiv(u->kern[c].val * f->ppem, u->emsize); + } + return 0; +} + +typedef struct { + TTBitmap *b; + TTFont *font; + TTGlyph **glyph; + int *gwidth; + char **cpos; + char *pp; + int nglyph, aglyph; + int linew; + int adj; + int nspc; + int oy, lh; + int spcidx; + int flags; + int spcw; + int spcminus; +} Render; + +static int +addglyph(Render *r, char *p, TTGlyph *g) +{ + void *v; + int k; + + if(r->nglyph >= r->aglyph){ + r->aglyph += 32; + v = realloc(r->glyph, sizeof(TTGlyph *) * r->aglyph); + if(v == nil) return -1; + r->glyph = v; + v = realloc(r->gwidth, sizeof(int) * r->aglyph); + if(v == nil) return -1; + r->gwidth = v; + v = realloc(r->cpos, sizeof(char *) * r->aglyph); + if(v == nil) return -1; + r->cpos = v; + } + r->glyph[r->nglyph] = g; + r->cpos[r->nglyph] = p; + r->gwidth[r->nglyph] = g->advanceWidthpx; + if(r->nglyph > 0){ + k = ttfkern(r->font, r->glyph[r->nglyph-1]->idx, g->idx); + r->gwidth[r->nglyph-1] += k; + r->linew += k; + } + r->nglyph++; + r->linew += r->gwidth[r->nglyph-1]; + if(g->idx == r->spcidx) + r->nspc++; + return 0; +} + +static void +flushglyphs(Render *r, int justify) +{ + int i, n, k, x, y; + int llen; + int adj, spcw, nspc, c; + TTFont *f; + + f = r->font; + if((r->flags & TTFMODE) == TTFLALIGN && !justify) + while(r->nglyph > 0 && r->glyph[r->nglyph - 1]->idx == r->spcidx){ + r->linew -= r->gwidth[--r->nglyph]; + r->nspc--; + } + llen = r->linew; + k = n = r->nglyph; + nspc = r->nspc; + adj = (nspc * r->spcminus + 63) / 64; + if(r->linew - adj > r->b->width){ + n = r->nglyph; + while(n > 0 && r->glyph[n - 1]->idx != r->spcidx) + llen -= r->gwidth[--n]; + k = n; + while(n > 0 && r->glyph[n - 1]->idx == r->spcidx){ + llen -= r->gwidth[--n]; + nspc--; + } + if(n == 0){ + while(n < r->nglyph && llen + r->gwidth[n] < r->b->width) + llen += r->gwidth[n++]; + k = n; + } + } + if(justify){ + if(nspc == 0) + spcw = 0; + else + spcw = (r->b->width - llen + nspc * r->spcw) * 64 / nspc; + }else + spcw = r->spcw * 64; + switch(r->flags & TTFMODE | justify * TTFJUSTIFY){ + case TTFRALIGN: + x = r->b->width - llen; + break; + case TTFCENTER: + x = (r->b->width - llen)/2; + break; + default: + x = 0; + } + y = r->oy + f->ascentpx; + c = 0; + for(i = 0; i < k; i++){ + if(r->glyph[i]->idx == r->spcidx){ + c += spcw; + x += c >> 6; + c &= 63; + r->nspc--; + }else{ + ttfblit(r->b, x + r->glyph[i]->xminpx, y - r->glyph[i]->ymaxpx, r->glyph[i], 0, 0, r->glyph[i]->width, r->glyph[i]->height); + x += r->gwidth[i]; + } + r->linew -= r->gwidth[i]; + ttfputglyph(r->glyph[i]); + } + if(n > 0) + r->pp = r->cpos[n-1]; + r->oy += r->lh; + memmove(r->glyph, r->glyph + k, (r->nglyph - k) * sizeof(TTGlyph *)); + memmove(r->cpos, r->cpos + k, (r->nglyph - k) * sizeof(char *)); + memmove(r->gwidth, r->gwidth + k, (r->nglyph - k) * sizeof(int)); + r->nglyph -= k; +} + +TTBitmap * +_ttfrender(TTFont *f, int (*getrune)(Rune *, char *), char *p, char *end, int w, int h, int flags, char **rp) +{ + Render r; + Rune ch; + int i, adj; + TTGlyph *g; + + if(rp != nil) *rp = p; + if(f->u->nkern < 0 && ttfparsekern(f->u) < 0) + f->u->nkern = 0; + memset(&r, 0, sizeof(Render)); + r.flags = flags; + r.font = f; + r.b = ttfnewbitmap(w, h); + if(r.b == nil) goto error; + r.oy = 0; + r.lh = f->ascentpx + f->descentpx; + r.pp = p; + + g = ttfgetglyph(f, ttffindchar(f, ' '), 1); + r.spcidx = g->idx; + r.spcw = g->advanceWidthpx; + if((flags & TTFJUSTIFY) != 0) + r.spcminus = r.spcw * 21; + else + r.spcminus = 0; + + while(p < end && r.oy + r.lh < h){ + p += getrune(&ch, p); + if(ch == '\n'){ + flushglyphs(&r, 0); + continue; + } + g = ttfgetglyph(f, ttffindchar(f, ch), 1); + if(g == nil){ + g = ttfgetglyph(f, 0, 1); + if(g == nil) + continue; + } + if(addglyph(&r, p, g) < 0) + goto error; + adj = (r.nspc * r.spcminus + 63) / 64; + if(r.linew - adj > r.b->width){ + flushglyphs(&r, (flags & TTFJUSTIFY) != 0); + } + } + if(r.oy + r.lh < h) + flushglyphs(&r, 0); + for(i = 0; i < r.nglyph; i++) + ttfputglyph(r.glyph[i]); + free(r.glyph); + free(r.gwidth); + free(r.cpos); + if(rp != nil) + *rp = r.pp; + return r.b; +error: + ttffreebitmap(r.b); + free(r.glyph); + free(r.gwidth); + return nil; +} + +TTBitmap * +ttfrender(TTFont *f, char *str, char *end, int w, int h, int flags, char **rstr) +{ + if(str == nil) + end = nil; + else if(end == nil) + end = str + strlen(str); + return _ttfrender(f, chartorune, str, end, w, h, flags, rstr); +} + +static int +incrune(Rune *r, char *s) +{ + *r = *(Rune*)s; + return sizeof(Rune); +} + +TTBitmap * +ttfrunerender(TTFont *f, Rune *str, Rune *end, int w, int h, int flags, Rune **rstr) +{ + if(str == nil) + end = nil; + else if(end == nil) + end = str + runestrlen(str); + return _ttfrender(f, incrune, (char *) str, (char *) end, w, h, flags, (char **) rstr); +} |