shithub: ifilter

Download patch

ref: d6b3a92f9317c5c2fd1b38f8332225005a212eb0
parent: 90204e60f5b1e8fb734f99898e7128052b47d869
author: phil9 <telephil9@gmail.com>
date: Thu Dec 8 07:01:22 EST 2022

major refactoring

	- Code factorization to limit individual programs to the algorithm implementation
	- Added helper functions
	- Moved target output to an image folder under bin

--- a/README
+++ b/README
@@ -9,7 +9,7 @@
 --------
 cfilter implements basic color filters.
 
-Usage: cfilter [-f factor] [grayscale|sepia|invert|shade|tint]
+Usage: image/cfilter -f grayscale|sepia|invert|shade|tint [-r ratio]
 
 Available filters are:
 - grayscale
@@ -18,22 +18,32 @@
 - shade(1)
 - tint(1)
 
-1: these filters use the factor argument
+1: these filters use the ratio argument to determine the transformation ratio.
 
 blur:
 -----
 blur implements blurring algorithms.
 
-Usage: blur [-s size] [box|pixelate]
+Usage: image/blur -f box|gaussian [-s size]
 
 Available filters are:
 - box: 3x3 box blur
-- 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
+- gaussian: Gaussian blur using `-s` as the convolution kernel radius (ie kernel will be a matrix r*r with r=2*s+1)
 
+pixelate:
+---------
+pixelate implements a pixelation algorithm.
+
+Usage: image/pixelate [-s size]
+size is the output pixel size (defaults to 8)
+
 dither:
 -------
 Floyd-Steinberg dithering algorithm.
 
-Usage: dither
+Usage: image/dither
 
+
+--
+Author: phil9
+License: MIT
--- /dev/null
+++ b/a.h
@@ -1,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+typedef uchar*(*filterfn)(uchar*, int, int, int);
+
+void	applyfilter(const char*, filterfn);
+int		clamp(int);
+uchar*	eallocbuf(ulong);
+
+enum
+{
+	Cblue,
+	Cgreen,
+	Cred,
+	Calpha,
+};
--- a/blur.c
+++ b/blur.c
@@ -1,26 +1,24 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <memdraw.h>
-
+#include "a.h"
+	
 typedef struct Filter Filter;
-int size;
 
 struct Filter
 {
 	const char *name;
-	uchar* (*filter)(uchar *data, int w, int h, int depth);
+	filterfn filter;
 };
 
