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