shithub: front

Download patch

ref: ec028184cb8ceae024565bf68854316fd9e59180
parent: 73137841b26b784846ed1925d8c2f006b60e5205
author: rodri <rgl@antares-labs.eu>
date: Fri Aug 8 11:38:14 EDT 2025

libmemdraw: add memaffinewarp routine

--- a/sys/include/memdraw.h
+++ b/sys/include/memdraw.h
@@ -150,6 +150,7 @@
 extern void	memarc(Memimage*, Point, int, int, int, Memimage*, Point, int, int, int);
 extern Rectangle	memlinebbox(Point, Point, int, int, int);
 extern int	memlineendsize(int);
+extern int	memaffinewarp(Memimage*, Rectangle, Memimage*, Point, uchar*);
 extern void	_memmkcmap(void);
 extern int	memimageinit(void);
 
--- a/sys/man/2/memdraw
+++ b/sys/man/2/memdraw
@@ -24,6 +24,7 @@
 memfillpoly,
 memimageline,
 memimagedraw,
+memaffinewarp,
 drawclip,
 drawclipnorepl,
 memlinebbox,
@@ -133,6 +134,8 @@
 	   int end1, int radius, Memimage *src, Point sp, Drawop op)
 void	memimagedraw(Memimage *dst, Rectangle r, Memimage *src,
 	   Point sp, Memimage *mask, Point mp, Drawop op)
+int	memaffinewarp(Memimage *dst, Rectangle r, Memimage *src,
+	   Point sp, uchar *m)
 .PP
 .ft L
 .nf
@@ -377,6 +380,23 @@
 .BR Memsubfont s
 rather than
 .BR Font s.
+.PP
+.I Memaffinewarp
+applies an affine transformation defined by
+.B m
+to the
+.BR src ,
+where
+.B m
+is a 2×3 row-major matrix of
+.BR double s
+(usually a
+.IR geometry (2)
+Matrix), and stores the result in the
+.BR dst .
+Both
+.BR Memimage s
+must have the same channel description and use 8-bit channels.
 .PP
 .I Drawclip
 takes the images involved in a draw operation,
--- a/sys/src/libmemdraw/mkfile
+++ b/sys/src/libmemdraw/mkfile
@@ -21,6 +21,7 @@
 	subfont.$O\
 	unload.$O\
 	write.$O\
+	warp.$O\
 
 </sys/src/cmd/mksyslib
 
