]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/page/nrotate.c
merge
[plan9front.git] / sys / src / cmd / page / nrotate.c
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
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
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
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
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
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
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