shithub: fnt

Download patch

ref: c609f9639ffd99236a5403a057a5d920244fe3c9
parent: 00f3bc7db52b6ebd414ae354d599cf16a2965df2
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sun Jul 14 20:48:50 EDT 2024

initial component glyph rasterizing

--- a/otf.c.in
+++ b/otf.c.in
@@ -599,3 +599,9 @@
 {
 	return o->numGlyphs;
 }
+
+int
+otfupem(Otf *o)
+{
+	return o->td.head->unitsPerEm;
+}
--- a/otf.h.in
+++ b/otf.h.in
@@ -43,8 +43,14 @@
 struct ComponentGlyph {
 	u16int flags;
 	u16int glyphIndex;
-	int arg1;
-	int arg2;
+	union {
+		int arg1;
+		int dx;
+	};
+	union {
+		int arg2;
+		int dy;
+	};
 	float scale, scaleX, scale01, scale10, scaleY;
 	u16int numInstr;
 	u8int *instr;
@@ -77,8 +83,19 @@
 void otfprint(Otf *o, Otfile *out, int indent);
 void otfclose(Otf *o);
 
-/* FIXME these will go, this is for debugging and designing the drawing logic */
+/* FIXME these might go eventually */
+
+typedef struct GlyfImage GlyfImage;
+
+struct GlyfImage {
+	u8int *b;
+	int w;
+	int h;
+	int baseline;
+};
+
 typedef struct Glyf Glyf;
 Glyf *otfglyf(Otf *o, int index);
 int otfglyfnum(Otf *o);
-u8int *otfdrawglyf(Glyf *g, int ppem, int *wo, int *ho);
+int otfdrawglyf(Otf *o, Glyf *g, int ppem, GlyfImage *im);
+int otfupem(Otf *o);
--- a/plan9/otf.c
+++ b/plan9/otf.c
@@ -614,6 +614,12 @@
 	return o->numGlyphs;
 }
 
+int
+otfupem(Otf *o)
+{
+	return o->td.head->unitsPerEm;
+}
+
 
 int
 read_SubHeader(Otf *o, SubHeader *v)
