|  | /* | 
|  | * Rotate an image 180° in O(log Dx + log Dy) | 
|  | * draw calls, using an extra buffer the 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. | 
|  | * | 
|  | * 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 <thread.h> | 
|  | #include <cursor.h> | 
|  | #include "page.h" | 
|  |  | 
|  | int ndraw = 0; | 
|  |  | 
|  | enum { | 
|  | Xaxis, | 
|  | Yaxis, | 
|  | }; | 
|  |  | 
|  | static void reverse(Image*, Image*, int); | 
|  | static void shuffle(Image*, Image*, int, int, Image*, int, int); | 
|  | static void writefile(char *name, Image *im, int gran); | 
|  | static void halvemaskdim(Image*); | 
|  | static void swapranges(Image*, Image*, int, int, int, int); | 
|  |  | 
|  | /* | 
|  | * Rotate the image 180° by reflecting first | 
|  | * along the X axis, and then along the Y axis. | 
|  | */ | 
|  | void | 
|  | rot180(Image *img) | 
|  | { | 
|  | Image *tmp; | 
|  |  | 
|  | tmp = xallocimage(display, img->r, img->chan, 0, DNofill); | 
|  | if(tmp == nil) | 
|  | return; | 
|  |  | 
|  | reverse(img, tmp, Xaxis); | 
|  | reverse(img, tmp, Yaxis); | 
|  |  | 
|  | freeimage(tmp); | 
|  | } | 
|  |  | 
|  | Image *mtmp; | 
|  |  | 
|  | static void | 
|  | reverse(Image *img, Image *tmp, int axis) | 
|  | { | 
|  | Image *mask; | 
|  | Rectangle r; | 
|  | int i, d; | 
|  |  | 
|  | /* | 
|  | * We start by swapping large chunks at a time. | 
|  | * The chunk size should be the largest power of | 
|  | * two that fits in the dimension. | 
|  | */ | 
|  | d = axis==Xaxis ? Dx(img) : Dy(img); | 
|  | for(i = 1; i*2 <= d; i *= 2) | 
|  | ; | 
|  |  | 
|  | r = axis==Xaxis ? Rect(0,0, i,100) : Rect(0,0, 100,i); | 
|  | mask = xallocimage(display, r, GREY1, 1, DTransparent); | 
|  | mtmp = xallocimage(display, r, GREY1, 1, DTransparent); | 
|  |  | 
|  | /* | 
|  | * Now color the bottom (or left) half of the mask opaque. | 
|  | */ | 
|  | if(axis==Xaxis) | 
|  | r.max.x /= 2; | 
|  | else | 
|  | r.max.y /= 2; | 
|  |  | 
|  | draw(mask, r, display->opaque, nil, ZP); | 
|  | writefile("mask", mask, i); | 
|  |  | 
|  | /* | 
|  | * Shuffle will recur, shuffling the pieces as necessary | 
|  | * and making the mask a finer and finer grating. | 
|  | */ | 
|  | shuffle(img, tmp, axis, d, mask, i, 0); | 
|  |  | 
|  | freeimage(mask); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Shuffle the image by swapping pieces of size maskdim. | 
|  | */ | 
|  | static void | 
|  | shuffle(Image *img, Image *tmp, int axis, int imgdim, Image *mask, int maskdim) | 
|  | { | 
|  | int slop; | 
|  |  | 
|  | if(maskdim == 0) | 
|  | return; | 
|  |  | 
|  | /* | 
|  | * Figure out how much will be left over that needs to be | 
|  | * shifted specially to the bottom. | 
|  | */ | 
|  | slop = imgdim % maskdim; | 
|  |  | 
|  | /* | 
|  | * Swap adjacent grating lines as per mask. | 
|  | */ | 
|  | swapadjacent(img, tmp, axis, imgdim - slop, mask, maskdim); | 
|  |  | 
|  | /* | 
|  | * Calculate the mask with gratings half as wide and recur. | 
|  | */ | 
|  | halvemaskdim(mask, maskdim, axis); | 
|  | writefile("mask", mask, maskdim/2); | 
|  |  | 
|  | shuffle(img, tmp, axis, imgdim, mask, maskdim/2); | 
|  |  | 
|  | /* | 
|  | * Move the slop down to the bottom of the image. | 
|  | */ | 
|  | swapranges(img, tmp, 0, imgdim-slop, imgdim, axis); | 
|  | moveup(im, tmp, lastnn, nn, n, axis); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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) | 
|  | * =========================================== | 
|  | *     ##__##__##__##__##__##__##__##__ | 
|  | */ | 
|  | static void | 
|  | halvemaskdim(Image *m, int maskdim, int axis) | 
|  | { | 
|  | Point δ; | 
|  |  | 
|  | δ = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim); | 
|  | draw(mtmp, mtmp->r, mask, nil, mask->r.min); | 
|  | gendraw(mask, mask->r, mtmp, δ, mtmp, divpt(δ,2)); | 
|  | writefile("mask", mask, maskdim/2); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Swap the regions [a,b] and [b,c] | 
|  | */ | 
|  | static void | 
|  | swapranges(Image *img, Image *tmp, int a, int b, int c, int axis) | 
|  | { | 
|  | Rectangle r; | 
|  | Point δ; | 
|  |  | 
|  | if(a == b || b == c) | 
|  | return; | 
|  |  | 
|  | writefile("swap", img, 0); | 
|  | draw(tmp, tmp->r, im, nil, im->r.min); | 
|  |  | 
|  | /* [a,a+(c-b)] gets [b,c] */ | 
|  | r = img->r; | 
|  | if(axis==Xaxis){ | 
|  | δ = Pt(1,0); | 
|  | r.min.x = img->r.min.x + a; | 
|  | r.max.x = img->r.min.x + a + (c-b); | 
|  | }else{ | 
|  | δ = Pt(0,1); | 
|  | r.min.y = img->r.min.y + a; | 
|  | r.max.y = img->r.min.y + a + (c-b); | 
|  | } | 
|  | draw(img, r, tmp, nil, addpt(tmp->r.min, mulpt(δ, b))); | 
|  |  | 
|  | /* [a+(c-b), c] gets [a,b] */ | 
|  | r = img->r; | 
|  | if(axis==Xaxis){ | 
|  | r.min.x = img->r.min.x + a + (c-b); | 
|  | r.max.x = img->r.min.x + c; | 
|  | }else{ | 
|  | r.min.y = img->r.min.y + a + (c-b); | 
|  | r.max.y = img->r.min.y + c; | 
|  | } | 
|  | draw(img, r, tmp, nil, addpt(tmp->r.min, mulpt(δ, a))); | 
|  | writefile("swap", img, 1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Swap adjacent regions as specified by the grating. | 
|  | * We do this by copying the image through the mask twice, | 
|  | * once aligned with the grading and once 180° out of phase. | 
|  | */ | 
|  | static void | 
|  | swapadjacent(Image *img, Image *tmp, int axis, int imgdim, Image *mask, int maskdim) | 
|  | { | 
|  | Point δ; | 
|  | Rectangle r0, r1; | 
|  |  | 
|  | δ = axis==Xaxis ? Pt(1,0) : Pt(0,1); | 
|  |  | 
|  | r0 = img->r; | 
|  | r1 = img->r; | 
|  | switch(axis){ | 
|  | case Xaxis: | 
|  | r0.max.x = imgdim; | 
|  | r1.min.x = imgdim; | 
|  | break; | 
|  | case Yaxis: | 
|  | r0.max.y = imgdim; | 
|  | r1.min.y = imgdim; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * r0 is the lower rectangle, while r1 is the upper one. | 
|  | */ | 
|  | draw(tmp, tmp->r, img, nil, | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | draw(tmp, im->r, im, display->black, im->r.min); | 
|  | gendraw(im, r0, tmp, p0, mask, mask->r.min); | 
|  | gendraw(im, r0, tmp, p1, mask, p1); | 
|  | } | 
|  |  | 
|  |  | 
|  | static 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); | 
|  | } | 
|  |  |