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);
+}