6000e563c7b6bb5fa14a1f030e64bac22b2e0b2c
[xscreensaver] / hacks / glx / grab-ximage.c
1 /* grab-ximage.c --- grab the screen to an XImage for use with OpenGL.
2  * xscreensaver, Copyright (c) 2001, 2003 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <GL/gl.h>      /* only for GLfloat */
23
24 #include "grabscreen.h"
25 #include "visual.h"
26
27 extern char *progname;
28
29 #include <X11/Xutil.h>
30 #include <sys/time.h>
31
32 #undef MAX
33 #define MAX(a,b) ((a)>(b)?(a):(b))
34
35 #undef countof
36 #define countof(x) (sizeof((x))/sizeof((*x)))
37
38 /* return the next larger power of 2. */
39 static int
40 to_pow2 (int i)
41 {
42   static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
43                                  2048, 4096, 8192, 16384, 32768, 65536 };
44   int j;
45   for (j = 0; j < countof(pow2); j++)
46     if (pow2[j] >= i) return pow2[j];
47   abort();  /* too big! */
48 }
49
50
51 /* Given a bitmask, returns the position and width of the field.
52  */
53 static void
54 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
55 {
56   int i;
57   for (i = 0; i < 32; i++)
58     if (mask & (1L << i))
59       {
60         int j = 0;
61         *pos_ret = i;
62         for (; i < 32; i++, j++)
63           if (! (mask & (1L << i)))
64             break;
65         *size_ret = j;
66         return;
67       }
68 }
69
70
71 /* Given a value and a field-width, expands the field to fill out 8 bits.
72  */
73 static unsigned char
74 spread_bits (unsigned char value, unsigned char width)
75 {
76   switch (width)
77     {
78     case 8: return value;
79     case 7: return (value << 1) | (value >> 6);
80     case 6: return (value << 2) | (value >> 4);
81     case 5: return (value << 3) | (value >> 2);
82     case 4: return (value << 4) | (value);
83     case 3: return (value << 5) | (value << 2) | (value >> 2);
84     case 2: return (value << 6) | (value << 4) | (value);
85     default: abort(); break;
86     }
87 }
88
89
90 static Bool
91 bigendian (void)
92 {
93   union { int i; char c[sizeof(int)]; } u;
94   u.i = 1;
95   return !u.c[0];
96 }
97
98
99 static XImage *
100 screen_to_ximage_1 (Screen *screen, Window window, Pixmap pixmap)
101 {
102   Display *dpy = DisplayOfScreen (screen);
103   XWindowAttributes xgwa;
104   int win_width, win_height;
105   int tex_width, tex_height;
106
107   XGetWindowAttributes (dpy, window, &xgwa);
108   win_width = xgwa.width;
109   win_height = xgwa.height;
110
111   /* GL texture sizes must be powers of two. */
112   tex_width  = to_pow2(win_width);
113   tex_height = to_pow2(win_height);
114
115   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
116    */
117   {
118     XImage *ximage1, *ximage2;
119     XColor *colors = 0;
120
121     ximage1 = XGetImage (dpy, pixmap, 0, 0, win_width, win_height, ~0L,
122                          ZPixmap);
123     XFreePixmap (dpy, pixmap);
124     pixmap = 0;
125
126     ximage2 = XCreateImage (dpy, xgwa.visual, 32, ZPixmap, 0, 0,
127                             tex_width, tex_height, 32, 0);
128
129     ximage2->data = (char *) calloc (tex_height, ximage2->bytes_per_line);
130
131     {
132       Screen *dscreen = DefaultScreenOfDisplay (dpy);
133       Visual *dvisual = DefaultVisualOfScreen (dscreen);
134       if (visual_class (dscreen, dvisual) == PseudoColor ||
135           visual_class (dscreen, dvisual) == GrayScale)
136         {
137           Colormap cmap = DefaultColormapOfScreen(dscreen);
138           int ncolors = visual_cells (dscreen, dvisual);
139           int i;
140           colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
141           for (i = 0; i < ncolors; i++)
142             colors[i].pixel = i;
143           XQueryColors (dpy, cmap, colors, ncolors);
144         }
145     }
146
147     /* Translate the server-ordered image to a client-ordered image.
148      */
149     {
150       int x, y;
151       unsigned int crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
152       unsigned int srpos=0, sgpos=0, sbpos=0;
153       unsigned int srmsk=0, sgmsk=0, sbmsk=0;
154       unsigned int srsiz=0, sgsiz=0, sbsiz=0;
155       int i;
156
157       unsigned char spread_map[3][256];
158
159       if (colors == 0)  /* truecolor */
160         {
161           srmsk = ximage2->red_mask;
162           sgmsk = ximage2->green_mask;
163           sbmsk = ximage2->blue_mask;
164
165           decode_mask (srmsk, &srpos, &srsiz);
166           decode_mask (sgmsk, &sgpos, &sgsiz);
167           decode_mask (sbmsk, &sbpos, &sbsiz);
168         }
169
170       /* Note that unlike X, which is endianness-agnostic (since any XImage
171          can have its own specific bit ordering, with the server reversing
172          things as necessary) OpenGL pretends everything is client-side, so
173          we need to pack things in "RGBA" order on the client machine,
174          regardless of its endianness.
175        */
176       if (bigendian())
177         crpos = 24, cgpos = 16, cbpos =  8, capos =  0;
178       else
179         crpos =  0, cgpos =  8, cbpos = 16, capos = 24;
180
181       if (colors == 0)  /* truecolor */
182         {
183           for (i = 0; i < 256; i++)
184             {
185               spread_map[0][i] = spread_bits (i, srsiz);
186               spread_map[1][i] = spread_bits (i, sgsiz);
187               spread_map[2][i] = spread_bits (i, sbsiz);
188             }
189         }
190
191       for (y = 0; y < win_height; y++)
192         {
193           int y2 = (win_height-1-y); /* Texture maps are upside down. */
194           for (x = 0; x < win_width; x++)
195             {
196               unsigned long sp = XGetPixel (ximage1, x, y2);
197               unsigned char sr, sg, sb;
198               unsigned long cp;
199
200               if (colors)
201                 {
202                   sr = colors[sp].red   & 0xFF;
203                   sg = colors[sp].green & 0xFF;
204                   sb = colors[sp].blue  & 0xFF;
205                 }
206               else
207                 {
208                   sr = (sp & srmsk) >> srpos;
209                   sg = (sp & sgmsk) >> sgpos;
210                   sb = (sp & sbmsk) >> sbpos;
211
212                   sr = spread_map[0][sr];
213                   sg = spread_map[1][sg];
214                   sb = spread_map[2][sb];
215                 }
216
217               cp = ((sr << crpos) |
218                     (sg << cgpos) |
219                     (sb << cbpos) |
220                     (0xFF << capos));
221
222               XPutPixel (ximage2, x, y, cp);
223             }
224         }
225     }
226
227     if (pixmap) XFreePixmap (dpy, pixmap);
228     if (colors) free (colors);
229     free (ximage1->data);
230     ximage1->data = 0;
231     XDestroyImage (ximage1);
232
233 #if 0
234     fprintf(stderr, "%s: grabbed %dx%d window 0x%lx to %dx%d texture\n",
235             progname, win_width, win_height, (unsigned long) window,
236             tex_width, tex_height);
237 #endif
238
239     return ximage2;
240   }
241 }
242
243
244 /* Returns an XImage structure containing an image of the desktop.
245    (As a side-effect, that image *may* be painted onto the given Window.)
246    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
247    extra byte set to 0xFF.
248  */
249 XImage *
250 screen_to_ximage (Screen *screen, Window window, char **filename_return)
251 {
252   Display *dpy = DisplayOfScreen (screen);
253   Pixmap pixmap = 0;
254   XWindowAttributes xgwa;
255
256   XGetWindowAttributes (dpy, window, &xgwa);
257   pixmap = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
258   load_random_image (screen, window, pixmap, filename_return);
259
260   return screen_to_ximage_1 (screen, window, pixmap);
261 }
262
263
264 typedef struct {
265   void (*callback) (Screen *, Window, XImage *,
266                     const char *name, void *closure, double cvt_time);
267   void *closure;
268   Pixmap pixmap;
269 } img_closure;
270
271
272 /* Returns the current time in seconds as a double.
273  */
274 static double
275 double_time (void)
276 {
277   struct timeval now;
278 # ifdef GETTIMEOFDAY_TWO_ARGS
279   struct timezone tzp;
280   gettimeofday(&now, &tzp);
281 # else
282   gettimeofday(&now);
283 # endif
284
285   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
286 }
287
288
289 static void
290 img_cb (Screen *screen, Window window, Drawable drawable,
291         const char *name, void *closure)
292 {
293   XImage *ximage;
294   double cvt_time = double_time();
295   img_closure *data = (img_closure *) closure;
296   /* copy closure data to stack and free the original before running cb */
297   img_closure dd = *data;
298   memset (data, 0, sizeof (*data));
299   free (data);
300   data = 0;
301   ximage = screen_to_ximage_1 (screen, window, dd.pixmap);
302   dd.callback (screen, window, ximage, name, dd.closure, cvt_time);
303 }
304
305
306 /* Like the above, but loads the image in the background and runs the
307    given callback once it has been loaded.
308  */
309 #include <X11/Intrinsic.h>
310 extern XtAppContext app;
311
312 void
313 fork_screen_to_ximage (Screen *screen, Window window,
314                        void (*callback) (Screen *, Window, XImage *,
315                                          const char *name,
316                                          void *closure,
317                                          double cvt_time),
318                        void *closure)
319 {
320   Display *dpy = DisplayOfScreen (screen);
321   XWindowAttributes xgwa;
322   img_closure *data = (img_closure *) calloc (1, sizeof(*data));
323   data->callback = callback;
324   data->closure  = closure;
325
326   XGetWindowAttributes (dpy, window, &xgwa);
327   data->pixmap = XCreatePixmap (dpy, window, xgwa.width, xgwa.height,
328                                 xgwa.depth);
329   fork_load_random_image (screen, window, data->pixmap, img_cb, data);
330 }