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