shithub: imagetools

Download patch

ref: 912ab03a797d34ae0166b825aefde2207e29c8a7
author: rodri <rgl@antares-labs.eu>
date: Tue Apr 29 18:48:35 EDT 2025

import from the brokentoys repo

--- /dev/null
+++ b/add.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static int saturate;
+
+static int
+opadd(uchar b1, uchar b2)
+{
+	return b1 + b2;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-s] img1 img2 [imgs...]\n", argv0);
+	exits(nil);
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *imgs[2];
+	int i, j, fd;
+
+	ARGBEGIN{
+	case 's': saturate++; break;
+	default: usage();
+	}ARGEND;
+	if(argc < 2)
+		usage();
+
+	for(i = 0; i < argc; i++){
+		j = i != 0;
+		fd = eopen(argv[i], OREAD);
+		imgs[j] = ereadmemimage(fd);
+		close(fd);
+
+		if(j){
+			imgbinop(imgs[0], imgs[1], opadd, saturate);
+			freememimage(imgs[1]);
+		}
+	}
+
+	ewritememimage(1, imgs[0]);
+	freememimage(imgs[0]);
+
+	exits(nil);
+}
--- /dev/null
+++ b/com.c
@@ -1,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static int
+opcom(uchar b)
+{
+	return ~b;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s\n", argv0);
+	exits(nil);
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *img;
+
+	ARGBEGIN{
+	default: usage();
+	}ARGEND;
+	if(argc != 0)
+		usage();
+
+	img = ereadmemimage(0);
+	imgunaop(img, opcom, 0);
+
+	ewritememimage(1, img);
+	freememimage(img);
+
+	exits(nil);
+}
--- /dev/null
+++ b/convolution.c
@@ -1,0 +1,265 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static int dim;
+static int saturate;
+
+static void
+fprintm(int fd, double *m, int dim)
+{
+	int i, j;
+
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		fprint(fd, "%g%c", m[j*dim+i], i == dim-1? '\n': '\t');
+}
+
+static char *
+getline(Biobuf *b)
+{
+	char *line;
+
+	if((line = Brdline(b, '\n')) != nil)
+		line[Blinelen(b)-1] = 0;
+	return line;
+}
+
+static double *
+readkernel(int fd)
+{
+	Biobuf *bin;
+	double *kern;
+	char *line, *f[10];
+	int nf, i, j;
+
+	bin = Bfdopen(fd, OREAD);
+	if(bin == nil)
+		sysfatal("Bfdopen: %r");
+	do{
+		line = getline(bin);
+		if(line == nil)
+			sysfatal("Brdline: %r");
+		dim = tokenize(line, f, nelem(f));
+	}while(dim < 1);
+	kern = emalloc(dim*dim*sizeof(double));
+	for(j = i = 0; i < dim; i++)
+		kern[j*dim+i] = strtod(f[i], nil);
+	j++;
+
+	while((line = getline(bin)) != nil){
+		if((nf = tokenize(line, f, nelem(f))) < 1)
+			continue;
+		if(nf != dim)
+			sysfatal("expected a %dx%d matrix", dim, dim);
+		for(i = 0; i < dim; i++)
+			kern[j*dim+i] = strtod(f[i], nil);
+		j++;
+	}
+	Bterm(bin);
+
+	if(j != dim)
+		sysfatal("expected a %dx%d matrix", dim, dim);
+
+	return kern;
+}
+
+static double
+coeffsum(double *k, int dim)
+{
+	double s;
+	int i, j;
+
+	s = 0;
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		s += k[j*dim+i];
+	return s;
+}
+
+static void
+normalize(double *k, int dim)
+{
+	double denom;
+	int i, j;
+
+	denom = coeffsum(k, dim);
+	if(denom == 0)
+		return;
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		k[j*dim+i] /= denom;
+}
+
+static double *
+reverse(double *k, int dim)
+{
+	double *ck;
+	int i, j;
+
+	ck = emalloc(dim*dim*sizeof(double));
+
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		ck[j*dim+i] = k[(dim-j-1)*dim+(dim-i-1)];
+	return ck;
+}
+
+static void
+modulate(double *d, double *s, int dim)
+{
+	int i, j;
+
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		d[j*dim+i] *= s[j*dim+i];
+}
+
+static double
+convolve(double *d, double *s, int dim)
+{
+	int i, j;
+	double r;
+
+	modulate(d, s, dim);
+	r = 0;
+	for(j = 0; j < dim; j++)
+	for(i = 0; i < dim; i++)
+		r += d[j*dim+i];
+	return r;
+}
+
+static uchar
+sample(Memimage *i, Point p, int off)
+{
+	if(p.x < i->r.min.x || p.y < i->r.min.y
+	|| p.x >= i->r.max.x || p.y >= i->r.max.y)
+		return 0;	/* edge handler: constant */
+	return *(byteaddr(i, p) + off);
+}
+
+static void
+mksubrects(Rectangle *sr, int nsr, Rectangle *r)
+{
+	int i, Δy;
+
+	sr[0] = *r;
+	Δy = Dy(sr[0])/nsr;
+	sr[0].max.y = sr[0].min.y + Δy;
+	for(i = 1; i < nsr; i++)
+		sr[i] = rectaddpt(sr[i-1], Pt(0,Δy));
+	if(sr[nsr-1].max.y < r->max.y)
+		sr[nsr-1].max.y = r->max.y;
+}
+
+static void
+subimgconvolution(Memimage *d, Memimage *s, Rectangle *r, double *k, int dim, double denom)
+{
+	double **im;
+	Point imc, p, cp;
+	double c;
+	int i;
+
+	im = emalloc(d->nchan*sizeof(double*));
+	for(i = 0; i < d->nchan; i++)
+		im[i] = emalloc(dim*dim*sizeof(double));
+
+	imc = Pt(dim/2, dim/2);
+
+	for(p.y = r->min.y; p.y < r->max.y; p.y++)
+	for(p.x = r->min.x; p.x < r->max.x; p.x++){
+		for(cp.y = 0; cp.y < dim; cp.y++)
+		for(cp.x = 0; cp.x < dim; cp.x++){
+			for(i = 0; i < d->nchan; i++)
+				im[i][cp.y*dim + cp.x] = sample(s, addpt(p, subpt(cp, imc)), i);
+		}
+		for(i = 0; i < d->nchan; i++){
+			c = fabs(convolve(im[i], k, dim) * denom);
+			if(saturate)
+				c = clamp(c, 0, 0xFF);
+			*(byteaddr(d, p) + i) = c;
+		}
+	}
+
+	for(i = 0; i < d->nchan; i++)
+		free(im[i]);
+	free(im);
+}
+
+static void
+imgconvolution(Memimage *d, Memimage *s, double *k, int dim)
+{
+	double denom;
+	Rectangle *subr;
+	char *nprocs;
+	int nproc, i;
+
+	denom = coeffsum(k, dim);
+	denom = denom == 0? 1: 1/denom;
+
+	nprocs = getenv("NPROC");
+	if(nprocs == nil || (nproc = strtoul(nprocs, nil, 10)) < 2)
+		nproc = 1;
+	free(nprocs);
+
+	subr = emalloc(nproc*sizeof(*subr));
+	mksubrects(subr, nproc, &s->r);
+
+	for(i = 0; i < nproc; i++){
+		switch(rfork(RFPROC|RFMEM)){
+		case -1:
+			sysfatal("rfork: %r");
+		case 0:
+			subimgconvolution(d, s, &subr[i], k, dim, denom);
+			exits(nil);
+		}
+	}
+	while(waitpid() != -1)
+		;
+
+	free(subr);
+}
+
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-s] kernfile\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *in, *out;
+	double *kern, *ckern;
+	int fd;
+
+	ARGBEGIN{
+	case 's': saturate++; break;
+	default: usage();
+	}ARGEND;
+	if(argc != 1)
+		usage();
+
+	fd = eopen(argv[0], OREAD);
+	kern = readkernel(fd);
+	close(fd);
+	ckern = reverse(kern, dim);
+	free(kern);
+	kern = ckern;
+
+	in = ereadmemimage(0);
+	out = eallocmemimage(in->r, in->chan);
+
+	imgconvolution(out, in, kern, dim);
+	ewritememimage(1, out);
+
+	freememimage(out);
+	freememimage(in);
+	free(kern);
+	exits(nil);
+}
--- /dev/null
+++ b/filter/blur.box16
@@ -1,0 +1,4 @@
+0.0625 0.0625 0.0625 0.0625
+0.0625 0.0625 0.0625 0.0625
+0.0625 0.0625 0.0625 0.0625
+0.0625 0.0625 0.0625 0.0625
--- /dev/null
+++ b/filter/blur.box25
@@ -1,0 +1,5 @@
+0.04 0.04 0.04 0.04 0.04
+0.04 0.04 0.04 0.04 0.04
+0.04 0.04 0.04 0.04 0.04
+0.04 0.04 0.04 0.04 0.04
+0.04 0.04 0.04 0.04 0.04
--- /dev/null
+++ b/filter/blur.box9
@@ -1,0 +1,3 @@
+0.11111 0.11111 0.11111
+0.11111 0.11111 0.11111
+0.11111 0.11111 0.11111
--- /dev/null
+++ b/filter/edge.4
@@ -1,0 +1,3 @@
+-1 -1 -1
+-1 4 -1
+-1 -1 -1
--- /dev/null
+++ b/filter/edge.5
@@ -1,0 +1,3 @@
+-1 -1 -1
+-1 5 -1
+-1 -1 -1
--- /dev/null
+++ b/filter/edge.diag
@@ -1,0 +1,3 @@
+-1 0 1
+0 0 0
+1 0 -1
--- /dev/null
+++ b/filter/sharpen.4
@@ -1,0 +1,3 @@
+0 -1 0
+-1 4 -1
+0 -1 0
--- /dev/null
+++ b/filter/sharpen.5
@@ -1,0 +1,3 @@
+0 -1 0
+-1 5 -1
+0 -1 0
--- /dev/null
+++ b/filter/sobel.Gx
@@ -1,0 +1,3 @@
+-1 0 1
+-2 0 2
+-1 0 1
--- /dev/null
+++ b/filter/sobel.Gy
@@ -1,0 +1,3 @@
+-1 -2 -1
+0 0 0
+1 2 1
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,12 @@
+#define min(a,b)	((a)<(b)?(a):(b))
+#define max(a,b)	((a)>(b)?(a):(b))
+#define clamp(a,b,c)	min(max(a,b),c)
+
+void *emalloc(ulong);
+void *erealloc(void*, ulong);
+int eopen(char*, int);
+Memimage *eallocmemimage(Rectangle, ulong);
+Memimage *ereadmemimage(int);
+int ewritememimage(int, Memimage*);
+void imgbinop(Memimage*, Memimage*, int(*)(uchar, uchar), int);
+void imgunaop(Memimage*, int(*)(uchar), int);
--- /dev/null
+++ b/genmask.c
@@ -1,0 +1,61 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static Memimage *
+genmask(Memimage *s, ulong clr)
+{
+	Memimage *m;
+	Point p;
+	int i;
+	uchar b, sh;
+
+	m = eallocmemimage(s->r, GREY1);
+
+	for(p.y = s->r.min.y; p.y < s->r.max.y; p.y++)
+	for(p.x = s->r.min.x; p.x < s->r.max.x; p.x++){
+		b = 0;
+		for(i = 0; i < s->nchan; i++)
+			if(*(byteaddr(s, p) + i) != (clr>>i*8 & 0xFF)){
+				b = 1;
+				break;
+			}
+
+		sh = (p.y*Dx(m->r) + p.x) & 7;
+		*byteaddr(m, p) |= b << (7-sh);
+	}
+
+	return m;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s clrcol\n", argv0);
+	exits(nil);
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *img, *mask;
+	ulong clrcol;
+
+	ARGBEGIN{
+	default: usage();
+	}ARGEND;
+	if(argc != 1)
+		usage();
+
+	clrcol = strtoul(argv[0], nil, 0);
+	img = ereadmemimage(0);
+	mask = genmask(img, clrcol);
+	freememimage(img);
+
+	ewritememimage(1, mask);
+	freememimage(mask);
+
+	exits(nil);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,17 @@
+</$objtype/mkfile
+
+BIN=$home/bin/$objtype
+TARG=\
+	convolution\
+	add\
+	sub\
+	sobel\
+	com\
+	genmask\
+
+OFILES=\
+	utils.$O\
+
+HFILES=fns.h
+
+</sys/src/cmd/mkmany
--- /dev/null
+++ b/sobel.c
@@ -1,0 +1,55 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static int saturate;
+static int byangle;
+
+static int
+opsobelΔ(uchar b1, uchar b2)
+{
+	return sqrt(b1*b1 + b2*b2);
+}
+
+static int
+opsobelΘ(uchar b1, uchar b2)
+{
+	return atan2(b2, b1);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-sa] Gx Gy\n", argv0);
+	exits(nil);
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *imgs[2];
+	int i, fd;
+
+	ARGBEGIN{
+	case 's': saturate++; break;
+	case 'a': byangle++; break;
+	default: usage();
+	}ARGEND;
+	if(argc != 2)
+		usage();
+
+	for(i = 0; i < argc; i++){
+		fd = eopen(argv[i], OREAD);
+		imgs[i] = ereadmemimage(fd);
+		close(fd);
+	}
+
+	imgbinop(imgs[0], imgs[1], byangle? opsobelΘ: opsobelΔ, saturate);
+	ewritememimage(1, imgs[0]);
+
+	freememimage(imgs[1]);
+	freememimage(imgs[0]);
+	exits(nil);
+}
--- /dev/null
+++ b/sub.c
@@ -1,0 +1,47 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+static int saturate;
+
+static int
+opsub(uchar b1, uchar b2)
+{
+	return b1 - b2;
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-s] img1 img2\n", argv0);
+	exits(nil);
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *imgs[2];
+	int i, fd;
+
+	ARGBEGIN{
+	case 's': saturate++; break;
+	default: usage();
+	}ARGEND;
+	if(argc != 2)
+		usage();
+
+	for(i = 0; i < argc; i++){
+		fd = eopen(argv[i], OREAD);
+		imgs[i] = ereadmemimage(fd);
+		close(fd);
+	}
+
+	imgbinop(imgs[0], imgs[1], opsub, saturate);
+	ewritememimage(1, imgs[0]);
+
+	freememimage(imgs[1]);
+	freememimage(imgs[0]);
+	exits(nil);
+}
--- /dev/null
+++ b/utils.c
@@ -1,0 +1,116 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include "fns.h"
+
+void *
+emalloc(ulong n)
+{
+	void *p;
+
+	p = malloc(n);
+	if(p == nil)
+		sysfatal("malloc: %r");
+	setmalloctag(p, getcallerpc(&n));
+	return p;
+}
+
+void *
+erealloc(void *p, ulong n)
+{
+	void *np;
+
+	np = realloc(p, n);
+	if(np == nil){
+		if(n == 0)
+			return nil;
+		sysfatal("realloc: %r");
+	}
+	if(p == nil)
+		setmalloctag(np, getcallerpc(&p));
+	else
+		setrealloctag(np, getcallerpc(&p));
+	return np;
+}
+
+int
+eopen(char *file, int omode)
+{
+	int fd;
+
+	fd = open(file, omode);
+	if(fd < 0)
+		sysfatal("open: %r");
+	return fd;
+}
+
+Memimage *
+eallocmemimage(Rectangle r, ulong chan)
+{
+	Memimage *i;
+
+	i = allocmemimage(r, chan);
+	if(i == nil)
+		sysfatal("allocmemimage: %r");
+	memfillcolor(i, DTransparent);
+	return i;
+}
+
+Memimage *
+ereadmemimage(int fd)
+{
+	Memimage *i;
+
+	i = readmemimage(fd);
+	if(i == nil)
+		sysfatal("readmemimage: %r");
+	return i;
+}
+
+int
+ewritememimage(int fd, Memimage *i)
+{
+	int rc;
+
+	rc = writememimage(fd, i);
+	if(rc < 0)
+		sysfatal("writememimage: %r");
+	return rc;
+}
+
+void
+imgbinop(Memimage *i1, Memimage *i2, int(*op)(uchar, uchar), int saturate)
+{
+	uchar *p1, *p1e, *p2;
+	int c;
+
+	if(i1->chan != i2->chan || !eqrect(i1->r, i2->r))
+		sysfatal("images shapes differ");
+
+	p1 = i1->data->bdata;
+	p2 = i2->data->bdata;
+	p1e = p1 + Dx(i1->r)*Dy(i1->r)*i1->nchan;
+	while(p1 < p1e){
+		c = op(*p1, *p2++);
+		if(saturate)
+			c = clamp(c, 0, 0xFF);
+		*p1++ = c;
+	}
+}
+
+void
+imgunaop(Memimage *i1, int(*op)(uchar), int saturate)
+{
+	uchar *p1, *p1e;
+	int c;
+
+	p1 = i1->data->bdata;
+	p1e = p1 + Dx(i1->r)*Dy(i1->r)*i1->nchan;
+	while(p1 < p1e){
+		c = op(*p1);
+		if(saturate)
+			c = clamp(c, 0, 0xFF);
+		*p1++ = c;
+	}
+}
--