3ca543fd2e079d1e4b77ed65f317a23d2e2f9498
[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 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
25 extern char *progname;
26
27 #include <X11/Xutil.h>
28
29 #undef MAX
30 #define MAX(a,b) ((a)>(b)?(a):(b))
31
32 #undef countof
33 #define countof(x) (sizeof((x))/sizeof((*x)))
34
35 static Bool
36 bigendian (void)
37 {
38   union { int i; char c[sizeof(int)]; } u;
39   u.i = 1;
40   return !u.c[0];
41 }
42
43 /* return the next larger power of 2. */
44 static int
45 to_pow2 (int i)
46 {
47   static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
48                                  2048, 4096, 8192, 16384, 32768, 65536 };
49   int j;
50   for (j = 0; j < countof(pow2); j++)
51     if (pow2[j] >= i) return pow2[j];
52   abort();  /* too big! */
53 }
54
55
56 /* Returns an XImage structure containing an image of the desktop.
57    (As a side-effect, that image will be painted onto the given Window.)
58    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
59    extra byte set to 0xFF.
60  */
61 XImage *
62 screen_to_ximage (Screen *screen, Window window)
63 {
64   Display *dpy = DisplayOfScreen (screen);
65   XWindowAttributes xgwa;
66   int win_width, win_height;
67   int tex_width, tex_height;
68
69   grab_screen_image (screen, window);
70
71   XGetWindowAttributes (dpy, window, &xgwa);
72   win_width = xgwa.width;
73   win_height = xgwa.height;
74
75   /* GL texture sizes must be powers of two. */
76   tex_width  = to_pow2(win_width);
77   tex_height = to_pow2(win_height);
78
79   /* Convert the server-side Drawable to a client-side GL-ordered XImage.
80    */
81   {
82     XImage *ximage1, *ximage2;
83
84     ximage1 = XGetImage (dpy, window, 0, 0, win_width, win_height, ~0L,
85                          ZPixmap);
86     ximage2 = XCreateImage (dpy, xgwa.visual, 32, ZPixmap, 0, 0,
87                             tex_width, tex_height, 32, 0);
88
89     ximage2->data = (char *) calloc (tex_height, ximage2->bytes_per_line);
90
91     /* Translate the server-ordered image to a client-ordered image.
92      */
93     {
94       int x, y;
95       int crpos, cgpos, cbpos, capos;  /* bitfield positions */
96       int srpos, sgpos, sbpos;
97       int srmsk, sgmsk, sbmsk;
98       int sdepth = ximage1->depth;
99
100       srmsk = ximage1->red_mask;
101       sgmsk = ximage1->green_mask;
102       sbmsk = ximage1->blue_mask;
103       if (sdepth == 32 || sdepth == 24)
104         srpos = 16, sgpos = 8, sbpos = 0;
105       else /* 15 or 16 */
106         srpos = 10, sgpos = 5, sbpos = 0;
107
108       /* Note that unlike X, which is endianness-agnostic (since any XImage
109          can have its own specific bit ordering, with the server reversing
110          things as necessary) OpenGL pretends everything is client-side, so
111          we need to pack things in the right order for the client machine.
112        */
113       if (bigendian())
114         crpos = 24, cgpos = 16, cbpos =  8, capos =  0;
115       else
116         crpos =  0, cgpos =  8, cbpos = 16, capos = 24;
117
118       for (y = 0; y < win_height; y++)
119         {
120           int y2 = (win_height-1-y); /* Texture maps are upside down. */
121           for (x = 0; x < win_width; x++)
122             {
123               unsigned long sp = XGetPixel (ximage1, x, y2);
124               unsigned char sr = (sp & srmsk) >> srpos;
125               unsigned char sg = (sp & sgmsk) >> sgpos;
126               unsigned char sb = (sp & sbmsk) >> sbpos;
127               unsigned long cp;
128
129               if (sdepth < 24)   /* spread 5 bits to 8 */
130                 {
131                   sr = (sr << 3) | (sr >> 2);
132                   sg = (sg << 3) | (sg >> 2);
133                   sb = (sb << 3) | (sb >> 2);
134                 }
135               cp = ((sr << crpos) |
136                     (sg << cgpos) |
137                     (sb << cbpos) |
138                     (0xFF << capos));
139               XPutPixel (ximage2, x, y, cp);
140             }
141         }
142     }
143
144     free (ximage1->data);
145     ximage1->data = 0;
146     XDestroyImage (ximage1);
147
148 #if 0
149     fprintf(stderr, "%s: grabbed %dx%d window 0x%lx to %dx%d texture\n",
150             progname, win_width, win_height, (unsigned long) window,
151             tex_width, tex_height);
152 #endif
153
154     return ximage2;
155   }
156 }