summaryrefslogtreecommitdiff
path: root/sys/src/libttf/scan.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/scan.c
parent198f10bb25382882c4abd9081dac4dd74dbdbb9f (diff)
add libttf
Diffstat (limited to 'sys/src/libttf/scan.c')
-rw-r--r--sys/src/libttf/scan.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/sys/src/libttf/scan.c b/sys/src/libttf/scan.c
new file mode 100644
index 000000000..c76143555
--- /dev/null
+++ b/sys/src/libttf/scan.c
@@ -0,0 +1,478 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ttf.h>
+#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) - (a<b);
+}
+
+static void
+hprep(Scan *s)
+{
+ int i, j, x, y;
+ TTLine *l;
+ int watch, act, *p;
+
+ qsort(s->lines, 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, j-1, i);
+ 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;
+}