shithub: subpixelize

Download patch

ref: 27ab378bba094b64ecd9e681ef30637cfbf3d9e8
parent: fd5885e44a6599011679f70fb701280b77748992
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Mar 3 14:27:56 EST 2026

adds better subpixelize version. Adds sand.bit as a test case

--- a/README
+++ b/README
@@ -7,24 +7,27 @@
 
 This program converts a grayscale image to a colored image that makes use of subpixels to add fine details.
 
-The program defines three levels of gray:
+The program expects an image three times the size of the target image. Each group of 3x3 pixels will be converted to a single pixel that is composed out of the three subpixels.
 
-- Black: <  64
-- Low:   ≥  64 and < 128
-- High:  ≥ 128 and < 192
-- White: ≥ 192
+It is possible to scale the value by using grayscale values. The subpixel will be filled with the average value of its column. For example, both
 
-Depending on the surrounding two pixels (left and right), Low and High values will be replaced by colored pixels.
+000 128 000
+000 128 000
+000 128 000
 
+and
 
-BUGS
+000 255 000
+000 128 000
+000 000 000
 
-Right now, the input image needs to have 3 or 4 channels (alpha is ignored/will be passed through). Single-channel images must be converted before use, e. g. using iconv.
+will be converted to rgb(0, 128, 0).
 
-Only images with (mostly) pure black and (mostly) pure white areas are supported.
 
+BUGS
+
+The groups of 3x3 pixels need to be aligned to the grid.
+
 Subpixelize supports only horizontal subpixels (RGB in a row).
 
 Some people don't like subpixels.
-
-The quality of the results isn't visually perfect and might need manual fixup.
--- a/mkfile
+++ b/mkfile
@@ -7,4 +7,4 @@
 </sys/src/cmd/mkone
 
 t:V: $O.out
-	$O.out < test.bit > testout.bit
+	$O.out < sand.bit > sandout.bit
binary files /dev/null b/sand.bit differ
--- a/subpixelize.c
+++ b/subpixelize.c
@@ -3,189 +3,68 @@
 #include <draw.h>
 #include <memdraw.h>
 
-void
-usage(void)
+static void
+transformpx(Memimage *src, Memimage *dst, Point p)
 {
-	fprint(2, "usage: %s\n", argv0);
-	exits("usage");
-}
-
-#define LOWVAL   (255/4)
-#define HIGHVAL  (255/4*3)
-#define LOW(A)   ((A) >= LOWVAL && (A) < 128)
-#define HIGH(A)  ((A) < HIGHVAL && (A) >= 128)
-#define BLACK(A)  ((A) < LOWVAL)
-#define WHITE(A) ((A) >= HIGHVAL)
-
-typedef enum {
-	Equal,
-	Low,
-	High,
-	Rise,
-	Fall,
-} Type;
-
-int
-getbyteval(uchar *val, int nchan)
-{
-	int v, i;
+	uchar *t;
+	uchar *s1, *s2, *s3;
+	uchar *s4, *s5, *s6;
+	uchar *s7, *s8, *s9;
 	
-	v = 0;
-	for (i = 0; i < nchan-1; i++)
-		v += val[i];
+	t = byteaddr(dst, p);
 	
-	return v/i;
-}
-
-Type
-checkrow(Memimage *src, Point p2, uchar *ival)
-{
-	Point p1, p3;
-	int v1, v2, v3;
+	p.x *= 3;
+	p.y *= 3;
 	
-	p1 = p2;
-	p3 = p2;
-	p1.x--;
-	p3.x++;
+	s1 = byteaddr(src, Pt(p.x  , p.y  ));
+	s2 = byteaddr(src, Pt(p.x+1, p.y  ));
+	s3 = byteaddr(src, Pt(p.x+2, p.y  ));
+	s4 = byteaddr(src, Pt(p.x  , p.y+1));
+	s5 = byteaddr(src, Pt(p.x+1, p.y+1));
+	s6 = byteaddr(src, Pt(p.x+2, p.y+1));
+	s7 = byteaddr(src, Pt(p.x  , p.y+2));
+	s8 = byteaddr(src, Pt(p.x+1, p.y+2));
+	s9 = byteaddr(src, Pt(p.x+2, p.y+2));
 	
-	if (!ptinrect(p1, src->r))
-		return Equal;
-	if (!ptinrect(p2, src->r))
-		return Equal;
-	if (!ptinrect(p3, src->r))
-		return Equal;
-	
-	v1 = getbyteval(byteaddr(src, p1), src->nchan);
-	v2 = getbyteval(byteaddr(src, p2), src->nchan);
-	v3 = getbyteval(byteaddr(src, p3), src->nchan);
-	
-	*ival = v2;
-	
-	if (BLACK(v2) || WHITE(v2))
-		return Equal;
-	
-	if (BLACK(v1) && BLACK(v3))
-		return Low;
-	if (WHITE(v1) && WHITE(v3))
-		return High;
-	if (BLACK(v1) && WHITE(v3))
-		return Rise;
-	if (WHITE(v1) && BLACK(v3))
-		return Fall;
-	if (BLACK(v1) &&   LOW(v3))
-		return Rise;
-	if (BLACK(v1) &&  HIGH(v3))
-		return Rise;
-	if ( HIGH(v1) && BLACK(v3))
-		return Fall;
-	if (  LOW(v1) && BLACK(v3))
-		return Fall;
-	
-	fprint(2, "unhandled case! %3d %3d %3d\n", v1, v2, v3);
-	return Equal;
+	t[2] = (*s1 + *s4 + *s7) / 3;
+	t[1] = (*s2 + *s5 + *s8) / 3;
+	t[0] = (*s3 + *s6 + *s9) / 3;
 }
 