--- a/plan9/otf.h
+++ b/plan9/otf.h
@@ -43,8 +43,14 @@
 struct ComponentGlyph {
 	u16int flags;
 	u16int glyphIndex;
-	int arg1;
-	int arg2;
+	union {
+		int arg1;
+		int dx;
+	};
+	union {
+		int arg2;
+		int dy;
+	};
 	float scale, scaleX, scale01, scale10, scaleY;
 	u16int numInstr;
 	u8int *instr;
@@ -77,11 +83,22 @@
 void otfprint(Otf *o, Otfile *out, int indent);
 void otfclose(Otf *o);
 
-/* FIXME these will go, this is for debugging and designing the drawing logic */
+/* FIXME these might go eventually */
+
+typedef struct GlyfImage GlyfImage;
+
+struct GlyfImage {
+	u8int *b;
+	int w;
+	int h;
+	int baseline;
+};
+
 typedef struct Glyf Glyf;
 Glyf *otfglyf(Otf *o, int index);
 int otfglyfnum(Otf *o);
-u8int *otfdrawglyf(Glyf *g, int ppem, int *wo, int *ho);
+int otfdrawglyf(Otf *o, Glyf *g, int ppem, GlyfImage *im);
+int otfupem(Otf *o);
 
 typedef struct SubHeader SubHeader;
 typedef struct MapGroup MapGroup;
--- a/plan9/test.c
+++ b/plan9/test.c
@@ -3,35 +3,11 @@
 #include <bio.h>
 #include "otf.h"
 
-typedef struct Image Image;
-
-struct Image {
-	int w;
-	int h;
-	u8int *b;
-};
-
 static void
-fit(u8int *b, int bw, int bh, Image *im, int maxh, int *x, int *y, int gap)
+dumpmap(Biobuf *out, GlyfImage *im, int n)
 {
-	int d;
-
-	if((bw-*x) < im->w+gap){
-		*y += maxh + gap;
-		*x = gap;
-	}
-	if((bh-*y) < maxh+gap)
-		return;
-	for(d = 0; d < im->h; d++)
-		memcpy(b + (*y + d)*bw + *x, im->b + d*im->w, im->w);
-	*x += im->w + gap;
-}
-
-static void
-dumpmap(Biobuf *out, Image *im, int n)
-{
 #define gap 1
-	int total, mid, npix, bw, bh, x, y, i, maxh;
+	int total, mid, npix, bw, bh, x, y, i, maxh, d;
 	u8int *b;
 
 	total = 0;
@@ -52,10 +28,19 @@
 		sysfatal("no memory");
 	memset(b, 0xff, npix);
 	x = y = gap;
-	for(i = 0; i < n; i++)
-		fit(b, bw, bh, im+i, maxh, &x, &y, gap);
-	Bprint(out, "%11s %11d %11d %11d %11d ", "k8", 0, 0, bw, y+maxh+gap);
-	Bwrite(out, b, bw*(y+maxh+gap));
+	for(i = 0; i < n; i++){
+		if((bw-x) < im[i].w+gap){
+			y += maxh + gap;
+			x = gap;
+		}
+		if((bh-y) < maxh+gap)
+			continue;
+		for(d = 0; d < im[i].h; d++)
+			memcpy(b + (maxh - im[i].h + y + im[i].baseline + d)*bw + x, im[i].b + d*im[i].w, im[i].w);
+		x += im[i].w + gap;
+	}
+	Bprint(out, "%11s %11d %11d %11d %11d ", "k8", 0, 0, bw, y+maxh*2+gap);
+	Bwrite(out, b, bw*(y+maxh*2+gap));
 	free(b);
 }
 
@@ -119,15 +104,14 @@
 			Bprint(out.aux, "%s\n", argv[i]);
 		if(G && gi < 0){
 			int i, n = otfglyfnum(o);
-			Image *im = ppem > 0 ? calloc(n, sizeof(*im)) : nil;
+			GlyfImage *im = ppem > 0 ? calloc(n, sizeof(*im)) : nil;
 			for(i = 0; i < n; i++){
 				Glyf *g = otfglyf(o, i);
 				if(g == nil)
-					sysfatal("%r");
-				if(ppem > 0 && g->simple != nil && g->numberOfContours > 0){
-					im[i].b = otfdrawglyf(g, ppem, &im[i].w, &im[i].h);
-					if(im[i].b == nil)
-						sysfatal("%r");
+					sysfatal("glyf %d: %r", i);
+				if(ppem > 0 && g->numberOfContours != 0){
+					if(otfdrawglyf(o, g, ppem, &im[i]) != 0)
+						sysfatal("glyf %d: %r", i);
 				}else if(ppem <= 0){
 					Bprint(out.aux, "%d (%s):\n", i,
 						g->simple ? "simple" : (g->component ? "component" : "empty"));
@@ -153,17 +137,12 @@
 			if(g == nil){
 				fprint(2, "%d: %r\n", gi);
 			}else if(ppem > 0){
-				if(g->component != nil)
-					fprint(2, "%d: component\n", gi);
-				else{
-					int w, h;
-					u8int *b = otfdrawglyf(g, ppem, &w, &h);
-					if(b == nil)
-						sysfatal("%r");
-					fprint(1, "%11s %11d %11d %11d %11d ", "k8", 0, 0, w, h);
-					write(1, b, w*h);
-					free(b);
-				}
+				GlyfImage im;
+				if(otfdrawglyf(o, g, ppem, &im) != 0)
+					sysfatal("%r");
+				fprint(1, "%11s %11d %11d %11d %11d ", "k8", 0, 0, im.w, im.h);
+				write(1, im.b, im.w*im.h);
+				free(im.b);
 			}else{
 				Bprint(out.aux, "\n%d:\n", gi);
 				print_Glyf(&out, indentΔ, o, g);
--- a/rast.c
+++ b/rast.c
@@ -451,21 +451,13 @@
 	return 0;
 }
 
-u8int *
-otfdrawglyf(Glyf *g, int ppem, int *wo, int *ho)
+static SegQ *
+addpoints(SegQ *s, Spt *pts₀, int max, Glyf *g, int dx, int dy, Sval scale, Sval offY)
 {
-	int w, h, i, r, k, npts, npx, ns;
 	Point *p₀, *p, *prev;
-	Spt *pts₀, *pts, *e;
-	SegQ *s₀, *s;
-	Sval *fp;
-	u8int *b;
+	Spt *e, *pts;
+	int k, npts;
 
-#define scale (double)ppem/g->unitsPerEm
-
-	ns = 0;
-	s₀ = nil;
-
 	for(k = 0; k < g->numberOfContours; k++){
 		npts = g->simple->endPtsOfContours[k]+1;
 		if(k > 0)
@@ -473,11 +465,13 @@
 		if(npts < 2)
 			continue;
 
-		pts₀ = e = malloc((npts+2)*3*sizeof(*e));
+		e = pts = pts₀;
 		p₀ = p = g->simple->points + (k > 0 ? g->simple->endPtsOfContours[k-1]+1 : 0);
 		prev = p₀+npts-1;
 		if(!p->onCurve){
 			/* fun stuff */
+			if(max-- < 1)
+				goto noplace;
 			if(prev->onCurve)
 				*e = (Spt){prev->x, prev->y};
 			else
@@ -487,6 +481,8 @@
 		for(prev = nil; p < p₀+npts; prev = p, p++, e++){
 			if(prev != nil && p->onCurve == prev->onCurve){
 				/* more fun stuff */
+				if(max-- < 1)
+					goto noplace;
 				if(p->onCurve) /* straight line */
 					*e = (Spt){p->x, p->y};
 				else /* a point in the middle */
@@ -493,46 +489,129 @@
 					*e = (Spt){(Sval)(p->x + prev->x)/2, (Sval)(p->y + prev->y)/2};
 				e++;
 			}
+			if(max-- < 1)
+				goto noplace;
 			*e = (Spt){p->x, p->y};
 		}
-		if(e > pts₀ && (e[-1].x != pts₀->x || e[-1].y != pts₀->y)){
-			if(p[-1].onCurve) /* close with a straight line */
-				*e++ = *pts₀;
-			*e++ = *pts₀;
+		if(e > pts && (e[-1].x != pts->x || e[-1].y != pts->y)){
+			if(p[-1].onCurve){ /* close with a straight line */
+				if(max-- < 1)
+					goto noplace;
+				*e++ = *pts;
+			}
+			if(max-- < 1)
+				goto noplace;
+			*e++ = *pts;
 		}
 
-		s₀ = realloc(s₀, (ns + (e-pts₀)/2)*sizeof(*s₀));
-		s = s₀ + ns;
-		pts = e;
-		for(e = pts₀; e <= pts-3; e += 2, s++, ns++){
-			s->v0[0] = (e[0].x - g->xMin) * scale + GAP;
-			s->v0[1] = (e[0].y - g->yMin) * scale + GAP;
-			s->v1[0] = (e[1].x - g->xMin) * scale + GAP;
-			s->v1[1] = (e[1].y - g->yMin) * scale + GAP;
-			s->v2[0] = (e[2].x - g->xMin) * scale + GAP;
-			s->v2[1] = (e[2].y - g->yMin) * scale + GAP;
+		for(; pts <= e-3; pts += 2, s++){
+			s->v0[0] = (pts[0].x + dx) * scale;
+			s->v0[1] = (pts[0].y + dy) * scale + offY;
+			s->v1[0] = (pts[1].x + dx) * scale;
+			s->v1[1] = (pts[1].y + dy) * scale + offY;
+			s->v2[0] = (pts[2].x + dx) * scale;
+			s->v2[1] = (pts[2].y + dy) * scale + offY;
 		}
-		free(pts₀);
 	}
+	return s;
+noplace:
+	werrstr("points don't fit");
+	return nil;
+}
 
-	w = ceil((g->xMax - g->xMin)*scale + 2*GAP);
-	h = ceil((g->yMax - g->yMin)*scale + 2*GAP);
+static int
+glyfmaxnpts(Glyf *g)
+{
+	int k, npts, d;
+
+	npts = 0;
+	for(k = 0; k < g->numberOfContours; k++){
+		d = g->simple->endPtsOfContours[k]+1;
+		if(k > 0)
+			d -= g->simple->endPtsOfContours[k-1]+1;
+		if(d > 1)
+			npts += 1+2*d+2; /* two extra to close off the contour */
+	}
+	return npts < 2 ? 0 : npts;
+}
+
+#define MAXCOMPONENTS 16
+
+int
+otfdrawglyf(Otf *o, Glyf *g, int ppem, GlyfImage *im)
+{
+	int i, j, maxptstotal, maxpts, ngs, w, h, r, npx, baseline;
+	Glyf *gs[MAXCOMPONENTS];
+	ComponentGlyph *cg;
+	Sval scale, offY;
+	SegQ *s₀, *s;
+	Spt *pts;
+	Sval *fp;
+	u8int *b;
+
+	r = -1;
+	ngs = 0;
+	pts = nil;
+
+	if(g->simple != nil){
+		gs[ngs++] = g;
+	}else{
+		for(cg = g->component; cg != nil && ngs < nelem(gs); cg = cg->next, ngs++){
+			if((gs[ngs] = otfglyf(o, cg->glyphIndex)) == nil)
+				goto done;
+		}
+	}
+	for(maxptstotal = maxpts = i = 0; i < ngs; i++){
+		j = glyfmaxnpts(gs[i]);
+		maxptstotal += j;
+		if(maxpts < j)
+			maxpts = j;
+	}
+
+	scale = (Sval)ppem / otfupem(o);
+	baseline = g->yMin < 0 ? ceil(scale*-g->yMin) : 0;
+	offY = baseline > 0 ? (Sval)baseline - scale*-g->yMin : 0;
+	w = ceil((g->xMax - g->xMin)*scale);
+	h = ceil((g->yMax - g->yMin)*scale + (baseline ? 1 : 0));
 	npx = w*h;
-	fp = calloc(1, npx*sizeof(*fp));
-	r = qbzr(s₀, ns, w, h, fp);
-	free(s₀);
 
-	b = nil;
-	if(r == 0){
-		b = (u8int*)fp;
+	pts = calloc(1, maxpts*sizeof(*pts) + maxptstotal/2*sizeof(*s) + npx*sizeof(*fp));
+	s = s₀ = (SegQ*)(pts + maxptstotal);
+	fp = (Sval*)(s + maxpts/2);
+	cg = g->component;
+	for(i = 0; i < ngs; i++){
+		int dx = -g->xMin, dy = -g->yMin;
+		if(cg != nil){
+			if(cg->flags & CGLYPH_FL_SIGNED_XY){
+				dx += cg->dx;
+				dy += cg->dy;
+			}
+			cg = cg->next;
+		}
+		if((s = addpoints(s, pts, maxpts, gs[i], dx, dy, scale, offY)) == nil)
+			goto done;
+	}
+
+	if((r = qbzr(s₀, s-s₀, w, h, fp)) == 0){
+		b = (u8int*)pts;
 		for(i = 0; i < npx; i++)
 			b[i] = 255 - (fp[i] <= 0 ? 0 : (fp[i] >= QBZR_PIX_SCALE ? 255 : fp[i]/QBZR_PIX_SCALE*255));
 		if((b = realloc(b, npx)) != nil){
-			*wo = w;
-			*ho = h;
-			fp = nil;
+			im->b = b;
+			im->w = w;
+			im->h = h;
+			im->baseline = baseline;
+			pts = nil;
+		}else{
+			r = -1;
 		}
 	}
-	free(fp);
-	return b;
+
+done:
+	for(i = 0; i < ngs; i++){
+		if(gs[i] != g)
+			free(gs[i]);
+	}
+	free(pts);
+	return r;
 }
--- a/unix/test.c
+++ b/unix/test.c
@@ -5,35 +5,11 @@
 #include <string.h>
 #include "otf.h"
 
-typedef struct Image Image;
-
-struct Image {
-	int w;
-	int h;
-	u8int *b;
-};
-
 static void
-fit(u8int *b, int bw, int bh, Image *im, int maxh, int *x, int *y, int gap)
+dumpmap(FILE *out, GlyfImage *im, int n)
 {
-	int d;
-
-	if((bw-*x) < im->w+gap){
-		*y += maxh + gap;
-		*x = gap;
-	}
-	if((bh-*y) < maxh+gap)
-		return;
-	for(d = 0; d < im->h; d++)
-		memcpy(b + (*y + d)*bw + *x, im->b + d*im->w, im->w);
-	*x += im->w + gap;
-}
-
-static void
-dumpmap(FILE *out, Image *im, int n)
-{
 #define gap 1
-	int total, mid, npix, bw, bh, x, y, i, maxh;
+	int total, mid, npix, bw, bh, x, y, i, maxh, d;
 	u8int *b;
 
 	total = 0;
@@ -52,11 +28,19 @@
 	npix += bw*gap;
 	if((b = malloc(npix)) == NULL)
 		err(1, NULL);
-
 	memset(b, 0xff, npix);
 	x = y = gap;
-	for(i = 0; i < n; i++)
-		fit(b, bw, bh, im+i, maxh, &x, &y, gap);
+	for(i = 0; i < n; i++){
+		if((bw-x) < im[i].w+gap){
+			y += maxh + gap;
+			x = gap;
+		}
+		if((bh-y) < maxh+gap)
+			continue;
+		for(d = 0; d < im[i].h; d++)
+			memcpy(b + (maxh - im[i].h + y + im[i].baseline + d)*bw + x, im[i].b + d*im[i].w, im[i].w);
+		x += im[i].w + gap;
+	}
 	fprintf(out, "%11s %11d %11d %11d %11d ", "k8", 0, 0, bw, y+maxh+gap);
 	fwrite(b, 1, bw*(y+maxh+gap), out);
 	free(b);
@@ -146,14 +130,13 @@
 			fprintf(out.aux, "%s\n", argv[i]);
 		if(G && gi < 0){
 			int i, n = otfglyfnum(o);
-			Image *im = ppem > 0 ? calloc(n, sizeof(*im)) : NULL;
+			GlyfImage *im = ppem > 0 ? calloc(n, sizeof(*im)) : NULL;
 			for(i = 0; i < n; i++){
 				Glyf *g = otfglyf(o, i);
 				if(g == NULL)
 					errx(1, "glyph %d: %s", i, otferrstr());
-				if(ppem > 0 && g->simple != NULL && g->numberOfContours > 0){
-					im[i].b = otfdrawglyf(g, ppem, &im[i].w, &im[i].h);
-					if(im[i].b == NULL)
+				if(ppem > 0 && g->numberOfContours != 0){
+					if(otfdrawglyf(o, g, ppem, im+i) != 0)
 						errx(1, "glyph %d: %s", i, otferrstr());
 				}else if(ppem <= 0){
 					fprintf(out.aux, "%d (%s):\n", i,
@@ -181,9 +164,8 @@
 				if(g->component != NULL)
 					errx(1, "glyph %d: component", gi);
 				else{
-					Image im;
-					im.b = otfdrawglyf(g, ppem, &im.w, &im.h);
-					if(im.b == NULL)
+					GlyfImage im;
+					if(otfdrawglyf(o, g, ppem, &im) != 0)
 						errx(1, "glyph %d: %s", gi, otferrstr());
 					fprintf(out.aux, "%11s %11d %11d %11d %11d ", "k8", 0, 0, im.w, im.h);
 					fwrite(im.b, 1, im.w*im.h, out.aux);