#include #include #include #include #include "impl.h" typedef struct Scan Scan; typedef struct TTLine TTLine; enum { LINEBLOCK = 32, PTBLOCK = 64, }; struct TTLine { int x0, y0; int x1, y1; int link; u8int dir; }; struct Scan { enum { DROPOUTS = 1, STUBDET = 2, SMART = 4, } flags; TTGlyph *g; TTLine *lines; int nlines; int *hpts, *vpts; int nhpts, nvpts; int *hscanl, *vscanl; u8int *bit; int width, height; int stride; }; static void dobezier(Scan *s, TTPoint p, TTPoint q, TTPoint r) { vlong m, n; TTLine *l; m = (vlong)(q.x - p.x) * (r.y - p.y) - (vlong)(q.y - p.y) * (r.x - p.x); n = (vlong)(r.x - p.x) * (r.x - p.x) + (vlong)(r.y - p.y) * (r.y - p.y); if(m * m > 4 * n){ dobezier(s, p, (TTPoint){(p.x+q.x+1)/2, (p.y+q.y+1)/2, 0}, (TTPoint){(p.x+2*q.x+r.x+2)/4, (p.y+2*q.y+r.y+2)/4, 0}); dobezier(s, (TTPoint){(p.x+2*q.x+r.x+2)/4, (p.y+2*q.y+r.y+2)/4, 0}, (TTPoint){(r.x+q.x+1)/2, (r.y+q.y+1)/2, 0}, r); return; } if((s->nlines & LINEBLOCK - 1) == 0) s->lines = realloc(s->lines, sizeof(TTLine) * (s->nlines + LINEBLOCK)); l = &s->lines[s->nlines++]; if(p.y < r.y){ l->x0 = p.x; l->y0 = p.y; l->x1 = r.x; l->y1 = r.y; l->dir = 0; }else{ l->x0 = r.x; l->y0 = r.y; l->x1 = p.x; l->y1 = p.y; l->dir = 1; } l->link = -1; } static int hlinecmp(void *va, void *vb) { TTLine *a, *b; a = va; b = vb; if(a->y0 < b->y0) return -1; if(a->y0 > b->y0) return 1; return 0; } static int vlinecmp(void *va, void *vb) { TTLine *a, *b; a = va; b = vb; if(a->x0 < b->x0) return -1; if(a->x0 > b->x0) return 1; return 0; } static int intcmp(void *va, void *vb) { int a, b; a = *(int*)va; b = *(int*)vb; return (a>b) - (alines, s->nlines, sizeof(TTLine), hlinecmp); s->hscanl = calloc(sizeof(int), (s->height + 1)); act = -1; watch = 0; p = &act; for(i = 0; i < s->height; i++){ y = 64 * i + 32; for(; watch < s->nlines && s->lines[watch].y0 <= y; watch++){ if(s->lines[watch].y1 <= y || s->lines[watch].y0 == s->lines[watch].y1) continue; s->lines[watch].link = -1; *p = watch; p = &s->lines[watch].link; } s->hscanl[i] = s->nhpts; p = &act; while(j = *p, j >= 0){ l = &s->lines[j]; if(l->y1 <= y){ j = l->link; l->link = -1; *p = j; continue; } x = l->x0 + ttfvrounddiv((vlong)(y - l->y0)*(l->x1 - l->x0), l->y1 - l->y0); if((s->nhpts & PTBLOCK - 1) == 0) s->hpts = realloc(s->hpts, (s->nhpts + PTBLOCK) * sizeof(int)); s->hpts[s->nhpts++] = x << 1 | l->dir; p = &l->link; } qsort(s->hpts + s->hscanl[i], s->nhpts - s->hscanl[i], sizeof(int), intcmp); } s->hscanl[i] = s->nhpts; } static int iswhite(Scan *s, int x, int y) { return (s->bit[(s->height - 1 - y) * s->stride + (x>>3)] >> 7-(x&7) & 1)==0; } static void pixel(Scan *s, int x, int y) { assert(x >= 0 && x < s->width && y >= 0 && y < s->height); s->bit[(s->height - 1 - y) * s->stride + (x>>3)] |= (1<<7-(x&7)); } static int intersectsh(Scan *s, int x, int y) { int a, b, c, vc, v; a = s->hscanl[y]; b = s->hscanl[y+1]-1; v = x * 64 + 32; if(a > b || s->hpts[a]>>1 > v + 64 || s->hpts[b]>>1 < v) return 0; while(a <= b){ c = (a + b) / 2; vc = s->hpts[c]>>1; if(vc < v) a = c + 1; else if(vc > v + 64) b = c - 1; else return 1; } return 0; } static int intersectsv(Scan *s, int x, int y) { int a, b, c, vc, v; a = s->vscanl[x]; b = s->vscanl[x+1]-1; v = y * 64 + 32; if(a > b || s->vpts[a]>>1 > v + 64 || s->vpts[b]>>1 < v) return 0; while(a <= b){ c = (a + b) / 2; vc = s->vpts[c]>>1; if(vc < v) a = c + 1; else if(vc > v + 64) b = c - 1; else return 1; } return 0; } static void hscan(Scan *s) { int i, j, k, e; int wind, match, seen, x; for(i = 0; i < s->height; i++){ e = s->hscanl[i+1]; k = s->hscanl[i]; if(k == e) continue; wind = 0; for(j = 0; j < s->width; j++){ x = 64 * j + 32; match = 0; seen = 0; while(k < e && (s->hpts[k] >> 1) <= x){ wind += (s->hpts[k] & 1) * 2 - 1; seen |= 1<<(s->hpts[k] & 1); if((s->hpts[k] >> 1) == x) match++; k++; } if(match || wind) pixel(s, j, i); else if((s->flags & DROPOUTS) != 0 && seen == 3 && j > 0 && iswhite(s, j-1, i)){ if((s->flags & STUBDET) == 0){ pixel(s, j-1, i); continue; } if(i <= 0 || i > s->height - 1 || j <= 0 || j > s->width - 1) continue; if(!intersectsv(s, j-1, i-1) && !intersectsh(s, j-1, i-1) && !intersectsv(s, j, i-1) || !intersectsv(s, j-1, i) && !intersectsh(s, j-1, i+1) && !intersectsv(s, j, i)) continue; pixel(s, j-1, i); } } } } static void vprep(Scan *s) { int i, j, x, y; TTLine *l; int watch, act, *p; for(i = 0; i < s->nlines; i++){ l = &s->lines[i]; if(l->x0 > l->x1){ x = l->x0, l->x0 = l->x1, l->x1 = x; x = l->y0, l->y0 = l->y1, l->y1 = x; l->dir ^= 1; } } qsort(s->lines, s->nlines, sizeof(TTLine), vlinecmp); s->vscanl = calloc(sizeof(int), (s->width + 1)); act = -1; watch = 0; p = &act; for(i = 0; i < s->width; i++){ x = 64 * i + 32; for(; watch < s->nlines && s->lines[watch].x0 <= x; watch++){ if(s->lines[watch].x1 <= x || s->lines[watch].x0 == s->lines[watch].x1) continue; s->lines[watch].link = -1; *p = watch; p = &s->lines[watch].link; } s->vscanl[i] = s->nvpts; p = &act; while(j = *p, j >= 0){ l = &s->lines[j]; if(l->x1 <= x){ j = l->link; l->link = -1; *p = j; continue; } y = l->y0 + ttfvrounddiv((vlong)(x - l->x0) * (l->y1 - l->y0), l->x1 - l->x0); if((s->nvpts & PTBLOCK - 1) == 0) s->vpts = realloc(s->vpts, (s->nvpts + PTBLOCK) * sizeof(int)); s->vpts[s->nvpts++] = y << 1 | l->dir; p = &l->link; } qsort(s->vpts + s->vscanl[i], s->nvpts - s->vscanl[i], sizeof(int), intcmp); } s->vscanl[i] = s->nvpts; } static void vscan(Scan *s) { int i, j, k, e; int seen, y; for(i = 0; i < s->width; i++){ e = s->vscanl[i+1]; k = s->vscanl[i]; if(k == e) continue; for(j = 0; j < s->height; j++){ y = 64 * j + 32; seen = 0; while(k < e && (s->vpts[k] >> 1) <= y){ seen |= 1<<(s->vpts[k] & 1); k++; } if(seen == 3 && j > 0 && iswhite(s, i, j-1) && iswhite(s, i, j)){ if((s->flags & STUBDET) == 0){ pixel(s, i, j-1); continue; } if(i <= 0 || i > s->width - 1 || j <= 0 || j > s->height - 1) continue; if(!intersectsv(s, i-1, j-1) & !intersectsh(s, i-1, j-1) & !intersectsh(s, i-1, j) | !intersectsv(s, i+1, j-1) & !intersectsh(s, i, j-1) & !intersectsh(s, i, j)) continue; pixel(s, i, j-1); } } } } void ttfscan(TTGlyph *g) { int i, j, c; TTPoint p, q, r; Scan s; memset(&s, 0, sizeof(s)); s.g = g; s.flags = 0; c = g->font->scanctrl; if((c & 1<<8) != 0 && g->font->ppem <= (c & 0xff)) s.flags |= DROPOUTS; if((c & 1<<11) != 0 && g->font->ppem > (c & 0xff)) s.flags &= ~DROPOUTS; if((c & 3<<12) != 0) s.flags &= ~DROPOUTS; if((s.flags & DROPOUTS) != 0) switch(g->font->scantype){ case 0: break; case 1: s.flags |= STUBDET; break; case 2: case 3: case 6: case 7: s.flags &= ~DROPOUTS; break; case 4: s.flags |= SMART; break; case 5: s.flags |= SMART | STUBDET; break; } // s.width = (g->pt[g->npt - 1].x + 63) / 64; // s.height = g->font->ascentpx + g->font->descentpx; s.width = -g->xminpx + g->xmaxpx; s.height = -g->yminpx + g->ymaxpx; s.stride = s.width + 7 >> 3; s.bit = mallocz(s.height * s.stride, 1); assert(s.bit != nil); for(i = 0; i < g->npt; i++){ g->pt[i].x -= g->xminpx * 64; g->pt[i].y -= g->yminpx * 64; // g->pt[i].y += g->font->descentpx * 64; } for(i = 0; i < g->ncon; i++){ if(g->confst[i] + 1 >= g->confst[i+1]) continue; p = g->pt[g->confst[i]]; assert((p.flags & 1) != 0); for(j = g->confst[i]; j++ < g->confst[i+1]; ){ if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0) q = g->pt[j++]; else q = p; if(j >= g->confst[i+1]) r = g->pt[g->confst[i]]; else{ r = g->pt[j]; if((g->pt[j].flags & 1) == 0){ r.x = (r.x + q.x) / 2; r.y = (r.y + q.y) / 2; } } dobezier(&s, p, q, r); p = r; if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0) j--; } } hprep(&s); if((s.flags & DROPOUTS) != 0) vprep(&s); hscan(&s); if((s.flags & DROPOUTS) != 0) vscan(&s); free(s.hpts); free(s.vpts); free(s.hscanl); free(s.vscanl); free(s.lines); g->bit = s.bit; g->width = s.width; g->height = s.height; g->stride = s.stride; } int ttfgetcontour(TTGlyph *g, int i, float **fp, int *np) { float offx, offy, scale; float *nf; int n, j; TTPoint p, q, r; if((uint)i >= g->ncon) return 0; if(g->confst[i]+1 >= g->confst[i+1]){ if(np != nil) *np = 0; if(fp != nil) *fp = malloc(0); return g->ncon - i; } if(g->bit != nil){ scale = 1.0f / 64; offx = g->xminpx; offy = g->yminpx; }else{ scale = 1.0f * g->font->ppem / g->font->u->emsize; offx = 0; offy = 0; } p = g->pt[g->confst[i]]; n = 1; if(fp != nil){ *fp = malloc(2 * sizeof(float)); if(*fp == nil) return -1; (*fp)[0] = p.x * scale; (*fp)[1] = p.y * scale + offy; } assert((p.flags & 1) != 0); for(j = g->confst[i]; j++ < g->confst[i+1]; ){ if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0) q = g->pt[j++]; else q = p; if(j >= g->confst[i+1]) r = g->pt[g->confst[i]]; else{ r = g->pt[j]; if((g->pt[j].flags & 1) == 0){ r.x = (r.x + q.x) / 2; r.y = (r.y + q.y) / 2; } } if(fp != nil){ nf = realloc(*fp, sizeof(float) * 2 * (n + 2)); if(nf == nil){ free(*fp); return -1; } *fp = nf; nf[2*n] = q.x * scale; nf[2*n+1] = q.y * scale + offy; nf[2*n+2] = r.x * scale; nf[2*n+3] = r.y * scale + offy; } p = r; n += 2; if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0) j--; } if(np != nil) *np = n; return g->ncon - i; }