shithub: fnt

Download patch

ref: 493d6287c76fdc7237804b08c4099ee82ab8c6cf
parent: b91f5a473f8bff91966146e564a28798950a8888
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Mon Jul 29 19:10:58 EDT 2024

more subpixel configurations, bring back the obligatory gap on X and Y for now

--- a/README.md
+++ b/README.md
@@ -18,6 +18,6 @@
 not used.  This is an implementation from scratch.
 
  * Antialiasing.
- * Basic subpixel rendering (atm it assumes `r,g,b` LCD matrix pixels order).
+ * Basic subpixel rendering (RGB/BGR, horizontal/vertical).
  * Arbitrary non-integer scaling on X and Y axis.
  * Bitmap (`*.otb`) fonts.
--- a/otf.h.in
+++ b/otf.h.in
@@ -131,7 +131,8 @@
 };
 
 enum {
-	LCD_RGB,
+	LCD_ORDER_RGB,
+	LCD_ORDER_BGR,
 };
 
 struct GlyfImage {
@@ -147,7 +148,10 @@
 		u8int *c;
 		int nc;
 	}fir;
-	int lcd;
+	int lcdorder;
+	int lcdvert;
+	u8int *buf;
+	int bufsz;
 };
 
 struct RasterOpts {
--- a/plan9/otf.h
+++ b/plan9/otf.h
@@ -132,7 +132,8 @@
 };
 
 enum {
-	LCD_RGB,
+	LCD_ORDER_RGB,
+	LCD_ORDER_BGR,
 };
 
 struct GlyfImage {
@@ -148,7 +149,8 @@
 		u8int *c;
 		int nc;
 	}fir;