-void
-handlepixel(Memimage *tgt, Memimage *src, Point p)
+static void
+transform(Memimage *src, Memimage *dst)
 {
-	uchar *t, *s;
-	int i;
-	Type tp;
-	uchar ival;
-	uchar col[4]; /* bgra */
+	int x, y;
 	
-	t = byteaddr(tgt, p);
-	s = byteaddr(src, p);
-	
-	for (i = 0; i < tgt->nchan; i++) {
-		t[i] = s[i];
-	}
-	
-	tp = checkrow(src, p, &ival);
-	
-	if (tp == Equal)
-		return;
-	
-	if (BLACK(ival))
-		return;
-	if (WHITE(ival)) /* considered white */
-		return;
-	
-	/* green value */
-	col[1] = LOW(ival) ? 0 : 255;
-	
-	col[3] = 255;
-	switch (tp) {
-	case Rise:
-		//fprint(2, "case r √\n");
-		col[0] = 255;
-		col[2] =   0;
-		goto Fill;
-	case Fall:
-		//fprint(2, "case f √\n");
-		col[0] =   0;
-		col[2] = 255;
-		goto Fill;
-	case High:
-		//fprint(2, "case h √\n");
-		col[0] = 255;
-		col[1] = LOW(ival) ? 0 : 128;
-		col[2] = 255;
-		goto Fill;
-	case Low:
-		//fprint(2, "case l √\n");
-		col[0] =   0;
-		col[1] = LOW(ival) ? 128 : 255;
-		col[2] =   0;
-		goto Fill;
-	}
-	fprint(2, "should never happen!\n");
-	return;
-Fill:
-	for (i = 0; i < tgt->nchan; i++) {
-		t[i] = col[i];
-	}
+	for (y = dst->r.min.y; y < dst->r.max.y; y++)
+		for (x = dst->r.min.x; x < dst->r.max.x; x++)
+			transformpx(src, dst, Pt(x, y));
 }
 
 void
-subpixelize(Memimage *tgt, Memimage *src)
+main(void)
 {
-	long x, y;
+	Memimage *in, *out;
+	Rectangle r;
 	
-	for (y = tgt->r.min.y; y < tgt->r.max.y; y++)
-		for (x = tgt->r.min.x; x < tgt->r.max.x; x++)
-			handlepixel(tgt, src, Pt(x, y));
-}
-
-void
-main(int argc, char **argv)
-{
-	Memimage *src;
-	Memimage *tgt;
-	
-	ARGBEGIN{
-	case 'h':
-		usage();
-		break;
-	}ARGEND;
-	
 	if (memimageinit() < 0)
 		sysfatal("%r");
 	
-	src = readmemimage(0);
-	if (!src)
-		sysfatal("readmemimage: %r");
+	in = readmemimage(0);
+	if (!in)
+		sysfatal("%r");
 	
-	if (!(src->nchan == 3 || src->nchan == 4))
-		sysfatal("unsupported chan");
+	r.min.x = in->r.min.x/3;
+	r.min.y = in->r.min.y/3;
+	r.max.x = r.min.x + Dx(in->r)/3;
+	r.max.y = r.min.y + Dy(in->r)/3;
 	
-	tgt = allocmemimage(src->r, src->chan);
-	if (!tgt)
-		sysfatal("allocmemimage: %r");
-	memfillcolor(tgt, DTransparent);
+	out = allocmemimage(r, RGB24);
+	if (!out)
+		sysfatal("%r");
 	
-	subpixelize(tgt, src);
-	if (writememimage(1, tgt) < 0)
-		sysfatal("writememimage");
+	transform(in, out);
+	
+	writememimage(1, out);
 	exits(nil);
-}
\ No newline at end of file
+}
binary files a/test.bit /dev/null differ
--