--- /dev/null
+++ b/sys/src/libmemdraw/warp.c
@@ -1,0 +1,121 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+
+#define lerp(a,b,t)	((a) + ((((b) - (a))*(t))>>16))
+
+typedef struct Sampler Sampler;
+struct Sampler
+{
+	Memimage *i;
+	uchar *a;
+	int bpp;
+	int bpl;
+	int cmask;
+};
+
+static ulong
+edgehandler(Sampler*, Point*)
+{
+	return 0;	/* constant */
+}
+
+static ulong
+sample(Sampler *s, Point p)
+{
+	ulong c;
+
+	if(ptinrect(p, s->i->r)){
+		c = *(ulong*)(s->a + p.y*s->bpl + p.x*s->bpp);
+		c &= s->cmask;
+	}else
+		c = edgehandler(s, &p);
+	return c;
+}
+
+int
+memaffinewarp(Memimage *d, Rectangle r, Memimage *s, Point sp0, uchar *ma)
+{
+	Sampler samp;
+	Matrix m;
+	Point sp, dp;
+	Point2 p2;
+	double Δx, Δy;
+	int Δx2, Δy2;
+	ulong c00, c01, c10, c11;
+	uchar c0, c1, c, *a;
+
+	if(s->depth < 8 || (s->depth & 3) != 0){
+		werrstr("unsupported image format");
+		return -1;
+	}
+
+	if(d->chan != s->chan){
+		werrstr("image formats differ");
+		return -1;
+	}
+
+	if(rectclip(&r, d->r) == 0)
+		return 0;
+
+	samp.i = s;
+	samp.a = s->data->bdata + s->zero;
+	samp.bpp = s->depth >> 3;
+	samp.bpl = sizeof(ulong)*s->width;
+	samp.cmask = (1ULL << 8*s->nchan) - 1;
+
+	memmove(m, ma, 2*3*8);
+	m[2][0] = 0; m[2][1] = 0; m[2][2] = 1;
+	invm(m);
+
+	for(dp.y = r.min.y; dp.y < r.max.y; dp.y++)
+	for(dp.x = r.min.x; dp.x < r.max.x; dp.x++){
+		a = byteaddr(d, dp);
+
+		p2 = xform((Point2){dp.x - r.min.x, dp.y - r.min.y, 1}, m);
+		sp.x = p2.x;
+		sp.y = p2.y;
+
+		Δx = p2.x - sp.x;
+		Δx2 = Δx * (1<<16);
+		Δy = p2.y - sp.y;
+		Δy2 = Δy * (1<<16);
+
+		sp.x += sp0.x;
+		sp.y += sp0.y;
+		c00 = sample(&samp, sp);
+		sp.x++;
+		c01 = sample(&samp, sp);
+		sp.x--; sp.y++;
+		c10 = sample(&samp, sp);
+		sp.x++;
+		c11 = sample(&samp, sp);
+
+		switch(s->nchan){
+		case 4:
+			c0 = c00 >> 8*3 & 0xFF; c0 = lerp(c0, c01 >> 8*3 & 0xFF, Δx2);
+			c1 = c10 >> 8*3 & 0xFF; c1 = lerp(c1, c11 >> 8*3 & 0xFF, Δx2);
+			c  = lerp(c0, c1, Δy2);
+			*(a + 3) = c;
+		case 3:
+			c0 = c00 >> 8*2 & 0xFF; c0 = lerp(c0, c01 >> 8*2 & 0xFF, Δx2);
+			c1 = c10 >> 8*2 & 0xFF; c1 = lerp(c1, c11 >> 8*2 & 0xFF, Δx2);
+			c  = lerp(c0, c1, Δy2);
+			*(a + 2) = c;
+		case 2:
+			c0 = c00 >> 8*1 & 0xFF; c0 = lerp(c0, c01 >> 8*1 & 0xFF, Δx2);
+			c1 = c10 >> 8*1 & 0xFF; c1 = lerp(c1, c11 >> 8*1 & 0xFF, Δx2);
+			c  = lerp(c0, c1, Δy2);
+			*(a + 1) = c;
+		case 1:
+			c0 = c00 >> 8*0 & 0xFF; c0 = lerp(c0, c01 >> 8*0 & 0xFF, Δx2);
+			c1 = c10 >> 8*0 & 0xFF; c1 = lerp(c1, c11 >> 8*0 & 0xFF, Δx2);
+			c  = lerp(c0, c1, Δy2);
+			*(a + 0) = c;
+		}
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/sys/src/libmemdraw/warptest.c
@@ -1,0 +1,140 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+
+uvlong t0;
+
+void
+profbegin(void)
+{
+	char buf[128];
+	int fd;
+
+	snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+	fd = open(buf, OWRITE);
+	if(fd < 0)
+		sysfatal("open: %r");
+	fprint(fd, "profile\n");
+	close(fd);
+	t0 = nsec();
+}
+
+void
+profend(void)
+{
+	char buf[128];
+	double dt;
+
+	dt = (nsec() - t0)/1e6;
+	fprint(2, "dt: %fms\n", dt);
+
+	snprint(buf, sizeof(buf), "%d", getpid());
+	switch(fork()){
+	case -1:
+		abort();
+	case 0:
+		dup(2, 1);
+		print("tprof %s\n", buf);
+		execl("/bin/tprof", "tprof", buf, nil);
+		abort();
+	default:
+		free(wait());
+		break;
+	}
+}
+
+#define min(a,b)	((a)<(b)?(a):(b))
+#define max(a,b)	((a)>(b)?(a):(b))
+Rectangle
+getbbox(Rectangle *sr, Matrix m)
+{
+	Point2 p0, p1, p2, p3;
+
+	p0 = Pt2(sr->min.x, sr->min.y, 1);
+	p0 = xform(p0, m);
+	p1 = Pt2(sr->max.x, sr->min.y, 1);
+	p1 = xform(p1, m);
+	p2 = Pt2(sr->min.x, sr->max.y, 1);
+	p2 = xform(p2, m);
+	p3 = Pt2(sr->max.x, sr->max.y, 1);
+	p3 = xform(p3, m);
+
+	p0.x = min(min(min(p0.x, p1.x), p2.x), p3.x);
+	p0.y = min(min(min(p0.y, p1.y), p2.y), p3.y);
+	p1.x = max(max(max(p1.x, p1.x), p2.x), p3.x);
+	p1.y = max(max(max(p1.y, p1.y), p2.y), p3.y);
+	return Rect(p0.x, p0.y, p1.x, p1.y);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-s sx sy] [-t tx ty] [-r θ]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Memimage *dst, *src;
+	Rectangle dr;
+	double sx, sy, tx, ty, θ, c, s;
+
+	sx = sy = 1;
+	tx = ty = 0;
+	θ = 0;
+	ARGBEGIN{
+	case 's':
+		sx = strtod(EARGF(usage()), nil);
+		sy = strtod(EARGF(usage()), nil);
+		break;
+	case 't':
+		tx = strtod(EARGF(usage()), nil);
+		ty = strtod(EARGF(usage()), nil);
+		break;
+	case 'r':
+		θ = strtod(EARGF(usage()), nil)*DEG;
+		break;
+	default:
+		usage();
+	}ARGEND;
+	if(argc != 0)
+		usage();
+
+	if(memimageinit() != 0)
+		sysfatal("memimageinit: %r");
+
+	src = readmemimage(0);
+	c = cos(θ);
+	s = sin(θ);
+	Matrix S = {
+		sx, 0, 0,
+		0, sy, 0,
+		0, 0, 1,
+	}, T = {
+		1, 0, tx,
+		0, 1, ty,
+		0, 0, 1,
+	}, R = {
+		c, -s, 0,
+		s, c, 0,
+		0, 0, 1,
+	};
+
+	mulm(S, R);
+	mulm(T, S);
+
+//	dr = getbbox(&src->r, T);
+	dr = src->r;
+	dst = allocmemimage(dr, src->chan);
+
+
+	profbegin();
+	memaffinewarp(dst, dst->r, src, src->r.min, (uchar*)T);
+	profend();
+	writememimage(1, dst);
+
+	exits(nil);
+}
--