shithub: ifilter

Download patch

ref: 90204e60f5b1e8fb734f99898e7128052b47d869
parent: bf1934aa4a1bbc6b13d8008c4a044a0d66dbadb7
author: phil9 <telephil9@gmail.com>
date: Wed Dec 7 04:07:27 EST 2022

add dither program to perform dithering using Floyd-Steinberg algorithm

--- a/README
+++ b/README
@@ -31,4 +31,9 @@
 - gaussian: Gaussian blur using `-s` as the convolution kernel radius (ie kernel will be a matrix r*r with r=2*size+1)
 - pixelate: a simple pixelation filter using `-s` argument as pixel size
 
+dither:
+-------
+Floyd-Steinberg dithering algorithm.
+
+Usage: dither
 
--- /dev/null
+++ b/dither.c
@@ -1,0 +1,91 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+int
+clamp(int n)
+{
+	if(n < 0)
+		n = 0;
+	else if(n > 255)
+		n = 255;
+	return n;
+}
+
+uchar*
+dither(uchar *data, int w, int h, int depth)
+{
+#define P(X,Y,C) out[((X) + w*(Y))*depth + C]
+#define SETP(X,Y,C,E) out[((X) + w*(Y))*depth + C] = clamp(out[((X) + w*(Y))*depth + C] + E)
+	uchar *out;
+	int x, y, c, o, n, q;
+
+	out = malloc(depth*w*h*sizeof(uchar));
+	if(out == nil)
+		return nil;
+	for(y = 0; y < depth*w*h; y++)
+		out[y] = data[y];
+	for(y = 0; y < h; y++){
+		for(x = 0; x < w; x++){
+			for(c = 0; c < 3; c++){ /* B G R */
+				o = P(x, y, c);
+				n = 255*floor(o/255.0 + 0.5);
+				q = o - n;
+				P(x, y, c) = n;
+				if(x + 1 < w)               SETP(x + 1, y    , c, q * 7.0/16.0);
+				if(x - 1 >= 0 && y + 1 < h) SETP(x - 1, y + 1, c, q * 3.0/16.0);
+				if(y + 1 < h)               SETP(x    , y + 1, c, q * 5.0/16.0);
+				if(x + 1 < w && y + 1 < h)  SETP(x + 1, y + 1, c, q * 1.0/16.0);
+			}
+		}
+	}
+	return out;
+#undef SETP
+#undef P
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *o, *i;
+	int w, h, n;
+	uchar *buf, *out;
+
+	ARGBEGIN{
+	default:
+		usage();
+		break;
+	}ARGEND;
+	if(memimageinit()<0)
+		sysfatal("memimageinit: %r");
+	o = readmemimage(0);
+	if(o==nil)
+		sysfatal("readmemimage: %r");
+	i = allocmemimage(rectsubpt(o->r, o->r.min), XRGB32);
+	memimagedraw(i, i->r, o, o->r.min, nil, ZP, S);
+	freememimage(o);
+	w = Dx(i->r);
+	h = Dy(i->r);
+	n = 4*w*h*sizeof(uchar);
+	buf = malloc(n);
+	if(buf==nil)
+		sysfatal("malloc: %r");
+	if(unloadmemimage(i, i->r, buf, n)<0)
+		sysfatal("unloadmemimage: %r");
+	freememimage(i);
+	out = dither(buf, w, h, 4);
+	if(out == nil)
+		sysfatal("dither failed: %r");
+	print("   x8r8g8b8 %11d %11d %11d %11d ", 0, 0, w, h);
+	write(1, out, n);
+	exits(nil);
+}
+
--- a/mkfile
+++ b/mkfile
@@ -2,7 +2,7 @@
 
 BIN=/$objtype/bin
 CFLAGS=-FTVw
-TARG=cfilter blur
+TARG=cfilter blur dither
 
 default:V: all