From http://www.jwz.org/xscreensaver/xscreensaver-5.27.tar.gz
[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-2008 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
21 #ifdef HAVE_COCOA
22 # include "jwxyz.h"
23 # ifndef HAVE_JWZGLES
24 #  include <OpenGL/glu.h>
25 # endif
26 #else
27 # include <X11/Xlib.h>
28 # include <X11/Xutil.h>
29 # include <GL/gl.h>     /* only for GLfloat */
30 # include <GL/glu.h>    /* for gluBuild2DMipmaps */
31 # include <GL/glx.h>    /* for glXMakeCurrent() */
32 #endif
33
34 #ifdef HAVE_JWZGLES
35 # include "jwzgles.h"
36 #endif /* HAVE_JWZGLES */
37
38 #include "grab-ximage.h"
39 #include "grabscreen.h"
40 #include "visual.h"
41
42 /* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
43    like this:
44
45      - get Pixmap as an XImage in whatever form the server hands us;
46      - convert that XImage to 32-bit RGBA in client-local endianness;
47      - make the texture using RGBA, UNSIGNED_BYTE.
48
49    If undefined, we do this:
50
51      - get Pixmap as an XImage in whatever form the server hands us;
52      - figure out what OpenGL texture packing parameters correspond to
53        the image data that the server sent us and use that, e.g.,
54        BGRA, INT_8_8_8_8_REV.
55
56    You might expect the second method to be faster, since we're not making
57    a second copy of the data and iterating each pixel before we hand it
58    to GL.  But, you'd be wrong.  The first method is almost 6x faster.
59    I guess GL is reformatting it *again*, and doing it very inefficiently!
60 */
61 #define REFORMAT_IMAGE_DATA
62
63
64 #ifdef HAVE_XSHM_EXTENSION
65 # include "resources.h"
66 # include "xshm.h"
67 #endif /* HAVE_XSHM_EXTENSION */
68
69 extern char *progname;
70
71 #include <sys/time.h>
72
73 #ifdef HAVE_COCOA
74 # include "jwxyz.h"
75 #else
76 # include <X11/Xutil.h>
77 #endif
78
79 #undef MAX
80 #define MAX(a,b) ((a)>(b)?(a):(b))
81
82 #undef countof
83 #define countof(x) (sizeof((x))/sizeof((*x)))
84
85
86 static int debug_p = 0;
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 #ifdef REFORMAT_IMAGE_DATA
98
99 /* Given a bitmask, returns the position and width of the field.
100  */
101 static void
102 decode_mask (unsigned long mask, unsigned long *pos_ret,
103              unsigned long *size_ret)
104 {
105   int i;
106   for (i = 0; i < 32; i++)
107     if (mask & (1L << i))
108       {
109         int j = 0;
110         *pos_ret = i;
111         for (; i < 32; i++, j++)
112           if (! (mask & (1L << i)))
113             break;
114         *size_ret = j;
115         return;
116       }
117 }
118
119
120 /* Given a value and a field-width, expands the field to fill out 8 bits.
121  */
122 static unsigned char
123 spread_bits (unsigned char value, unsigned char width)
124 {
125   switch (width)
126     {
127     case 8: return value;
128     case 7: return (value << 1) | (value >> 6);
129     case 6: return (value << 2) | (value >> 4);
130     case 5: return (value << 3) | (value >> 2);
131     case 4: return (value << 4) | (value);
132     case 3: return (value << 5) | (value << 2) | (value >> 2);
133     case 2: return (value << 6) | (value << 4) | (value);
134     default: abort(); break;
135     }
136 }
137
138
139 static XImage *
140 convert_ximage_to_rgba32 (Screen *screen, XImage *image)
141 {
142   Display *dpy = DisplayOfScreen (screen);
143   Visual *visual = DefaultVisualOfScreen (screen);
144
145   int x, y;
146   unsigned long crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
147   unsigned long srpos=0, sgpos=0, sbpos=0;
148   unsigned long srmsk=0, sgmsk=0, sbmsk=0;
149   unsigned long srsiz=0, sgsiz=0, sbsiz=0;
150   XColor *colors = 0;
151   unsigned char spread_map[3][256];
152
153   /* Note: height+2 in "to" to work around an array bounds overrun
154      in gluBuild2DMipmaps / gluScaleImage.
155    */
156   XImage *from = image;
157   XImage *to = XCreateImage (dpy, visual, 32,  /* depth */
158                              ZPixmap, 0, 0, from->width, from->height + 2,
159                              32, /* bitmap pad */
160                              0);
161   to->data = (char *) calloc (to->height, to->bytes_per_line);
162
163   /* Set the bit order in the XImage structure to whatever the
164      local host's native bit order is.
165    */
166   to->bitmap_bit_order =
167     to->byte_order =
168     (bigendian() ? MSBFirst : LSBFirst);
169
170   if (visual_class (screen, visual) == PseudoColor ||
171       visual_class (screen, visual) == GrayScale)
172     {
173       Colormap cmap = DefaultColormapOfScreen (screen);
174       int ncolors = visual_cells (screen, visual);
175       int i;
176       colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
177       for (i = 0; i < ncolors; i++)
178         colors[i].pixel = i;
179       XQueryColors (dpy, cmap, colors, ncolors);
180     }
181
182   if (colors == 0)  /* truecolor */
183     {
184       srmsk = to->red_mask;
185       sgmsk = to->green_mask;
186       sbmsk = to->blue_mask;
187
188       decode_mask (srmsk, &srpos, &srsiz);
189       decode_mask (sgmsk, &sgpos, &sgsiz);
190       decode_mask (sbmsk, &sbpos, &sbsiz);
191     }
192
193   /* Pack things in "RGBA" order in client endianness. */
194   if (bigendian())
195     crpos = 24, cgpos = 16, cbpos =  8, capos =  0;
196   else
197     crpos =  0, cgpos =  8, cbpos = 16, capos = 24;
198
199   if (colors == 0)  /* truecolor */
200     {
201       int i;
202       for (i = 0; i < 256; i++)
203         {
204           spread_map[0][i] = spread_bits (i, srsiz);
205           spread_map[1][i] = spread_bits (i, sgsiz);
206           spread_map[2][i] = spread_bits (i, sbsiz);
207         }
208     }
209
210   /* trying to track down an intermittent crash in ximage_putpixel_32 */
211   if (to->width  < from->width)  abort();
212   if (to->height < from->height) abort();
213
214   for (y = 0; y < from->height; y++)
215     for (x = 0; x < from->width; x++)
216       {
217         unsigned long sp = XGetPixel (from, x, y);
218         unsigned char sr, sg, sb;
219         unsigned long cp;
220
221         if (colors)
222           {
223             sr = colors[sp].red   & 0xFF;
224             sg = colors[sp].green & 0xFF;
225             sb = colors[sp].blue  & 0xFF;
226           }
227         else
228           {
229             sr = (sp & srmsk) >> srpos;
230             sg = (sp & sgmsk) >> sgpos;
231             sb = (sp & sbmsk) >> sbpos;
232
233             sr = spread_map[0][sr];
234             sg = spread_map[1][sg];
235             sb = spread_map[2][sb];
236           }
237
238         cp = ((sr << crpos) |
239               (sg << cgpos) |
240               (sb << cbpos) |
241               (0xFF << capos));
242
243         XPutPixel (to, x, y, cp);
244       }
245
246   if (colors) free (colors);
247
248   return to;
249 }
250
251 #endif /* REFORMAT_IMAGE_DATA */
252
253 /* Shrinks the XImage by a factor of two.
254    We use this when mipmapping fails on large textures.
255  */
256 static void
257 halve_image (XImage *ximage, XRectangle *geom)
258 {
259   int w2 = ximage->width/2;
260   int h2 = ximage->height/2;
261   int x, y;
262   XImage *ximage2;
263
264   if (w2 <= 32 || h2 <= 32)   /* let's not go crazy here, man. */
265     return;
266
267   if (debug_p)
268     fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
269              progname, ximage->width, ximage->height, w2, h2);
270
271   ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
272   *ximage2 = *ximage;
273   ximage2->width = w2;
274   ximage2->height = h2;
275   ximage2->bytes_per_line = 0;
276   ximage2->data = 0;
277   XInitImage (ximage2);
278
279   ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
280   if (!ximage2->data)
281     {
282       fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
283                progname, ximage->width, ximage->height, w2, h2);
284       exit (1);
285     }
286
287   for (y = 0; y < h2; y++)
288     for (x = 0; x < w2; x++)
289       XPutPixel (ximage2, x, y, XGetPixel (ximage, x*2, y*2));
290
291   free (ximage->data);
292   *ximage = *ximage2;
293   ximage2->data = 0;
294   XFree (ximage2);
295
296   if (geom)
297     {
298       geom->x /= 2;
299       geom->y /= 2;
300       geom->width  /= 2;
301       geom->height /= 2;
302     }
303 }
304
305
306 #ifdef REFORMAT_IMAGE_DATA
307
308 /* Pulls the Pixmap bits from the server and returns an XImage
309    in some format acceptable to OpenGL.
310  */
311 static XImage *
312 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
313 {
314   Display *dpy = DisplayOfScreen (screen);
315   unsigned int width, height, depth;
316
317 # ifdef HAVE_XSHM_EXTENSION
318   Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
319   XShmSegmentInfo shm_info;
320 # endif /* HAVE_XSHM_EXTENSION */
321
322   XImage *server_ximage = 0;
323   XImage *client_ximage = 0;
324
325   {
326     Window root;
327     int x, y;
328     unsigned int bw;
329     XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
330   }
331
332   if (width < 5 || height < 5)  /* something's gone wrong somewhere... */
333     return 0;
334
335   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
336    */
337 # ifdef HAVE_XSHM_EXTENSION
338   if (use_shm)
339     {
340       Visual *visual = DefaultVisualOfScreen (screen);
341       server_ximage = create_xshm_image (dpy, visual, depth,
342                                          ZPixmap, 0, &shm_info,
343                                          width, height);
344       if (server_ximage)
345         XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
346       else
347         use_shm = False;
348     }
349 # endif /* HAVE_XSHM_EXTENSION */
350
351   if (!server_ximage)
352     server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
353
354   client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
355
356 # ifdef HAVE_XSHM_EXTENSION
357   if (use_shm)
358     destroy_xshm_image (dpy, server_ximage, &shm_info);
359   else
360 # endif /* HAVE_XSHM_EXTENSION */
361     XDestroyImage (server_ximage);
362
363   return client_ximage;
364 }
365
366
367 # else /* ! REFORMAT_IMAGE_DATA */
368
369 typedef struct {
370   unsigned int depth, red_mask, green_mask, blue_mask;  /* when this... */
371   GLint type, format;                                   /* ...use this. */
372 } conversion_table;
373
374 /* Abbreviate these so that the table entries all fit on one line...
375  */
376 #define BYTE               GL_UNSIGNED_BYTE
377 #define BYTE_2_3_3_REV     GL_UNSIGNED_BYTE_2_3_3_REV
378 #define BYTE_3_3_2         GL_UNSIGNED_BYTE_3_3_2
379 #define INT_10_10_10_2     GL_UNSIGNED_INT_10_10_10_2
380 #define INT_2_10_10_10_REV GL_UNSIGNED_INT_2_10_10_10_REV
381 #define INT_8_8_8_8        GL_UNSIGNED_INT_8_8_8_8
382 #define INT_8_8_8_8_REV    GL_UNSIGNED_INT_8_8_8_8_REV
383 #define SHORT_1_5_5_5_REV  GL_UNSIGNED_SHORT_1_5_5_5_REV
384 #define SHORT_4_4_4_4      GL_UNSIGNED_SHORT_4_4_4_4
385 #define SHORT_4_4_4_4_REV  GL_UNSIGNED_SHORT_4_4_4_4_REV
386 #define SHORT_5_5_5_1      GL_UNSIGNED_SHORT_5_5_5_1
387 #define SHORT_5_6_5        GL_UNSIGNED_SHORT_5_6_5
388 #define SHORT_5_6_5_REV    GL_UNSIGNED_SHORT_5_6_5_REV
389
390 static const conversion_table ctable[] = {
391   { 8,  0x000000E0, 0x0000001C, 0x00000003, BYTE_3_3_2,         GL_RGB      },
392   { 8,  0x00000007, 0x00000038, 0x000000C0, BYTE_2_3_3_REV,     GL_RGB      },
393   { 16, 0x0000F800, 0x000007E0, 0x0000001F, SHORT_5_6_5,        GL_RGB      },
394   { 16, 0x0000001F, 0x000007E0, 0x0000F800, SHORT_5_6_5_REV,    GL_RGB      },
395   { 16, 0x0000F000, 0x00000F00, 0x000000F0, SHORT_4_4_4_4,      GL_RGBA     },
396   { 16, 0x000000F0, 0x00000F00, 0x0000F000, SHORT_4_4_4_4,      GL_BGRA     },
397   { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4,      GL_ABGR_EXT },
398   { 16, 0x0000000F, 0x000000F0, 0x00000F00, SHORT_4_4_4_4_REV,  GL_RGBA     },
399   { 16, 0x00000F00, 0x000000F0, 0x0000000F, SHORT_4_4_4_4_REV,  GL_BGRA     },
400   { 16, 0x0000F800, 0x000007C0, 0x0000003E, SHORT_5_5_5_1,      GL_RGBA     },
401   { 16, 0x0000003E, 0x000007C0, 0x0000F800, SHORT_5_5_5_1,      GL_BGRA     },
402   { 16, 0x00000001, 0x0000003E, 0x000007C0, SHORT_5_5_5_1,      GL_ABGR_EXT },
403   { 16, 0x0000001F, 0x000003E0, 0x00007C00, SHORT_1_5_5_5_REV,  GL_RGBA     },
404   { 16, 0x00007C00, 0x000003E0, 0x0000001F, SHORT_1_5_5_5_REV,  GL_BGRA     },
405   { 32, 0xFF000000, 0x00FF0000, 0x0000FF00, INT_8_8_8_8,        GL_RGBA     },
406   { 32, 0x0000FF00, 0x00FF0000, 0xFF000000, INT_8_8_8_8,        GL_BGRA     },
407   { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8,        GL_ABGR_EXT },
408   { 32, 0x000000FF, 0x0000FF00, 0x00FF0000, INT_8_8_8_8_REV,    GL_RGBA     },
409   { 32, 0x00FF0000, 0x0000FF00, 0x000000FF, INT_8_8_8_8_REV,    GL_BGRA     },
410   { 32, 0xFFC00000, 0x003FF000, 0x00000FFC, INT_10_10_10_2,     GL_RGBA     },
411   { 32, 0x00000FFC, 0x003FF000, 0xFFC00000, INT_10_10_10_2,     GL_BGRA     },
412   { 32, 0x00000003, 0x00000FFC, 0x003FF000, INT_10_10_10_2,     GL_ABGR_EXT },
413   { 32, 0x000003FF, 0x000FFC00, 0x3FF00000, INT_2_10_10_10_REV, GL_RGBA     },
414   { 32, 0x3FF00000, 0x000FFC00, 0x000003FF, INT_2_10_10_10_REV, GL_BGRA     },
415   { 24, 0x000000FF, 0x0000FF00, 0x00FF0000, BYTE,               GL_RGB      },
416   { 24, 0x00FF0000, 0x0000FF00, 0x000000FF, BYTE,               GL_BGR      },
417 };
418
419
420 /* Given an XImage, returns the GL settings to use its data as a texture.
421  */
422 static void
423 gl_settings_for_ximage (XImage *image,
424                         GLint *type_ret, GLint *format_ret, GLint *swap_ret)
425 {
426   int i;
427   for (i = 0; i < countof(ctable); ++i)
428     {
429       if (image->bits_per_pixel == ctable[i].depth &&
430           image->red_mask       == ctable[i].red_mask &&
431           image->green_mask     == ctable[i].green_mask &&
432           image->blue_mask      == ctable[i].blue_mask)
433         {
434           *type_ret   = ctable[i].type;
435           *format_ret = ctable[i].format;
436
437           if (image->bits_per_pixel == 24)
438             {
439               /* don't know how to test this... */
440               *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
441               *swap_ret = 0;
442             }
443           else
444             {
445               *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
446             }
447
448           if (debug_p)
449             {
450               fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
451                        progname,
452                        (*format_ret == GL_RGB      ? "RGB" :
453                         *format_ret == GL_BGR      ? "BGR" :
454                         *format_ret == GL_RGBA     ? "RGBA" :
455                         *format_ret == GL_BGRA     ? "BGRA" :
456                         *format_ret == GL_ABGR_EXT ? "ABGR_EXT" :
457                         "???"),
458                        (*type_ret == BYTE               ? "BYTE" :
459                         *type_ret == BYTE_3_3_2         ? "BYTE_3_3_2" :
460                         *type_ret == BYTE_2_3_3_REV     ? "BYTE_2_3_3_REV" :
461                         *type_ret == INT_8_8_8_8        ? "INT_8_8_8_8" :
462                         *type_ret == INT_8_8_8_8_REV    ? "INT_8_8_8_8_REV" :
463                         *type_ret == INT_10_10_10_2     ? "INT_10_10_10_2" :
464                         *type_ret == INT_2_10_10_10_REV ? "INT_2_10_10_10_REV":
465                         *type_ret == SHORT_4_4_4_4      ? "SHORT_4_4_4_4" :
466                         *type_ret == SHORT_4_4_4_4_REV  ? "SHORT_4_4_4_4_REV" :
467                         *type_ret == SHORT_5_5_5_1      ? "SHORT_5_5_5_1" :
468                         *type_ret == SHORT_1_5_5_5_REV  ? "SHORT_1_5_5_5_REV" :
469                         *type_ret == SHORT_5_6_5        ? "SHORT_5_6_5" :
470                         *type_ret == SHORT_5_6_5_REV    ? "SHORT_5_6_5_REV" :
471                         "???"),
472                        *swap_ret,
473                        image->bits_per_pixel,
474                        image->red_mask, image->green_mask, image->blue_mask);
475             }
476
477           return;
478         }
479     }
480
481   /* Unknown RGB fields? */
482   abort();
483 }
484
485 #endif /* ! REFORMAT_IMAGE_DATA */
486
487 typedef struct {
488   GLXContext glx_context;
489   Pixmap pixmap;
490   int pix_width, pix_height, pix_depth;
491   int texid;
492   Bool mipmap_p;
493   double load_time;
494
495   /* Used in async mode
496    */
497   void (*callback) (const char *filename, XRectangle *geometry,
498                     int iw, int ih, int tw, int th,
499                     void *closure);
500   void *closure;
501
502   /* Used in sync mode
503    */
504   char **filename_return;
505   XRectangle *geometry_return;
506   int *image_width_return;
507   int *image_height_return;
508   int *texture_width_return;
509   int *texture_height_return;
510
511 } img_closure;
512
513
514 /* Returns the current time in seconds as a double.
515  */
516 static double
517 double_time (void)
518 {
519   struct timeval now;
520 # ifdef GETTIMEOFDAY_TWO_ARGS
521   struct timezone tzp;
522   gettimeofday(&now, &tzp);
523 # else
524   gettimeofday(&now);
525 # endif
526
527   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
528 }
529
530
531 /* return the next larger power of 2. */
532 static int
533 to_pow2 (int value)
534 {
535   int i = 1;
536   while (i < value) i <<= 1;
537   return i;
538 }
539
540
541 /* Loads the given XImage into GL's texture memory.
542    The image may be of any size.
543    If mipmap_p is true, then make mipmaps instead of just a single texture.
544    Writes to stderr and returns False on error.
545  */
546 static Bool
547 ximage_to_texture (XImage *ximage,
548                    GLint type, GLint format,
549                    int *width_return,
550                    int *height_return,
551                    XRectangle *geometry,
552                    Bool mipmap_p)
553 {
554   int max_reduction = 7;
555   int err_count = 0;
556   GLenum err = 0;
557   int orig_width = ximage->width;
558   int orig_height = ximage->height;
559   int tex_width = 0;
560   int tex_height = 0;
561
562  AGAIN:
563
564   if (mipmap_p)
565     {
566       /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
567       tex_width  = ximage->width;
568       tex_height = ximage->height;
569
570       if (debug_p)
571         fprintf (stderr, "%s: mipmap %d x %d\n",
572                  progname, ximage->width, ximage->height);
573
574       gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
575                          format, type, ximage->data);
576       err = glGetError();
577     }
578   else
579     {
580       /* glTexImage2D() requires the texture sizes to be powers of 2.
581          So first, create a texture of that size (but don't write any
582          data into it.)
583        */
584       tex_width  = to_pow2 (ximage->width);
585       tex_height = to_pow2 (ximage->height);
586
587       if (debug_p)
588         fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
589                  progname, ximage->width, ximage->height,
590                  tex_width, tex_height);
591
592       glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0,
593                     format, type, 0);
594       err = glGetError();
595
596       /* Now load our non-power-of-2 image data into the existing texture. */
597       if (!err)
598         {
599           glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
600                            ximage->width, ximage->height,
601                            GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
602           err = glGetError();
603         }
604     }
605
606   if (err)
607     {
608       char buf[100];
609       const char *s = (char *) gluErrorString (err);
610
611       if (!s || !*s)
612         {
613           sprintf (buf, "unknown error %d", (int) err);
614           s = buf;
615         }
616
617       while (glGetError() != GL_NO_ERROR)
618         ;  /* clear any lingering errors */
619
620       if (++err_count > max_reduction)
621         {
622           fprintf (stderr,
623                    "\n"
624                    "%s: %dx%d texture failed, even after reducing to %dx%d:\n"
625                    "%s: The error was: \"%s\".\n"
626                    "%s: probably this means "
627                    "\"your video card is worthless and weak\"?\n\n",
628                    progname, orig_width, orig_height,
629                    ximage->width, ximage->height,
630                    progname, s,
631                    progname);
632           return False;
633         }
634       else
635         {
636           if (debug_p)
637             fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
638                      progname, ximage->width, ximage->height, s);
639           halve_image (ximage, geometry);
640           goto AGAIN;
641         }
642     }
643
644   if (width_return)  *width_return  = tex_width;
645   if (height_return) *height_return = tex_height;
646   return True;
647 }
648
649
650 static void load_texture_async_cb (Screen *screen,
651                                         Window window, Drawable drawable,
652                                         const char *name, XRectangle *geometry,
653                                         void *closure);
654
655
656 /* Grabs an image of the desktop (or another random image file) and
657    loads the image into GL's texture memory.
658    When the callback is called, the image data will have been loaded
659    into texture number `texid' (via glBindTexture.)
660
661    If an error occurred, width/height will be 0.
662  */
663 void
664 load_texture_async (Screen *screen, Window window,
665                     GLXContext glx_context,
666                     int desired_width, int desired_height,
667                     Bool mipmap_p,
668                     GLuint texid,
669                     void (*callback) (const char *filename,
670                                       XRectangle *geometry,
671                                       int image_width,
672                                       int image_height,
673                                       int texture_width,
674                                       int texture_height,
675                                       void *closure),
676                     void *closure)
677 {
678   Display *dpy = DisplayOfScreen (screen);
679   XWindowAttributes xgwa;
680   img_closure *data = (img_closure *) calloc (1, sizeof(*data));
681
682   if (debug_p)
683     data->load_time = double_time();
684
685   data->texid       = texid;
686   data->mipmap_p    = mipmap_p;
687   data->glx_context = glx_context;
688   data->callback    = callback;
689   data->closure     = closure;
690
691   XGetWindowAttributes (dpy, window, &xgwa);
692   data->pix_width  = xgwa.width;
693   data->pix_height = xgwa.height;
694   data->pix_depth  = xgwa.depth;
695
696   if (desired_width  && desired_width  < xgwa.width)
697     data->pix_width  = desired_width;
698   if (desired_height && desired_height < xgwa.height)
699     data->pix_height = desired_height;
700
701   data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
702                                 data->pix_depth);
703   load_image_async (screen, window, data->pixmap, 
704                     load_texture_async_cb, data);
705 }
706
707
708 /* Once we have an XImage, this loads it into GL.
709    This is used in both synchronous and asynchronous mode.
710  */
711 static void
712 load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
713                        const char *name, XRectangle *geometry, void *closure)
714 {
715   Display *dpy = DisplayOfScreen (screen);
716   Bool ok;
717   XImage *ximage;
718   GLint type, format;
719   int iw=0, ih=0, tw=0, th=0;
720   double cvt_time=0, tex_time=0, done_time=0;
721   img_closure *data = (img_closure *) closure;
722   /* copy closure data to stack and free the original before running cb */
723   img_closure dd = *data;
724   memset (data, 0, sizeof (*data));
725   free (data);
726   data = 0;
727
728   if (dd.glx_context)
729     glXMakeCurrent (dpy, window, dd.glx_context);
730
731   if (geometry->width <= 0 || geometry->height <= 0)
732     {
733       /* This can happen if an old version of xscreensaver-getimage
734          is installed. */
735       geometry->x = 0;
736       geometry->y = 0;
737       geometry->width  = dd.pix_width;
738       geometry->height = dd.pix_height;
739     }
740
741   if (geometry->width <= 0 || geometry->height <= 0)
742     abort();
743
744   if (debug_p)
745     cvt_time = double_time();
746
747 # ifdef REFORMAT_IMAGE_DATA
748   ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
749   format = GL_RGBA;
750   type = GL_UNSIGNED_BYTE;
751
752 #else /* ! REFORMAT_IMAGE_DATA */
753   {
754     Visual *visual = DefaultVisualOfScreen (screen);
755     GLint swap;
756
757     ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
758                            dd.pix_width, dd.pix_height, 32, 0);
759
760     /* Note: height+2 in "to" to be to work around an array bounds overrun
761        in gluBuild2DMipmaps / gluScaleImage. */
762     ximage->data = (char *) calloc (ximage->height+2, ximage->bytes_per_line);
763
764     if (!ximage->data ||
765         !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
766                        ~0L, ximage->format, ximage, 0, 0))
767       {
768         XDestroyImage (ximage);
769         ximage = 0;
770       }
771
772     gl_settings_for_ximage (ximage, &type, &format, &swap);
773     glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
774   }
775 #endif /* REFORMAT_IMAGE_DATA */
776
777   XFreePixmap (dpy, dd.pixmap);
778   dd.pixmap = 0;
779
780   if (debug_p)
781     tex_time = double_time();
782
783   if (! ximage)
784     ok = False;
785   else
786     {
787       iw = ximage->width;
788       ih = ximage->height;
789       if (dd.texid != -1)
790         glBindTexture (GL_TEXTURE_2D, dd.texid);
791
792       glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
793       ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
794                               dd.mipmap_p);
795       if (ok)
796         {
797           iw = ximage->width;   /* in case the image was shrunk */
798           ih = ximage->height;
799         }
800     }
801
802   if (ximage) XDestroyImage (ximage);
803
804   if (! ok)
805     iw = ih = tw = th = 0;
806
807   if (debug_p)
808     done_time = double_time();
809
810   if (debug_p)
811     fprintf (stderr,
812              /* prints: A + B + C = D
813                 A = file I/O time (happens in background)
814                 B = time to pull bits from server (this process)
815                 C = time to convert bits to GL textures (this process)
816                 D = total elapsed time from "want image" to "see image"
817
818                 B+C is responsible for any frame-rate glitches.
819               */
820              "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
821              progname,
822              cvt_time  - dd.load_time,
823              tex_time  - cvt_time,
824              done_time - tex_time,
825              done_time - dd.load_time);
826
827   if (dd.callback)
828     /* asynchronous mode */
829     dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
830   else
831     {
832       /* synchronous mode */
833       if (dd.filename_return)       *dd.filename_return       = (char *) name;
834       if (dd.geometry_return)       *dd.geometry_return       = *geometry;
835       if (dd.image_width_return)    *dd.image_width_return    = iw;
836       if (dd.image_height_return)   *dd.image_height_return   = ih;
837       if (dd.texture_width_return)  *dd.texture_width_return  = tw;
838       if (dd.texture_height_return) *dd.texture_height_return = th;
839     }
840 }