+int size;
+
 uchar*
 box(uchar *data, int w, int h, int depth)
 {
 	uchar *out;
-	int x, y, c, s;
+	int n, x, y, c, s;
 
-	out = malloc(depth*w*h*sizeof(uchar));
-	if(out == nil)
-		return nil;
+	n = depth*w*h*sizeof(uchar);
+	out = eallocbuf(n);
+	memmove(out, data, n);
 	/* box blur is not defined for edge points */
 	for(y = 1; y < h - 1; y++){
 		for(x = 1; x < w - 1; x++){
@@ -44,44 +42,6 @@
 }
 
 uchar*
-pixelate(uchar *data, int w, int h, int depth)
-{
-	uchar *out;
-	int x, y, ox, oy, oi, sr, sg, sb;
-
-	if(size < 0)
-		sysfatal("pixelate filter needs a size argument");
-	out = malloc(depth*w*h*sizeof(uchar));
-	if(out == nil)
-		return nil;
-	for(y = 0; y < h; y += size){
-		for(x = 0; x < w; x += size){
-			sr = sg = sb = 0;
-			for(oy = y; oy < y+size; oy++){
-				for(ox = x; ox < x+size; ox++){
-					oi = (ox + w * oy) * depth;
-					sr += data[oi + 0]*data[oi + 0];
-					sg += data[oi + 1]*data[oi + 0];
-					sb += data[oi + 2]*data[oi + 0];
-				}
-			}
-			sr = sqrt(sr/(size*size));
-			sg = sqrt(sg/(size*size));
-			sb = sqrt(sb/(size*size));
-			for(oy = y; oy < y+size; oy++){
-				for(ox = x; ox < x+size; ox++){
-					oi = (ox + w * oy) * depth;
-					out[oi + 0] = sr;
-					out[oi + 1] = sg;
-					out[oi + 2] = sb;
-				}
-			}
-		}
-	}
-	return out;
-}
-
-uchar*
 gaussian(uchar *data, int w, int h, int depth)
 {
 	const double E = 2.7182818284590452354;
@@ -92,9 +52,9 @@
 
 	if(size < 0)
 		sysfatal("gaussian filter needs a size argument");
-	out = malloc(depth*w*h*sizeof(uchar));
-	if(out == nil)
-		return nil;
+	n = depth*w*h*sizeof(uchar);
+	out = eallocbuf(n);
+	memmove(out, data, n);
 	σ = (double)size / 2.0;
 	kw = 2 * size + 1;
 	k = malloc(kw*kw*sizeof(double));
@@ -141,13 +101,12 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-s size] [box|gaussian|pixelate]\n", argv0);
+	fprint(2, "usage: %s -f box|gaussian [-s size]\n", argv0);
 	exits("usage");
 }
 
 static Filter filters[] = {
 	{ "box", box },
-	{ "pixelate", pixelate },
 	{ "gaussian", gaussian },
 };
 
@@ -154,14 +113,22 @@
 void
 main(int argc, char *argv[])
 {
-	Memimage *o, *i;
-	int w, h, n;
-	uchar *buf, *out;
 	Filter *f;
+	char *fname;
+	int i;
 
-	size = -1;
+	size = 2;
 	f = nil;
 	ARGBEGIN{
+	case 'f':
+		fname = EARGF(usage());
+		for(i=0; i<nelem(filters); i++){
+			if(strcmp(fname, filters[i].name)==0){
+				f = filters+i;
+				break;
+			}
+		}
+		break;
 	case 's':
 		size = atoi(EARGF(usage()));
 		break;
@@ -169,38 +136,7 @@
 		usage();
 		break;
 	}ARGEND;
-	if(argc!=1)
-		usage();
-	for(n=0; n<nelem(filters); n++){
-		if(strcmp(*argv, filters[n].name)==0){
-			f = filters+n;
-			break;
-		}
-	}
 	if(f==nil)
 		usage();
-	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 = f->filter(buf, w, h, 4);
-	if(out == nil)
-		sysfatal("filter '%s' failed: %r", f->name);
-	print("   x8r8g8b8 %11d %11d %11d %11d ", 0, 0, w, h);
-	write(1, out, n);
-	exits(nil);
+	applyfilter(f->name, f->filter);
 }
-
--- a/cfilter.c
+++ b/cfilter.c
@@ -1,66 +1,88 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <memdraw.h>
+#include "a.h"
 
-#define MIN(x,y) ((x)<(y)?(x):(y))
-
 typedef struct Filter Filter;
 
 struct Filter
 {
 	const char *name;
-	void (*filter)(uchar[3], float);
+	filterfn f;
 };
 
-void
-grayscale(uchar p[3], float)
+float ratio;
+
+uchar*
+grayscale(uchar* data, int w, int h, int depth)
 {
-	uchar v = 0.2126*p[2] + 0.7152*p[1] + 0.0722*p[0];
-	p[0] = p[1] = p[2] = v;
+	uchar v;
+	int i;
+
+	for(i = 0; i < w*h*depth; i += 4){
+		v = 0.2126*data[i+Cred] + 0.7152*data[i+Cgreen] + 0.0722*data[i+Cblue];
+		data[i+Cred] = data[i+Cgreen] = data[i+Cblue] = v;
+	}
+	return data;
 }
 
-void
-sepia(uchar p[3], float)
+uchar*
+sepia(uchar* data, int w, int h, int depth)
 {
 	float r, g, b;
+	int i;
 
-	r = 0.393*p[2] + 0.769*p[1] + 0.189*p[0];
-	g = 0.349*p[2] + 0.686*p[1] + 0.168*p[0];
-	b = 0.272*p[2] + 0.534*p[1] + 0.131*p[0];
-	p[2] = MIN(r, 255);
-	p[1] = MIN(g, 255);
-	p[0] = MIN(b, 255);
+	for(i = 0; i < w*h*depth; i += 4){
+		r = 0.393*data[i+Cred] + 0.769*data[i+Cgreen] + 0.189*data[i+Cblue];
+		g = 0.349*data[i+Cred] + 0.686*data[i+Cgreen] + 0.168*data[i+Cblue];
+		b = 0.272*data[i+Cred] + 0.534*data[i+Cgreen] + 0.131*data[i+Cblue];
+		data[i+Cred]   = clamp(r);
+		data[i+Cgreen] = clamp(g);
+		data[i+Cblue]  = clamp(b);
+	}
+	return data;
 }
 
-void
-invert(uchar p[3], float)
+uchar*
+invert(uchar* data, int w, int h, int depth)
 {
-	p[0] = 255 - p[0];
-	p[1] = 255 - p[1];
-	p[2] = 255 - p[2];
+	int i;
+
+	for(i = 0; i < w*h*depth; i += 4){
+		data[i+Cred]   = 255 - data[i+Cred];
+		data[i+Cgreen] = 255 - data[i+Cgreen];
+		data[i+Cblue]  = 255 - data[i+Cblue];
+	}
+	return data;
 }
 
-void
-shade(uchar p[3], float factor)
+uchar*
+shade(uchar* data, int w, int h, int depth)
 {
-	p[0] = p[0] * (1 - factor);
-	p[1] = p[1] * (1 - factor);
-	p[2] = p[2] * (1 - factor);
+	int i;
+
+	for(i = 0; i < w*h*depth; i += 4){
+		data[i+Cred]   *= (1 - ratio);
+		data[i+Cgreen] *= (1 - ratio);
+		data[i+Cblue]  *= (1 - ratio);
+	}
+	return data;
 }
 
-void
-tint(uchar p[3], float factor)
+uchar*
+tint(uchar* data, int w, int h, int depth)
 {
-	p[0] = p[0] + (255.0 - p[0]) * factor;
-	p[1] = p[1] + (255.0 - p[1]) * factor;
-	p[2] = p[2] + (255.0 - p[2]) * factor;
+	int i;
+
+	for(i = 0; i < w*h*depth; i += 4){
+		data[i+Cred]   += (255 - data[i+Cred]) * ratio;
+		data[i+Cgreen] += (255 - data[i+Cgreen]) * ratio;
+		data[i+Cblue]  += (255 - data[i+Cblue]) * ratio;
+	}
+	return data;
 }
 
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-f factor] [grayscale|sepia|invert|shade|tint]\n", argv0);
+	fprint(2, "usage: %s -f grayscale|sepia|invert|shade|tint [-r ratio]\n", argv0);
 	exits("usage");
 }
 
