ref: 1c7e58e75bc6b5620984f164d66354350b28dfe0
dir: /sys/src/libttf/glyf.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <draw.h> #include <ctype.h> #include <ttf.h> #include "impl.h" void ttfputglyph(TTGlyph *g) { if(g == nil) return; free(g->pt); free(g->ptorg); free(g->confst); free(g->bit); free(g->hint); free(g); } static void glyphscale(TTGlyph *g) { TTFont *f; int i; TTPoint *p; f = g->font; for(i = 0; i < g->npt; i++){ p = &g->pt[i]; p->x = ttfrounddiv(p->x * f->ppem * 64, f->u->emsize); p->y = ttfrounddiv(p->y * f->ppem * 64, f->u->emsize); } memmove(g->ptorg, g->pt, sizeof(TTPoint) * g->npt); g->pt[g->npt - 1].x = g->pt[g->npt - 1].x + 32 & -64; } static TTGlyph * emptyglyph(TTFont *fs, int glyph, int render) { TTGlyph *g; g = mallocz(sizeof(TTGlyph), 1); if(g == nil) return nil; g->font = fs; g->info = &fs->u->ginfo[glyph]; g->confst = malloc(sizeof(int)); g->npt = 2; g->pt = mallocz(sizeof(TTPoint) * 2, 1); g->ptorg = mallocz(sizeof(TTPoint) * 2, 1); if(g->confst == nil || g->pt == nil || g->ptorg == nil){ ttfputglyph(g); return nil; } g->pt[1].x = g->info->advanceWidth; g->npt = 2; if(render) glyphscale(g); g->xmin = 0; g->ymin = 0; g->xmax = g->info->advanceWidth; g->ymax = 1; if(render){ g->xminpx = 0; g->xmaxpx = (g->xmax * fs->ppem + fs->u->emsize - 1) / fs->u->emsize; g->yminpx = 0; g->ymaxpx = 1; } return g; } static TTGlyph * simpglyph(TTFont *fs, int glyph, int nc, int render) { u16int np; short x; u16int len; u16int temp16; u8int temp8; u8int *flags, *fp, *fq; TTPoint *p; int i, j, r; short lastx, lasty; TTFontU *f; TTGlyph *g; flags = nil; f = fs->u; g = mallocz(sizeof(TTGlyph), 1); if(g == nil) return nil; g->font = fs; g->info = &f->ginfo[glyph]; g->confst = malloc(sizeof(u16int) * (nc + 1)); if(g->confst == nil) goto err; x = -1; for(i = g->ncon; i < nc; i++){ g->confst[i] = x + 1; ttfunpack(f, "w", &x); } g->confst[i] = x + 1; g->ncon = nc; np = x + 1; ttfunpack(f, "w", &len); g->nhint = len; g->hint = mallocz(len, 1); if(g->hint == nil) goto err; Bread(f->bin, g->hint, len); flags = mallocz(np, 1); if(flags == nil) goto err; for(i = 0; i < np; i++){ j = Bgetc(f->bin); flags[i] = j; if((j & 8) != 0){ r = Bgetc(f->bin); while(r-- > 0) flags[++i] = j; } } fp = flags; fq = flags; lastx = lasty = 0; g->pt = malloc(sizeof(TTPoint) * (np + 2)); if(g->pt == nil) goto err; g->ptorg = malloc(sizeof(TTPoint) * (np + 2)); if(g->ptorg == nil) goto err; for(i = 0; i < np; i++){ p = &g->pt[g->npt + i]; p->flags = *fp & 1; switch(*fp++ & 0x12){ case 0x00: ttfunpack(f, "w", &temp16); p->x = lastx += temp16; break; case 0x02: ttfunpack(f, "b", &temp8); p->x = lastx -= temp8; break; case 0x10: p->x = lastx; break; case 0x12: ttfunpack(f, "b", &temp8); p->x = lastx += temp8; break; } } for(i = 0; i < np; i++){ p = &g->pt[g->npt + i]; switch(*fq++ & 0x24){ case 0x00: ttfunpack(f, "w", &temp16); p->y = lasty += temp16; break; case 0x04: ttfunpack(f, "b", &temp8); p->y = lasty -= temp8; break; case 0x20: p->y = lasty; break; case 0x24: ttfunpack(f, "b", &temp8); p->y = lasty += temp8; break; } } g->pt[np] = (TTPoint){0,0,0}; g->pt[np+1] = (TTPoint){f->ginfo[glyph].advanceWidth,0,0}; g->npt = np + 2; free(flags); if(render){ glyphscale(g); ttfhint(g); } return g; err: free(flags); ttfputglyph(g); return nil; } static TTGlyph *getglyph(TTFont *, int, int); enum { ARG_1_AND_2_ARE_WORDS = 1<<0, ARGS_ARE_XY_VALUES = 1<<1, ROUND_XY_TO_GRID = 1<<2, WE_HAVE_A_SCALE = 1<<3, MORE_COMPONENTS = 1<<5, WE_HAVE_AN_X_AND_Y_SCALE = 1<<6, WE_HAVE_A_TWO_BY_TWO = 1<<7, WE_HAVE_INSTRUCTIONS = 1<<8, USE_MY_METRICS = 1<<9, OVERLAP_COMPOUND = 1<<10, }; static int mergeglyph(TTGlyph *g, TTGlyph *h, int flags, int x, int y, int a, int b, int c, int d, int render) { int i, m; TTPoint *p; TTFont *f; int dx, dy; f = g->font; g->confst = realloc(g->confst, sizeof(int) * (g->ncon + h->ncon + 1)); for(i = 1; i <= h->ncon; i++) g->confst[g->ncon + i] = g->confst[g->ncon] + h->confst[i]; g->ncon += h->ncon; g->pt = realloc(g->pt, sizeof(TTPoint) * (g->npt + h->npt - 2)); if((flags & USE_MY_METRICS) == 0){ memmove(g->pt + g->npt + h->npt - 4, g->pt + g->npt - 2, 2 * sizeof(TTPoint)); m = h->npt - 2; }else m = h->npt; for(i = 0; i < m; i++){ p = &g->pt[g->npt - 2 + i]; *p = h->pt[i]; dx = ttfrounddiv(p->x * a + p->y * b, 16384); dy = ttfrounddiv(p->x * c + p->y * d, 16384); p->x = dx; p->y = dy; if((flags & ARGS_ARE_XY_VALUES) != 0){ if(render){ dx = ttfrounddiv(x * f->ppem * 64, f->u->emsize); dy = ttfrounddiv(y * f->ppem * 64, f->u->emsize); if((flags & ROUND_XY_TO_GRID) != 0){ dx = dx + 32 & -64; dy = dy + 32 & -64; } } p->x += dx; p->y += dy; }else abort(); } g->npt += h->npt - 2; return 0; } static TTGlyph * compglyph(TTFont *fs, int glyph, int render) { u16int flags, idx; int x, y; int a, b, c, d; TTFontU *f; uvlong off; TTGlyph *g, *h; u16int len; f = fs->u; g = mallocz(sizeof(TTGlyph), 1); if(g == nil) return nil; g->font = fs; g->info = &f->ginfo[glyph]; g->pt = mallocz(sizeof(TTPoint) * 2, 1); if(g->pt == nil){ err: ttfputglyph(g); return nil; } g->pt[1].x = ttfrounddiv(f->ginfo[glyph].advanceWidth * fs->ppem * 64, f->emsize); g->npt = 2; g->confst = mallocz(sizeof(int), 1); if(g->confst == nil) goto err; do{ ttfunpack(f, "ww", &flags, &idx); switch(flags & (ARG_1_AND_2_ARE_WORDS | ARGS_ARE_XY_VALUES)){ case 0: ttfunpack(f, "BB", &x, &y); break; case ARGS_ARE_XY_VALUES: ttfunpack(f, "BB", &x, &y); x = (char)x; y = (char)y; break; case ARG_1_AND_2_ARE_WORDS: ttfunpack(f, "WW", &x, &y); break; case ARG_1_AND_2_ARE_WORDS | ARGS_ARE_XY_VALUES: ttfunpack(f, "WW", &x, &y); x = (short)x; y = (short)y; break; } if((flags & WE_HAVE_A_SCALE) != 0){ ttfunpack(f, "S", &a); d = a; b = c = 0; }else if((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0){ ttfunpack(f, "SS", &a, &d); b = c = 0; }else if((flags & WE_HAVE_A_TWO_BY_TWO) != 0) ttfunpack(f, "SSSS", &a, &b, &c, &d); else{ a = d = 1<<14; b = c = 0; } off = Bseek(f->bin, 0, 1); h = getglyph(fs, idx, render); if(h == nil){ ttfputglyph(g); return nil; } if(mergeglyph(g, h, flags, x, y, a, b, c, d, render) < 0){ ttfputglyph(h); ttfputglyph(g); return nil; } ttfputglyph(h); Bseek(f->bin, off, 0); }while((flags & MORE_COMPONENTS) != 0); g->ptorg = malloc(sizeof(TTPoint) * g->npt); memmove(g->ptorg, g->pt, sizeof(TTPoint) * g->npt); // g->pt[g->npt - 1].x = g->pt[g->npt - 1].x + 32 & -64; if(render && (flags & WE_HAVE_INSTRUCTIONS) != 0){ ttfunpack(f, "w", &len); g->nhint = len; g->hint = mallocz(len, 1); if(g->hint == nil) goto err; Bread(f->bin, g->hint, len); ttfhint(g); } return g; } static TTGlyph * getglyph(TTFont *fs, int glyph, int render) { int i; short xmin, ymin, xmax, ymax, nc; TTFontU *f; TTGlyph *g; f = fs->u; if((uint)glyph >= f->numGlyphs){ werrstr("no such glyph %d", glyph); return nil; } if(f->ginfo[glyph].loca == f->ginfo[glyph+1].loca){ return emptyglyph(fs, glyph, render); } if(ttfgototable(f, "glyf") < 0) return nil; Bseek(f->bin, f->ginfo[glyph].loca, 1); ttfunpack(f, "wwwww", &nc, &xmin, &ymin, &xmax, &ymax); if(nc < 0) g = compglyph(fs, glyph, render); else g = simpglyph(fs, glyph, nc, render); if(g == nil) return nil; g->xmin = g->pt[0].x; g->xmax = g->pt[0].x; g->ymin = g->pt[0].y; g->ymax = g->pt[0].y; for(i = 1; i < g->npt - 2; i++){ if(g->pt[i].x < g->xmin) g->xmin = g->pt[i].x; if(g->pt[i].x > g->xmax) g->xmax = g->pt[i].x; if(g->pt[i].y < g->ymin) g->ymin = g->pt[i].y; if(g->pt[i].y > g->ymax) g->ymax = g->pt[i].y; } if(render){ g->xminpx = g->xmin >> 6; g->xmaxpx = g->xmax + 63 >> 6; g->yminpx = g->ymin >> 6; g->ymaxpx = g->ymax + 63 >> 6; } return g; } TTGlyph * ttfgetglyph(TTFont *fs, int glyph, int render) { TTGlyph *g; g = getglyph(fs, glyph, render); if(g == nil) return nil; g->idx = glyph; if(render){ ttfscan(g); g->advanceWidthpx = (g->pt[g->npt - 1].x - g->pt[g->npt - 2].x + 63) / 64; } setmalloctag(g, getcallerpc(&fs)); return g; }