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; \