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