plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

nrotate.c (5821B)


      1 /*
      2  * Rotate an image 180° in O(log Dx + log Dy)
      3  * draw calls, using an extra buffer the same size
      4  * as the image.
      5  *
      6  * The basic concept is that you can invert an array by
      7  * inverting the top half, inverting the bottom half, and
      8  * then swapping them.
      9  *
     10  * This is usually overkill, but it speeds up slow remote
     11  * connections quite a bit.
     12  */
     13 
     14 #include <u.h>
     15 #include <libc.h>
     16 #include <bio.h>
     17 #include <draw.h>
     18 #include <thread.h>
     19 #include <cursor.h>
     20 #include "page.h"
     21 
     22 int ndraw = 0;
     23 
     24 enum {
     25 	Xaxis,
     26 	Yaxis,
     27 };
     28 
     29 static void reverse(Image*, Image*, int);
     30 static void shuffle(Image*, Image*, int, int, Image*, int, int);
     31 static void writefile(char *name, Image *im, int gran);
     32 static void halvemaskdim(Image*);
     33 static void swapranges(Image*, Image*, int, int, int, int);
     34 
     35 /*
     36  * Rotate the image 180° by reflecting first
     37  * along the X axis, and then along the Y axis.
     38  */
     39 void
     40 rot180(Image *img)
     41 {
     42 	Image *tmp;
     43 
     44 	tmp = xallocimage(display, img->r, img->chan, 0, DNofill);
     45 	if(tmp == nil)
     46 		return;
     47 
     48 	reverse(img, tmp, Xaxis);
     49 	reverse(img, tmp, Yaxis);
     50 
     51 	freeimage(tmp);
     52 }
     53 
     54 Image *mtmp;
     55 
     56 static void
     57 reverse(Image *img, Image *tmp, int axis)
     58 {
     59 	Image *mask;
     60 	Rectangle r;
     61 	int i, d;
     62 
     63 	/*
     64 	 * We start by swapping large chunks at a time.
     65 	 * The chunk size should be the largest power of
     66 	 * two that fits in the dimension.
     67 	 */
     68 	d = axis==Xaxis ? Dx(img) : Dy(img);
     69 	for(i = 1; i*2 <= d; i *= 2)
     70 		;
     71 
     72 	r = axis==Xaxis ? Rect(0,0, i,100) : Rect(0,0, 100,i);
     73 	mask = xallocimage(display, r, GREY1, 1, DTransparent);
     74 	mtmp = xallocimage(display, r, GREY1, 1, DTransparent);
     75 
     76 	/*
     77 	 * Now color the bottom (or left) half of the mask opaque.
     78 	 */
     79 	if(axis==Xaxis)
     80 		r.max.x /= 2;
     81 	else
     82 		r.max.y /= 2;
     83 
     84 	draw(mask, r, display->opaque, nil, ZP);
     85 	writefile("mask", mask, i);
     86 
     87 	/*
     88 	 * Shuffle will recur, shuffling the pieces as necessary
     89 	 * and making the mask a finer and finer grating.
     90 	 */
     91 	shuffle(img, tmp, axis, d, mask, i, 0);
     92 
     93 	freeimage(mask);
     94 }
     95 
     96 /*
     97  * Shuffle the image by swapping pieces of size maskdim.
     98  */
     99 static void
    100 shuffle(Image *img, Image *tmp, int axis, int imgdim, Image *mask, int maskdim)
    101 {
    102 	int slop;
    103 
    104 	if(maskdim == 0)
    105 		return;
    106 
    107 	/*
    108 	 * Figure out how much will be left over that needs to be
    109 	 * shifted specially to the bottom.
    110 	 */
    111 	slop = imgdim % maskdim;
    112 
    113 	/*
    114 	 * Swap adjacent grating lines as per mask.
    115 	 */
    116 	swapadjacent(img, tmp, axis, imgdim - slop, mask, maskdim);
    117 
    118 	/*
    119 	 * Calculate the mask with gratings half as wide and recur.
    120 	 */
    121 	halvemaskdim(mask, maskdim, axis);
    122 	writefile("mask", mask, maskdim/2);
    123 
    124 	shuffle(img, tmp, axis, imgdim, mask, maskdim/2);
    125 
    126 	/*
    127 	 * Move the slop down to the bottom of the image.
    128 	 */
    129 	swapranges(img, tmp, 0, imgdim-slop, imgdim, axis);
    130 	moveup(im, tmp, lastnn, nn, n, axis);
    131 }
    132 
    133 /*
    134  * Halve the grating period in the mask.
    135  * The grating currently looks like
    136  * ####____####____####____####____
    137  * where #### is opacity.
    138  *
    139  * We want
    140  * ##__##__##__##__##__##__##__##__
    141  * which is achieved by shifting the mask
    142  * and drawing on itself through itself.
    143  * Draw doesn't actually allow this, so
    144  * we have to copy it first.
    145  *
    146  *     ####____####____####____####____ (dst)
    147  * +   ____####____####____####____#### (src)
    148  * in  __####____####____####____####__ (mask)
    149  * ===========================================
    150  *     ##__##__##__##__##__##__##__##__
    151  */
    152 static void
    153 halvemaskdim(Image *m, int maskdim, int axis)
    154 {
    155 	Point δ;
    156 
    157 	δ = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim);
    158 	draw(mtmp, mtmp->r, mask, nil, mask->r.min);
    159 	gendraw(mask, mask->r, mtmp, δ, mtmp, divpt(δ,2));
    160 	writefile("mask", mask, maskdim/2);
    161 }
    162 
    163 /*
    164  * Swap the regions [a,b] and [b,c]
    165  */
    166 static void
    167 swapranges(Image *img, Image *tmp, int a, int b, int c, int axis)
    168 {
    169 	Rectangle r;
    170 	Point δ;
    171 
    172 	if(a == b || b == c)
    173 		return;
    174 
    175 	writefile("swap", img, 0);
    176 	draw(tmp, tmp->r, im, nil, im->r.min);
    177 
    178 	/* [a,a+(c-b)] gets [b,c] */
    179 	r = img->r;
    180 	if(axis==Xaxis){
    181 		δ = Pt(1,0);
    182 		r.min.x = img->r.min.x + a;
    183 		r.max.x = img->r.min.x + a + (c-b);
    184 	}else{
    185 		δ = Pt(0,1);
    186 		r.min.y = img->r.min.y + a;
    187 		r.max.y = img->r.min.y + a + (c-b);
    188 	}
    189 	draw(img, r, tmp, nil, addpt(tmp->r.min, mulpt(δ, b)));
    190 
    191 	/* [a+(c-b), c] gets [a,b] */
    192 	r = img->r;
    193 	if(axis==Xaxis){
    194 		r.min.x = img->r.min.x + a + (c-b);
    195 		r.max.x = img->r.min.x + c;
    196 	}else{
    197 		r.min.y = img->r.min.y + a + (c-b);
    198 		r.max.y = img->r.min.y + c;
    199 	}
    200 	draw(img, r, tmp, nil, addpt(tmp->r.min, mulpt(δ, a)));
    201 	writefile("swap", img, 1);
    202 }
    203 
    204 /*
    205  * Swap adjacent regions as specified by the grating.
    206  * We do this by copying the image through the mask twice,
    207  * once aligned with the grading and once 180° out of phase.
    208  */
    209 static void
    210 swapadjacent(Image *img, Image *tmp, int axis, int imgdim, Image *mask, int maskdim)
    211 {
    212 	Point δ;
    213 	Rectangle r0, r1;
    214 
    215 	δ = axis==Xaxis ? Pt(1,0) : Pt(0,1);
    216 
    217 	r0 = img->r;
    218 	r1 = img->r;
    219 	switch(axis){
    220 	case Xaxis:
    221 		r0.max.x = imgdim;
    222 		r1.min.x = imgdim;
    223 		break;
    224 	case Yaxis:
    225 		r0.max.y = imgdim;
    226 		r1.min.y = imgdim;
    227 	}
    228 
    229 	/*
    230 	 * r0 is the lower rectangle, while r1 is the upper one.
    231 	 */
    232 	draw(tmp, tmp->r, img, nil,
    233 }
    234 
    235 void
    236 interlace(Image *im, Image *tmp, int axis, int n, Image *mask, int gran)
    237 {
    238 	Point p0, p1;
    239 	Rectangle r0, r1;
    240 
    241 	r0 = im->r;
    242 	r1 = im->r;
    243 	switch(axis) {
    244 	case Xaxis:
    245 		r0.max.x = n;
    246 		r1.min.x = n;
    247 		p0 = (Point){gran, 0};
    248 		p1 = (Point){-gran, 0};
    249 		break;
    250 	case Yaxis:
    251 		r0.max.y = n;
    252 		r1.min.y = n;
    253 		p0 = (Point){0, gran};
    254 		p1 = (Point){0, -gran};
    255 		break;
    256 	}
    257 
    258 	draw(tmp, im->r, im, display->black, im->r.min);
    259 	gendraw(im, r0, tmp, p0, mask, mask->r.min);
    260 	gendraw(im, r0, tmp, p1, mask, p1);
    261 }
    262 
    263 
    264 static void
    265 writefile(char *name, Image *im, int gran)
    266 {
    267 	static int c = 100;
    268 	int fd;
    269 	char buf[200];
    270 
    271 	snprint(buf, sizeof buf, "%d%s%d", c++, name, gran);
    272 	fd = create(buf, OWRITE, 0666);
    273 	if(fd < 0)
    274 		return;
    275 	writeimage(fd, im, 0);
    276 	close(fd);
    277 }