99e92317487e25373e5a456a86d94eb356e74a89
[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 <X11/Xlib.h>
20 #include <X11/Xutil.h>
21 #include <GL/gl.h>      /* only for GLfloat */
22
23 #include "grabscreen.h"
24 #include "visual.h"
25
26 extern char *progname;
27
28 #include <X11/Xutil.h>
29
30 #undef MAX
31 #define MAX(a,b) ((a)>(b)?(a):(b))
32
33 #undef countof
34 #define countof(x) (sizeof((x))/sizeof((*x)))
35
36 /* return the next larger power of 2. */
37 static int
38 to_pow2 (int i)
39 {
40   static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
41                                  2048, 4096, 8192, 16384, 32768, 65536 };
42   int j;
43   for (j = 0; j < countof(pow2); j++)
44     if (pow2[j] >= i) return pow2[j];
45   abort();  /* too big! */
46 }
47
48
49 /* Given a bitmask, returns the position and width of the field.
50  */
51 static void
52 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
53 {
54   int i;
55   for (i = 0; i < 32; i++)
56     if (mask & (1L << i))
57       {
58         int j = 0;
59         *pos_ret = i;
60         for (; i < 32; i++, j++)
61           if (! (mask & (1L << i)))
62             break;
63         *size_ret = j;
64         return;
65       }
66 }
67
68
69 /* Given a value and a field-width, expands the field to fill out 8 bits.
70  */
71 static unsigned char
72 spread_bits (unsigned char value, unsigned char width)
73 {
74   switch (width)
75     {
76     case 8: return value;
77     case 7: return (value << 1) | (value >> 6);
78     case 6: return (value << 2) | (value >> 4);
79     case 5: return (value << 3) | (value >> 2);
80     case 4: return (value << 4) | (value);
81     case 3: return (value << 5) | (value << 2) | (value >> 2);
82     case 2: return (value << 6) | (value << 4) | (value);
83     default: abort(); break;
84     }
85 }
86
87
88 static Bool
89 bigendian (void)
90 {
91   union { int i; char c[sizeof(int)]; } u;
92   u.i = 1;
93   return !u.c[0];
94 }
95
96
97 /* Returns an XImage structure containing an image of the desktop.
98    (As a side-effect, that image *may* be painted onto the given Window.)
99    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
100    extra byte set to 0xFF.
101  */
102 XImage *
103 screen_to_ximage (Screen *screen, Window window, char **filename_return)
104 {
105   Display *dpy = DisplayOfScreen (screen);
106   Pixmap pixmap = 0;
107   XWindowAttributes xgwa;
108   int win_width, win_height;
109   int tex_width, tex_height;
110
111   XGetWindowAttributes (dpy, window, &xgwa);
112   win_width = xgwa.width;
113   win_height = xgwa.height;
114
115   pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
116   load_random_image (screen, window, pixmap, filename_return);
117
118   /* GL texture sizes must be powers of two. */
119   tex_width  = to_pow2(win_width);
120   tex_height = to_pow2(win_height);
121
122   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
123    */
124   {
125     XImage *ximage1, *ximage2;
126     XColor *colors = 0;
127
128     ximage1 = XGetImage (dpy, pixmap, 0, 0, win_width, win_height, ~0L,
129                          ZPixmap);
130     XFreePixmap (dpy, pixmap);
131     pixmap = 0;
132
133     ximage2 = XCreateImage (dpy, xgwa.visual, 32, ZPixmap, 0, 0,
134                             tex_width, tex_height, 32, 0);
135
136     ximage2->data = (char *) calloc (tex_height, ximage2->bytes_per_line);
137
138     {
139       Screen *dscreen = DefaultScreenOfDisplay (dpy);
140       Visual *dvisual = DefaultVisualOfScreen (dscreen);
141       if (visual_class (dscreen, dvisual) == PseudoColor ||
142           visual_class (dscreen, dvisual) == GrayScale)
143         {
144           Colormap cmap = DefaultColormapOfScreen(dscreen);
145           int ncolors = visual_cells (dscreen, dvisual);
146           int i;
147           colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
148           for (i = 0; i < ncolors; i++)
149             colors[i].pixel = i;
150           XQueryColors (dpy, cmap, colors, ncolors);
151         }
152     }
153
154     /* Translate the server-ordered image to a client-ordered image.
155      */
156     {
157       int x, y;
158       unsigned int crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
159       unsigned int srpos=0, sgpos=0, sbpos=0;
160       unsigned int srmsk=0, sgmsk=0, sbmsk=0;
161       unsigned int srsiz=0, sgsiz=0, sbsiz=0;
162       int i;
163
164       unsigned char spread_map[3][256];
165
166       if (colors == 0)  /* truecolor */
167         {
168           srmsk = ximage2->red_mask;
169           sgmsk = ximage2->green_mask;
170           sbmsk = ximage2->blue_mask;
171
172           decode_mask (srmsk, &srpos, &srsiz);
173           decode_mask (sgmsk, &sgpos, &sgsiz);
174           decode_mask (sbmsk, &sbpos, &sbsiz);
175         }
176
177       /* Note that unlike X, which is endianness-agnostic (since any XImage
178          can have its own specific bit ordering, with the server reversing
179          things as necessary) OpenGL pretends everything is client-side, so
180          we need to pack things in "RGBA" order on the client machine,
181          regardless of its endianness.
182        */
183       if (bigendian())
184         crpos = 24, cgpos = 16, cbpos =  8, capos =  0;
185       else
186         crpos =  0, cgpos =  8, cbpos = 16, capos = 24;
187
188       if (colors == 0)  /* truecolor */
189         {
190           for (i = 0; i < 256; i++)
191             {
192               spread_map[0][i] = spread_bits (i, srsiz);
193               spread_map[1][i] = spread_bits (i, sgsiz);
194               spread_map[2][i] = spread_bits (i, sbsiz);
195             }
196         }
197
198       for (y = 0; y < win_height; y++)
199         {
200           int y2 = (win_height-1-y); /* Texture maps are upside down. */
201           for (x = 0; x < win_width; x++)
202             {
203               unsigned long sp = XGetPixel (ximage1, x, y2);
204               unsigned char sr, sg, sb;
205               unsigned long cp;
206
207               if (colors)
208                 {
209                   sr = colors[sp].red   & 0xFF;
210                   sg = colors[sp].green & 0xFF;
211                   sb = colors[sp].blue  & 0xFF;
212                 }
213               else
214                 {
215                   sr = (sp & srmsk) >> srpos;
216                   sg = (sp & sgmsk) >> sgpos;
217                   sb = (sp & sbmsk) >> sbpos;
218
219                   sr = spread_map[0][sr];
220                   sg = spread_map[1][sg];
221                   sb = spread_map[2][sb];
222                 }
223
224               cp = ((sr << crpos) |
225                     (sg << cgpos) |
226                     (sb << cbpos) |
227                     (0xFF << capos));
228
229               XPutPixel (ximage2, x, y, cp);
230             }
231         }
232     }
233
234     if (pixmap) XFreePixmap (dpy, pixmap);
235     if (colors) free (colors);
236     free (ximage1->data);
237     ximage1->data = 0;
238     XDestroyImage (ximage1);
239
240 #if 0
241     fprintf(stderr, "%s: grabbed %dx%d window 0x%lx to %dx%d texture\n",
242             progname, win_width, win_height, (unsigned long) window,
243             tex_width, tex_height);
244 #endif
245
246     return ximage2;
247   }
248 }