summaryrefslogtreecommitdiff
path: root/sys/src/libttf/render.c
diff options
context:
space:
mode:
authoraiju <devnull@localhost>2018-06-09 14:33:19 +0000
committeraiju <devnull@localhost>2018-06-09 14:33:19 +0000
commitdb71e19005574388bdc368081848327a5c104e5a (patch)
treedb33f3a6c6aac49dd9d5bd5a04145b3acf1f0cb7 /sys/src/libttf/render.c
parent198f10bb25382882c4abd9081dac4dd74dbdbb9f (diff)
add libttf
Diffstat (limited to 'sys/src/libttf/render.c')
-rw-r--r--sys/src/libttf/render.c274
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);
+}