@@ -72,72 +94,37 @@
 	{ "tint", tint },
 };
 
-int
-isalpha(ulong chan)
-{
-	ulong t;
-
-	for(t = chan; t; t >>= 8)
-		if(TYPE(t) == CAlpha)
-			return 1;
-	return 0;
-}
-
 void
 main(int argc, char *argv[])
 {
-	Memimage *o, *i;
-	int w, h, p, n, a;
-	uchar *buf;
 	Filter *f;
-	float factor;
+	char *fname;
+	int n;
 
 	f = nil;
-	factor = 0.0;
+	ratio = 0.0;
 	ARGBEGIN{
 	case 'f':
-		factor = (float)atof(EARGF(usage()));
+		fname = EARGF(usage());
+		for(n=0; n<nelem(filters); n++){
+			if(strcmp(fname, filters[n].name)==0){
+				f = filters+n;
+				break;
+			}
+		}
 		break;
+	case 'r':
+		ratio = (float)atof(EARGF(usage()));
+		break;
 	default:
 		usage();
 		break;
 	}ARGEND;
-	if(argc!=1)
-		usage();
-	for(n=0; n<nelem(filters); n++){
-		if(strcmp(*argv, filters[n].name)==0){
-			f = filters+n;
-			break;
-		}
-	}
 	if(f==nil)
 		usage();
-	if(factor<0.0 || factor>1.0){
-		fprint(2, "factor should be between 0.0 and 1.0\n");
-		exits("invalid factor");
+	if(ratio<0.0 || ratio>1.0){
+		fprint(2, "ratio should be between 0.0 and 1.0\n");
+		exits("invalid ratio");
 	}
-	if(memimageinit()<0)
-		sysfatal("memimageinit: %r");
-	o = readmemimage(0);
-	if(o==nil)
-		sysfatal("readmemimage: %r");
-	a = isalpha(o->chan);
-	i = allocmemimage(o->r, a ? ARGB32 : 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);
-	for(p = 0; p < n; p+=4)
-		f->filter(buf+p, factor);
-	print("   %c8r8g8b8 %11d %11d %11d %11d ", a ? 'a' : 'x', 0, 0, w, h);
-	write(1, buf, n);
-	exits(nil);
+	applyfilter(f->name, f->f);
 }
-
--- /dev/null
+++ b/common.c
@@ -1,0 +1,66 @@
+#include "a.h"
+
+int
+isalpha(ulong chan)
+{
+	ulong t;
+
+	for(t = chan; t; t >>= 8)
+		if(TYPE(t) == CAlpha)
+			return 1;
+	return 0;
+}
+
+void
+applyfilter(const char *fname, filterfn f)
+{
+	Memimage *o, *i;
+	int w, h, n, a;
+	uchar *buf, *out;
+
+	if(memimageinit()<0)
+		sysfatal("memimageinit: %r");
+	o = readmemimage(0);
+	if(o==nil)
+		sysfatal("readmemimage: %r");
+	a = isalpha(o->chan);
+	i = allocmemimage(rectsubpt(o->r, o->r.min), a ? ARGB32 : 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 = f(buf, w, h, 4);
+	if(out == nil)
+		sysfatal("%s failed: %r", fname);
+	print("   %c8r8g8b8 %11d %11d %11d %11d ", a ? 'a' : 'x', 0, 0, w, h);
+	write(1, out, n);
+	exits(nil);
+}
+
+int
+clamp(int n)
+{
+	if(n < 0)
+		n = 0;
+	else if(n > 255)
+		n = 255;
+	return n;
+}
+
+uchar*
+eallocbuf(ulong s)
+{
+	uchar *p;
+
+	p = malloc(s);
+	if(p == nil)
+		sysfatal("malloc: %r");
+	return p;
+}
--- a/dither.c
+++ b/dither.c
@@ -1,18 +1,5 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <memdraw.h>
+#include "a.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)
 {
@@ -21,11 +8,9 @@
 	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];
+	n = depth*w*h*sizeof(uchar);
+	out = eallocbuf(n);
+	memmove(out, data, n);
 	for(y = 0; y < h; y++){
 		for(x = 0; x < w; x++){
 			for(c = 0; c < 3; c++){ /* B G R */
@@ -55,37 +40,10 @@
 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);
+	applyfilter("dither", dither);
 }
-
--- a/mkfile
+++ b/mkfile
@@ -1,10 +1,17 @@
 </$objtype/mkfile
 
-BIN=/$objtype/bin
+BIN=/$objtype/bin/image
 CFLAGS=-FTVw
-TARG=cfilter blur dither
+TARG=cfilter pixelate blur dither
+HFILES=a.h
+OFILES=common.$O
 
 default:V: all
 
 </sys/src/cmd/mkmany
+
+install:V:
+	mkdir -p $BIN
+	for(i in $TARG)
+		mk $MKFLAGS $i.install
 
--- /dev/null
+++ b/pixelate.c
@@ -1,0 +1,63 @@
+#include "a.h"
+
+int size;
+
+uchar*
+pixelate(uchar *data, int w, int h, int depth)
+{
+	uchar *out;
+	int x, y, ox, oy, oi, sr, sg, sb;
+
+	if(size < 0)
+		sysfatal("pixelate filter needs a size argument");
+	out = malloc(depth*w*h*sizeof(uchar));
+	if(out == nil)
+		return nil;
+	for(y = 0; y < h; y += size){
+		for(x = 0; x < w; x += size){
+			sr = sg = sb = 0;
+			for(oy = y; oy < y+size; oy++){
+				for(ox = x; ox < x+size; ox++){
+					oi = (ox + w * oy) * depth;
+					sr += data[oi + 0]*data[oi + 0];
+					sg += data[oi + 1]*data[oi + 0];
+					sb += data[oi + 2]*data[oi + 0];
+				}
+			}
+			sr = sqrt(sr/(size*size));
+			sg = sqrt(sg/(size*size));
+			sb = sqrt(sb/(size*size));
+			for(oy = y; oy < y+size; oy++){
+				for(ox = x; ox < x+size; ox++){
+					oi = (ox + w * oy) * depth;
+					out[oi + 0] = sr;
+					out[oi + 1] = sg;
+					out[oi + 2] = sb;
+				}
+			}
+		}
+	}
+	return out;
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-s size] [box|gaussian|pixelate]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	size = 8;
+	ARGBEGIN{
+	case 's':
+		size = atoi(EARGF(usage()));
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND;
+	applyfilter("pixelate", pixelate);
+}