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