summaryrefslogtreecommitdiffstats
path: root/src/cmd/page/rotate.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-01-04 21:23:50 +0000
committerrsc <devnull@localhost>2005-01-04 21:23:50 +0000
commit24c02865d8fcc97d1fb5cb9281810d8074aa5eb1 (patch)
tree3bb16794ded14341ee58d9f1f173ce92fb4d2be7 /src/cmd/page/rotate.c
parentd1e9002f81f14fbfef1ebc4261edccd9eb97b72c (diff)
downloadplan9port-24c02865d8fcc97d1fb5cb9281810d8074aa5eb1.tar.gz
plan9port-24c02865d8fcc97d1fb5cb9281810d8074aa5eb1.zip
placeholder; does not yet build
Diffstat (limited to 'src/cmd/page/rotate.c')
-rw-r--r--src/cmd/page/rotate.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/src/cmd/page/rotate.c b/src/cmd/page/rotate.c
new file mode 100644
index 00000000..b2952637
--- /dev/null
+++ b/src/cmd/page/rotate.c
@@ -0,0 +1,474 @@
+/*
+ * rotate an image 180° in O(log Dx + log Dy) /dev/draw writes,
+ * using an extra buffer same size as the image.
+ *
+ * the basic concept is that you can invert an array by inverting
+ * the top half, inverting the bottom half, and then swapping them.
+ * the code does this slightly backwards to ensure O(log n) runtime.
+ * (If you do it wrong, you can get O(log² n) runtime.)
+ *
+ * This is usually overkill, but it speeds up slow remote
+ * connections quite a bit.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "page.h"
+
+int ndraw = 0;
+enum {
+ Xaxis = 0,
+ Yaxis = 1,
+};
+
+Image *mtmp;
+
+void
+writefile(char *name, Image *im, int gran)
+{
+ static int c = 100;
+ int fd;
+ char buf[200];
+
+ snprint(buf, sizeof buf, "%d%s%d", c++, name, gran);
+ fd = create(buf, OWRITE, 0666);
+ if(fd < 0)
+ return;
+ writeimage(fd, im, 0);
+ close(fd);
+}
+
+void
+moveup(Image *im, Image *tmp, int a, int b, int c, int axis)
+{
+ Rectangle range;
+ Rectangle dr0, dr1;
+ Point p0, p1;
+
+ if(a == b || b == c)
+ return;
+
+ drawop(tmp, tmp->r, im, nil, im->r.min, S);
+
+ switch(axis){
+ case Xaxis:
+ range = Rect(a, im->r.min.y, c, im->r.max.y);
+ dr0 = range;
+ dr0.max.x = dr0.min.x+(c-b);
+ p0 = Pt(b, im->r.min.y);
+
+ dr1 = range;
+ dr1.min.x = dr1.max.x-(b-a);
+ p1 = Pt(a, im->r.min.y);
+ break;
+ case Yaxis:
+ range = Rect(im->r.min.x, a, im->r.max.x, c);
+ dr0 = range;
+ dr0.max.y = dr0.min.y+(c-b);
+ p0 = Pt(im->r.min.x, b);
+
+ dr1 = range;
+ dr1.min.y = dr1.max.y-(b-a);
+ p1 = Pt(im->r.min.x, a);
+ break;
+ }
+ drawop(im, dr0, tmp, nil, p0, S);
+ drawop(im, dr1, tmp, nil, p1, S);
+}
+
+void
+interlace(Image *im, Image *tmp, int axis, int n, Image *mask, int gran)
+{
+ Point p0, p1;
+ Rectangle r0, r1;
+
+ r0 = im->r;
+ r1 = im->r;
+ switch(axis) {
+ case Xaxis:
+ r0.max.x = n;
+ r1.min.x = n;
+ p0 = (Point){gran, 0};
+ p1 = (Point){-gran, 0};
+ break;
+ case Yaxis:
+ r0.max.y = n;
+ r1.min.y = n;
+ p0 = (Point){0, gran};
+ p1 = (Point){0, -gran};
+ break;
+ }
+
+ drawop(tmp, im->r, im, display->opaque, im->r.min, S);
+ gendrawop(im, r0, tmp, p0, mask, mask->r.min, S);
+ gendrawop(im, r0, tmp, p1, mask, p1, S);
+}
+
+/*
+ * Halve the grating period in the mask.
+ * The grating currently looks like
+ * ####____####____####____####____
+ * where #### is opacity.
+ *
+ * We want
+ * ##__##__##__##__##__##__##__##__
+ * which is achieved by shifting the mask
+ * and drawing on itself through itself.
+ * Draw doesn't actually allow this, so
+ * we have to copy it first.
+ *
+ * ####____####____####____####____ (dst)
+ * + ____####____####____####____#### (src)
+ * in __####____####____####____####__ (mask)
+ * ===========================================
+ * ##__##__##__##__##__##__##__##__
+ */
+int
+nextmask(Image *mask, int axis, int maskdim)
+{
+ Point delta;
+
+ delta = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim);
+ drawop(mtmp, mtmp->r, mask, nil, mask->r.min, S);
+ gendrawop(mask, mask->r, mtmp, delta, mtmp, divpt(delta,-2), S);
+// writefile("mask", mask, maskdim/2);
+ return maskdim/2;
+}
+
+void
+shuffle(Image *im, Image *tmp, int axis, int n, Image *mask, int gran,
+ int lastnn)
+{
+ int nn, left;
+
+ if(gran == 0)
+ return;
+ left = n%(2*gran);
+ nn = n - left;
+
+ interlace(im, tmp, axis, nn, mask, gran);
+// writefile("interlace", im, gran);
+
+ gran = nextmask(mask, axis, gran);
+ shuffle(im, tmp, axis, n, mask, gran, nn);
+// writefile("shuffle", im, gran);
+ moveup(im, tmp, lastnn, nn, n, axis);
+// writefile("move", im, gran);
+}
+
+void
+rot180(Image *im)
+{
+ Image *tmp, *tmp0;
+ Image *mask;
+ Rectangle rmask;
+ int gran;
+
+ if(chantodepth(im->chan) < 8){
+ /* this speeds things up dramatically; draw is too slow on sub-byte pixel sizes */
+ tmp0 = xallocimage(display, im->r, CMAP8, 0, DNofill);
+ drawop(tmp0, tmp0->r, im, nil, im->r.min, S);
+ }else
+ tmp0 = im;
+
+ tmp = xallocimage(display, tmp0->r, tmp0->chan, 0, DNofill);
+ if(tmp == nil){
+ if(tmp0 != im)
+ freeimage(tmp0);
+ return;
+ }
+ for(gran=1; gran<Dx(im->r); gran *= 2)
+ ;
+ gran /= 4;
+
+ rmask.min = ZP;
+ rmask.max = (Point){2*gran, 100};
+
+ mask = xallocimage(display, rmask, GREY1, 1, DTransparent);
+ mtmp = xallocimage(display, rmask, GREY1, 1, DTransparent);
+ if(mask == nil || mtmp == nil) {
+ fprint(2, "out of memory during rot180: %r\n");
+ wexits("memory");
+ }
+ rmask.max.x = gran;
+ drawop(mask, rmask, display->opaque, nil, ZP, S);
+// writefile("mask", mask, gran);
+ shuffle(im, tmp, Xaxis, Dx(im->r), mask, gran, 0);
+ freeimage(mask);
+ freeimage(mtmp);
+
+ for(gran=1; gran<Dy(im->r); gran *= 2)
+ ;
+ gran /= 4;
+ rmask.max = (Point){100, 2*gran};
+ mask = xallocimage(display, rmask, GREY1, 1, DTransparent);
+ mtmp = xallocimage(display, rmask, GREY1, 1, DTransparent);
+ if(mask == nil || mtmp == nil) {
+ fprint(2, "out of memory during rot180: %r\n");
+ wexits("memory");
+ }
+ rmask.max.y = gran;
+ drawop(mask, rmask, display->opaque, nil, ZP, S);
+ shuffle(im, tmp, Yaxis, Dy(im->r), mask, gran, 0);
+ freeimage(mask);
+ freeimage(mtmp);
+ freeimage(tmp);
+ if(tmp0 != im)
+ freeimage(tmp0);
+}
+
+/* rotates an image 90 degrees clockwise */
+Image *
+rot90(Image *im)
+{
+ Image *tmp;
+ int i, j, dx, dy;
+
+ dx = Dx(im->r);
+ dy = Dy(im->r);
+ tmp = xallocimage(display, Rect(0, 0, dy, dx), im->chan, 0, DCyan);
+ if(tmp == nil) {
+ fprint(2, "out of memory during rot90: %r\n");
+ wexits("memory");
+ }
+
+ for(j = 0; j < dx; j++) {
+ for(i = 0; i < dy; i++) {
+ drawop(tmp, Rect(i, j, i+1, j+1), im, nil, Pt(j, dy-(i+1)), S);
+ }
+ }
+ freeimage(im);
+
+ return(tmp);
+}
+
+/* from resample.c -- resize from → to using interpolation */
+
+
+#define K2 7 /* from -.7 to +.7 inclusive, meaning .2 into each adjacent pixel */
+#define NK (2*K2+1)
+double K[NK];
+
+double
+fac(int L)
+{
+ int i, f;
+
+ f = 1;
+ for(i=L; i>1; --i)
+ f *= i;
+ return f;
+}
+
+/*
+ * i0(x) is the modified Bessel function, Σ (x/2)^2L / (L!)²
+ * There are faster ways to calculate this, but we precompute
+ * into a table so let's keep it simple.
+ */
+double
+i0(double x)
+{
+ double v;
+ int L;
+
+ v = 1.0;
+ for(L=1; L<10; L++)
+ v += pow(x/2., 2*L)/pow(fac(L), 2);
+ return v;
+}
+
+double
+kaiser(double x, double tau, double alpha)
+{
+ if(fabs(x) > tau)
+ return 0.;
+ return i0(alpha*sqrt(1-(x*x/(tau*tau))))/i0(alpha);
+}
+
+void
+resamplex(uchar *in, int off, int d, int inx, uchar *out, int outx)
+{
+ int i, x, k;
+ double X, xx, v, rat;
+
+
+ rat = (double)inx/(double)outx;
+ for(x=0; x<outx; x++){
+ if(inx == outx){
+ /* don't resample if size unchanged */
+ out[off+x*d] = in[off+x*d];
+ continue;
+ }
+ v = 0.0;
+ X = x*rat;
+ for(k=-K2; k<=K2; k++){
+ xx = X + rat*k/10.;
+ i = xx;
+ if(i < 0)
+ i = 0;
+ if(i >= inx)
+ i = inx-1;
+ v += in[off+i*d] * K[K2+k];
+ }
+ out[off+x*d] = v;
+ }
+}
+
+void
+resampley(uchar **in, int off, int iny, uchar **out, int outy)
+{
+ int y, i, k;
+ double Y, yy, v, rat;
+
+ rat = (double)iny/(double)outy;
+ for(y=0; y<outy; y++){
+ if(iny == outy){
+ /* don't resample if size unchanged */
+ out[y][off] = in[y][off];
+ continue;
+ }
+ v = 0.0;
+ Y = y*rat;
+ for(k=-K2; k<=K2; k++){
+ yy = Y + rat*k/10.;
+ i = yy;
+ if(i < 0)
+ i = 0;
+ if(i >= iny)
+ i = iny-1;
+ v += in[i][off] * K[K2+k];
+ }
+ out[y][off] = v;
+ }
+
+}
+
+Image*
+resample(Image *from, Image *to)
+{
+ int i, j, bpl, nchan;
+ uchar **oscan, **nscan;
+ char tmp[20];
+ int xsize, ysize;
+ double v;
+ Image *t1, *t2;
+ ulong tchan;
+
+ for(i=-K2; i<=K2; i++){
+ K[K2+i] = kaiser(i/10., K2/10., 4.);
+ }
+
+ /* normalize */
+ v = 0.0;
+ for(i=0; i<NK; i++)
+ v += K[i];
+ for(i=0; i<NK; i++)
+ K[i] /= v;
+
+ switch(from->chan){
+ case GREY8:
+ case RGB24:
+ case RGBA32:
+ case ARGB32:
+ case XRGB32:
+ break;
+
+ case CMAP8:
+ case RGB15:
+ case RGB16:
+ tchan = RGB24;
+ goto Convert;
+
+ case GREY1:
+ case GREY2:
+ case GREY4:
+ tchan = GREY8;
+ Convert:
+ /* use library to convert to byte-per-chan form, then convert back */
+ t1 = xallocimage(display, Rect(0, 0, Dx(from->r), Dy(from->r)), tchan, 0, DNofill);
+ if(t1 == nil) {
+ fprint(2, "out of memory for temp image 1 in resample: %r\n");
+ wexits("memory");
+ }
+ drawop(t1, t1->r, from, nil, ZP, S);
+ t2 = xallocimage(display, to->r, tchan, 0, DNofill);
+ if(t2 == nil) {
+ fprint(2, "out of memory temp image 2 in resample: %r\n");
+ wexits("memory");
+ }
+ resample(t1, t2);
+ drawop(to, to->r, t2, nil, ZP, S);
+ freeimage(t1);
+ freeimage(t2);
+ return to;
+
+ default:
+ sysfatal("can't handle channel type %s", chantostr(tmp, from->chan));
+ }
+
+ xsize = Dx(to->r);
+ ysize = Dy(to->r);
+ oscan = malloc(Dy(from->r)*sizeof(uchar*));
+ nscan = malloc(max(ysize, Dy(from->r))*sizeof(uchar*));
+ if(oscan == nil || nscan == nil)
+ sysfatal("can't allocate: %r");
+
+ /* unload original image into scan lines */
+ bpl = bytesperline(from->r, from->depth);
+ for(i=0; i<Dy(from->r); i++){
+ oscan[i] = malloc(bpl);
+ if(oscan[i] == nil)
+ sysfatal("can't allocate: %r");
+ j = unloadimage(from, Rect(from->r.min.x, from->r.min.y+i, from->r.max.x, from->r.min.y+i+1), oscan[i], bpl);
+ if(j != bpl)
+ sysfatal("unloadimage");
+ }
+
+ /* allocate scan lines for destination. we do y first, so need at least Dy(from->r) lines */
+ bpl = bytesperline(Rect(0, 0, xsize, Dy(from->r)), from->depth);
+ for(i=0; i<max(ysize, Dy(from->r)); i++){
+ nscan[i] = malloc(bpl);
+ if(nscan[i] == nil)
+ sysfatal("can't allocate: %r");
+ }
+
+ /* resample in X */
+ nchan = from->depth/8;
+ for(i=0; i<Dy(from->r); i++){
+ for(j=0; j<nchan; j++){
+ if(j==0 && from->chan==XRGB32)
+ continue;
+ resamplex(oscan[i], j, nchan, Dx(from->r), nscan[i], xsize);
+ }
+ free(oscan[i]);
+ oscan[i] = nscan[i];
+ nscan[i] = malloc(bpl);
+ if(nscan[i] == nil)
+ sysfatal("can't allocate: %r");
+ }
+
+ /* resample in Y */
+ for(i=0; i<xsize; i++)
+ for(j=0; j<nchan; j++)
+ resampley(oscan, nchan*i+j, Dy(from->r), nscan, ysize);
+
+ /* pack data into destination */
+ bpl = bytesperline(to->r, from->depth);
+ for(i=0; i<ysize; i++){
+ j = loadimage(to, Rect(0, i, xsize, i+1), nscan[i], bpl);
+ if(j != bpl)
+ sysfatal("loadimage: %r");
+ }
+
+ for(i=0; i<Dy(from->r); i++){
+ free(oscan[i]);
+ free(nscan[i]);
+ }
+ free(oscan);
+ free(nscan);
+
+ return to;
+}