1eebcb820f957747ce22215dfa2fda5e1e099b67
[xscreensaver] / hacks / blitspin.c
1 /* xscreensaver, Copyright (c) 1992-1997, 2003 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* Rotate a bitmap using using bitblts.
13    The bitmap must be square, and must be a power of 2 in size.
14    This was translated from SmallTalk code which appeared in the
15    August 1981 issue of Byte magazine.
16
17    The input bitmap may be non-square, it is padded and centered
18    with the background color.  Another way would be to subdivide
19    the bitmap into square components and rotate them independently
20    (and preferably in parallel), but I don't think that would be as
21    interesting looking.
22
23    It's too bad almost nothing uses blitter hardware these days,
24    or this might actually win.
25  */
26
27 #include "screenhack.h"
28 #include "xpm-pixmap.h"
29 #include <stdio.h>
30
31 #include "images/som.xbm"
32
33 static Display *dpy;
34 static Window window;
35 static unsigned int size;
36 static Pixmap self, temp, mask;
37 static GC SET, CLR, CPY, IOR, AND, XOR;
38 static GC gc;
39 static int delay, delay2;
40 static Pixmap bitmap;
41 static int depth;
42 static unsigned int fg, bg;
43
44 static void display (Pixmap);
45
46 #define copy_all_to(from, xoff, yoff, to, gc)           \
47   XCopyArea (dpy, (from), (to), (gc), 0, 0,             \
48              size-(xoff), size-(yoff), (xoff), (yoff))
49
50 #define copy_all_from(to, xoff, yoff, from, gc)         \
51   XCopyArea (dpy, (from), (to), (gc), (xoff), (yoff),   \
52              size-(xoff), size-(yoff), 0, 0)
53
54 static void
55 rotate (void)
56 {
57   int qwad; /* fuckin' C, man... who needs namespaces? */
58   XFillRectangle (dpy, mask, CLR, 0, 0, size, size);
59   XFillRectangle (dpy, mask, SET, 0, 0, size>>1, size>>1);
60   for (qwad = size>>1; qwad > 0; qwad>>=1)
61     {
62       if (delay) usleep (delay);
63       copy_all_to   (mask,       0,       0, temp, CPY);  /* 1 */
64       copy_all_to   (mask,       0,    qwad, temp, IOR);  /* 2 */
65       copy_all_to   (self,       0,       0, temp, AND);  /* 3 */
66       copy_all_to   (temp,       0,       0, self, XOR);  /* 4 */
67       copy_all_from (temp,    qwad,       0, self, XOR);  /* 5 */
68       copy_all_from (self,    qwad,       0, self, IOR);  /* 6 */
69       copy_all_to   (temp,    qwad,       0, self, XOR);  /* 7 */
70       copy_all_to   (self,       0,       0, temp, CPY);  /* 8 */
71       copy_all_from (temp,    qwad,    qwad, self, XOR);  /* 9 */
72       copy_all_to   (mask,       0,       0, temp, AND);  /* A */
73       copy_all_to   (temp,       0,       0, self, XOR);  /* B */
74       copy_all_to   (temp,    qwad,    qwad, self, XOR);  /* C */
75       copy_all_from (mask, qwad>>1, qwad>>1, mask, AND);  /* D */
76       copy_all_to   (mask,    qwad,       0, mask, IOR);  /* E */
77       copy_all_to   (mask,       0,    qwad, mask, IOR);  /* F */
78       display (self);
79     }
80 }
81
82 static Pixmap
83 read_screen (Display *dpy, Window window, int *widthP, int *heightP)
84 {
85   Pixmap p;
86   XWindowAttributes xgwa;
87   XGCValues gcv;
88   GC gc;
89   XGetWindowAttributes (dpy, window, &xgwa);
90   *widthP = xgwa.width;
91   *heightP = xgwa.height;
92
93   p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
94   gcv.function = GXcopy;
95   gc = XCreateGC (dpy, window, GCFunction, &gcv);
96
97   load_random_image (xgwa.screen, window, p, NULL);
98
99   /* Reset the window's background color... */
100   XSetWindowBackground (dpy, window,
101                         get_pixel_resource ("background", "Background",
102                                             dpy, xgwa.colormap));
103   XCopyArea (dpy, p, window, gc, 0, 0, *widthP, *heightP, 0, 0);
104   XFreeGC (dpy, gc);
105
106   return p;
107 }
108
109
110 static int 
111 to_pow2(int n, Bool up)
112 {
113   /* sizeof(Dimension) == 2. */
114   int powers_of_2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
115                         2048, 4096, 8192, 16384, 32768, 65536 };
116   int i = 0;
117   if (n > 65536) size = 65536;
118   while (n >= powers_of_2[i]) i++;
119   if (n == powers_of_2[i-1])
120     return n;
121   else
122     return powers_of_2[up ? i : i-1];
123 }
124
125 static void
126 init (void)
127 {
128   XWindowAttributes xgwa;
129   Colormap cmap;
130   XGCValues gcv;
131   int width, height;
132   char *bitmap_name;
133   Bool scale_up;
134
135   XGetWindowAttributes (dpy, window, &xgwa);
136   cmap = xgwa.colormap;
137   depth = xgwa.depth;
138
139   fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
140   bg = get_pixel_resource ("background", "Background", dpy, cmap);
141   delay = get_integer_resource ("delay", "Integer");
142   delay2 = get_integer_resource ("delay2", "Integer");
143   if (delay < 0) delay = 0;
144   if (delay2 < 0) delay2 = 0;
145   bitmap_name = get_string_resource ("bitmap", "Bitmap");
146   if (! bitmap_name || !*bitmap_name)
147     bitmap_name = "(default)";
148
149   if (!strcmp (bitmap_name, "(default)"))
150     {
151       width = som_width;
152       height = som_height;
153       bitmap = XCreatePixmapFromBitmapData (dpy, window, (char *) som_bits,
154                                             width, height, fg, bg, depth);
155       scale_up = True; /* definitely. */
156     }
157   else if (!strcmp (bitmap_name, "(screen)"))
158     {
159       bitmap = read_screen (dpy, window, &width, &height);
160       scale_up = True; /* maybe? */
161     }
162   else
163     {
164       bitmap = xpm_file_to_pixmap (dpy, window, bitmap_name,
165                                    &width, &height, 0);
166       scale_up = True; /* probably? */
167     }
168
169   size = (width < height) ? height : width;     /* make it square */
170   size = to_pow2(size, scale_up);               /* round up to power of 2 */
171   {                                             /* don't exceed screen size */
172     int s = XScreenNumberOfScreen(xgwa.screen);
173     int w = to_pow2(XDisplayWidth(dpy, s), False);
174     int h = to_pow2(XDisplayHeight(dpy, s), False);
175     if (size > w) size = w;
176     if (size > h) size = h;
177   }
178
179   self = XCreatePixmap (dpy, window, size, size, depth);
180   temp = XCreatePixmap (dpy, window, size, size, depth);
181   mask = XCreatePixmap (dpy, window, size, size, depth);
182   gcv.foreground = (depth == 1 ? 1 : (~0));
183   gcv.function=GXset;  SET = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
184   gcv.function=GXclear;CLR = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
185   gcv.function=GXcopy; CPY = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
186   gcv.function=GXor;   IOR = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
187   gcv.function=GXand;  AND = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
188   gcv.function=GXxor;  XOR = XCreateGC(dpy,self,GCFunction|GCForeground,&gcv);
189
190   gcv.foreground = gcv.background = bg;
191   gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
192   /* Clear self to the background color (not to 0, which CLR does.) */
193   XFillRectangle (dpy, self, gc, 0, 0, size, size);
194   XSetForeground (dpy, gc, fg);
195
196   XCopyArea (dpy, bitmap, self, CPY, 0, 0, width, height,
197              (size - width)>>1, (size - height)>>1);
198   XFreePixmap(dpy, bitmap);
199
200   display (self);
201   XSync(dpy, False);
202   screenhack_handle_events (dpy);
203 }
204
205 static void
206 display (Pixmap pixmap)
207 {
208   XWindowAttributes xgwa;
209   static int last_w = 0, last_h = 0;
210   XGetWindowAttributes (dpy, window, &xgwa);
211   if (xgwa.width != last_w || xgwa.height != last_h)
212     {
213       XClearWindow (dpy, window);
214       last_w = xgwa.width;
215       last_h = xgwa.height;
216     }
217   if (depth != 1)
218     XCopyArea (dpy, pixmap, window, gc, 0, 0, size, size,
219                (xgwa.width-size)>>1, (xgwa.height-size)>>1);
220   else
221     XCopyPlane (dpy, pixmap, window, gc, 0, 0, size, size,
222                 (xgwa.width-size)>>1, (xgwa.height-size)>>1, 1);
223 /*
224   XDrawRectangle (dpy, window, gc,
225                   ((xgwa.width-size)>>1)-1, ((xgwa.height-size)>>1)-1,
226                   size+2, size+2);
227 */
228   XSync (dpy, False);
229   screenhack_handle_events (dpy);
230 }
231
232 \f
233 char *progclass = "BlitSpin";
234
235 char *defaults [] = {
236   ".background: black",
237   ".foreground: white",
238   "*delay:      500000",
239   "*delay2:     500000",
240   "*bitmap:     (default)",
241   "*geometry:   512x512",
242   0
243 };
244
245 XrmOptionDescRec options [] = {
246   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
247   { "-delay2",          ".delay2",      XrmoptionSepArg, 0 },
248   { "-bitmap",          ".bitmap",      XrmoptionSepArg, 0 },
249   { "-grab-screen",     ".bitmap",      XrmoptionNoArg, "(screen)" },
250   { 0, 0, 0, 0 }
251 };
252
253 void
254 screenhack (Display *d, Window w)
255 {
256   dpy = d;
257   window = w;
258   init ();
259   if (delay2) usleep (delay2 * 2);
260   while (1)
261     {
262       rotate ();
263       screenhack_handle_events (d);
264       if (delay2) usleep (delay2);
265     }
266 }