-	int lcd;
+	int lcdorder;
+	int lcdvert;
 };
 
 struct RasterOpts {
--- a/rast.c
+++ b/rast.c
@@ -554,25 +554,90 @@
 	u8int *in, *o;
 	int x, y, i, a;
 
-	o = im->b + im->w*im->h;
-	for(y = 0; y < im->h; y++){
-		in = im->b + y*im->w;
+	if(sub->lcdvert){
+		o = sub->buf;
 		for(x = 0; x < im->w; x++){
-			a = 0;
-			for(i = 0; i < sub->fir.nc; i++){
-				if(x >= i)
-					a += sub->fir.c[i]*in[x-i]/255;
+			in = im->b + x;
+			for(y = 0; y < im->h; y++){
+				a = 0;
+				for(i = 0; i < sub->fir.nc; i++){
+					if(y >= i)
+						a += sub->fir.c[i]*in[(y-i)*im->w]/255;
+				}
+				*o++ = a > 255 ? 255 : a;
 			}
-			o[x] = a > 255 ? 255 : a;
 		}
-		memcpy(in, o, im->w);
+		o = sub->buf;
+		if(sub->lcdorder == LCD_ORDER_RGB){
+			for(x = 0; x < im->w; x++){
+				in = im->b + x*3;
+				for(y = 0; y < im->h; y += 3, in += im->w*3){
+					in[0] = *o++;
+					in[1] = *o++;
+					in[2] = *o++;
+				}
+			}
+		}else{
+			for(x = 0; x < im->w; x++){
+				in = im->b + x*3;
+				for(y = 0; y < im->h; y += 3, in += im->w*3){
+					in[2] = *o++;
+					in[1] = *o++;
+					in[0] = *o++;
+				}
+			}
+		}
+		im->baseline /= 3;
+		im->h /= 3;
+	}else{
+		o = sub->buf;
+		for(y = 0; y < im->h; y++){
+			in = im->b + y*im->w;
+			for(x = 0; x < im->w; x++){
+				a = 0;
+				for(i = 0; i < sub->fir.nc; i++){
+					if(x >= i)
+						a += sub->fir.c[i]*in[x-i]/255;
+				}
+				o[x] = a > 255 ? 255 : a;
+			}
+			if(sub->lcdorder == LCD_ORDER_RGB)
+				memcpy(in, o, im->w);
+			else{
+				for(i = 0; i < im->w; i += 3){
+					*in++ = o[i+2];
+					*in++ = o[i+1];
+					*in++ = o[i+0];
+				}
+			}
+		}
+		im->w /= 3;
 	}
 }
 
+static void
+subpxextend(SubPx *sub, int *w, int *h)
+{
+	int sz, n;
+
+	if(sub->lcdvert){
+		if((n = *h % 3) != 0)
+			*h += 3 - n;
+	}else{
+		if((n = *w % 3) != 0)
+			*w += 3 - n;
+	}
+	sz = *w * *h;
+	if(sz > sub->bufsz){
+		sub->buf = realloc(sub->buf, sz);
+		sub->bufsz = sz;
+	}
+}
+
 static GlyfImage *
 outlineglyf(Otf *o, Glyf *g, RasterOpts *opts)
 {
-	int i, j, maxptstotal, maxpts, ngs, rw, rh, w, h, npx, baseline;
+	int i, j, maxptstotal, maxpts, ngs, w, h, npx, baseline;
 	Sval bb[4], offY, scaleX, scaleY;
 	Glyf *gs[MAXCOMPONENTS] = {nil};
 	ComponentGlyph *cg;
@@ -612,8 +677,12 @@
 	pts = calloc(1, maxpts*sizeof(*pts) + maxptstotal/2*sizeof(*s));
 	scaleX = opts->ppemX / o->td.head->unitsPerEm;
 	scaleY = opts->ppemY / o->td.head->unitsPerEm;
-	if(opts->subpx != nil)
-		scaleX *= 3;
+	if(opts->subpx != nil){
+		if(opts->subpx->lcdvert)
+			scaleY *= 3;
+		else
+			scaleX *= 3;
+	}
 	bb[0] = g->xMin*scaleX;
 	bb[1] = g->yMin*scaleY;
 	bb[2] = g->xMax*scaleX;
@@ -653,53 +722,40 @@
 			goto done;
 	}
 
+	int gapX = 3, gapY = 3;
 	if(s == s₀){
-		w = h = 1;
+		w = gapX * 2;
+		h = gapY * 2;
 		baseline = 0;
 	}else{
 		/* height+baseline is where it is in the image */
 		baseline = floor(bb[1]);
 		offY = -baseline;
-		if(offY+bb[1] < ε){
-			/*
-			 * very rarely a glyph will have one of its segments
-			 * go over the edge because of imprecise arithmetic.
-			 * it's much easier to solve this by always having a small gap.
-			 */
-			offY += 4*ε;
-			bb[3] += 8*ε;
-		}
 		bb[3] += offY;
-		w = ceil(bb[2]+8*ε) - floor(bb[0]-4*ε);
-		h = ceil(bb[3]);
+		w = gapX + ceil(bb[2]) - floor(bb[0]) + gapX;
+		h = gapY + ceil(bb[3]) + gapY;
+		baseline -= gapY;
 
 		for(p = s₀; p != s; p++){
-			p->p0.x -= bb[0];
-			p->p1.x -= bb[0];
-			p->p2.x -= bb[0];
-			p->p0.y += offY;
-			p->p1.y += offY;
-			p->p2.y += offY;
+			p->p0.x -= bb[0] - gapX;
+			p->p1.x -= bb[0] - gapX;
+			p->p2.x -= bb[0] - gapX;
+			p->p0.y += offY + gapY;
+			p->p1.y += offY + gapY;
+			p->p2.y += offY + gapY;
 		}
 	}
-	rw = w;
-	rh = h;
-	if(opts->subpx != nil){
-		rw += 2;
-		rh++;
-		if((rw % 3) != 0)
-			rw += 3 - (rw % 3);
-	}
+	if(opts->subpx != nil) /* FIXME this should go */
+		subpxextend(opts->subpx, &w, &h);
 
-	npx = rw*rh;
+	npx = w*h;
 	im = calloc(npx, sizeof(*im));
-	if(qbzr(s₀, s-s₀, w, h, rw, im->b) == 0){
-		im->w = rw;
+	if(qbzr(s₀, s-s₀, w, h, w, im->b) == 0){
+		im->w = w;
 		im->h = h;
 		im->baseline = baseline;
 		if(opts->subpx != nil){
 			subpxfir(im, opts->subpx);
-			im->w /= 3;
 			im->fmt = GLYF_FMT_RGB_24;
 		}else{
 			im->fmt = GLYF_FMT_GREY_8;
--- a/test.h
+++ b/test.h
@@ -12,7 +12,7 @@
 	f->print(f->aux, " -H: highlight a specific glyph in the map by its ID\n");
 	f->print(f->aux, " -x: horizontal ppem\n");
 	f->print(f->aux, " -y: vertical ppem\n");
-	f->print(f->aux, " -s: use subpixel rendering if applicable\n");
+	f->print(f->aux, " -s: use subpixel rendering if applicable (rgb, bgr, rgbv, bgrv)\n");
 	f->print(f->aux, "Specifying -m more than once adds guide lines\n");
 	f->print(f->aux, "Specifying -m more than twice makes empty pixels filled out\n");
 }
@@ -20,11 +20,12 @@
 static u8int deffir[] = {8, 77, 86, 77, 8};
 static RasterOpts ropts;
 static SubPx subpx = {
-	.lcd = LCD_RGB,
 	.fir = {
 		.c = deffir,
 		.nc = nelem(deffir),
 	},
+	.lcdorder = LCD_ORDER_RGB,
+	.lcdvert = 0,
 };
 static int gind = -1, map, highlight = -1, runesonly;
 static Rune rune = NoRune;
@@ -252,9 +253,21 @@
 	case 'R': \
 		runesonly++; \
 		break; \
-	case 's': \
+	case 's': { \
+		char *s = EARGF(usage(&out)); \
+		if(strcmp(s, "bgr") == 0) \
+			subpx.lcdorder = LCD_ORDER_BGR; \
+		else if(strcmp(s, "rgbv") == 0) \
+			subpx.lcdvert = 1; \
+		else if(strcmp(s, "bgrv") == 0){ \
+			subpx.lcdorder = LCD_ORDER_BGR; \
+			subpx.lcdvert = 1; \
+		}else if(strcmp(s, "rgb") != 0){ \
+			out.print(out.aux, "invalid subpixel configuration: %s\n", s); \
+			usage(&out); \
+		} \
 		ropts.subpx = &subpx; \
-		break; \
+		} break; \
 	case 'x': \
 		ropts.ppemX = strtod(EARGF(usage(&out)), nil); \
 		break; \