From http://www.jwz.org/xscreensaver/xscreensaver-5.38.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   /* Allow the pixmap to be larger than the window. Esper wants this. */
669   if (desired_width /* && desired_width  < xgwa.width */)
670     data->pix_width  = desired_width;
671   if (desired_height /* && desired_height < xgwa.height */)
672     data->pix_height = desired_height;
673
674   data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
675                                 data->pix_depth);
676   load_image_async (screen, window, data->pixmap, 
677                     load_texture_async_cb, data);
678 }
679
680
681 /* Once we have an XImage, this loads it into GL.
682    This is used in both synchronous and asynchronous mode.
683  */
684 static void
685 load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
686                        const char *name, XRectangle *geometry, void *closure)
687 {
688   Display *dpy = DisplayOfScreen (screen);
689   Bool ok;
690   XImage *ximage;
691   GLint type, format;
692   int iw=0, ih=0, tw=0, th=0;
693   double cvt_time=0, tex_time=0, done_time=0;
694   img_closure *data = (img_closure *) closure;
695   /* copy closure data to stack and free the original before running cb */
696   img_closure dd = *data;
697   memset (data, 0, sizeof (*data));
698   free (data);
699   data = 0;
700
701   if (dd.glx_context)
702     glXMakeCurrent (dpy, window, dd.glx_context);
703
704   if (geometry->width <= 0 || geometry->height <= 0)
705     {
706       /* This can happen if an old version of xscreensaver-getimage
707          is installed. Or if we have no image (checkerboard). */
708       geometry->x = 0;
709       geometry->y = 0;
710       geometry->width  = dd.pix_width;
711       geometry->height = dd.pix_height;
712     }
713
714   if (geometry->width <= 0 || geometry->height <= 0)
715     abort();
716
717   if (debug_p)
718     cvt_time = double_time();
719
720 # ifdef REFORMAT_IMAGE_DATA
721   ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
722   format = GL_RGBA;
723   type = GL_UNSIGNED_BYTE;
724
725 #else /* ! REFORMAT_IMAGE_DATA */
726   {
727     Visual *visual = DefaultVisualOfScreen (screen);
728     GLint swap;
729
730     ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
731                            dd.pix_width, dd.pix_height, 32, 0);
732
733     /* Note: height+2 in "to" to be to work around an array bounds overrun
734        in gluBuild2DMipmaps / gluScaleImage. */
735     ximage->data = (char *) calloc (ximage->height+2, ximage->bytes_per_line);
736
737     if (!ximage->data ||
738         !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
739                        ~0L, ximage->format, ximage, 0, 0))
740       {
741         XDestroyImage (ximage);
742         ximage = 0;
743       }
744
745     gl_settings_for_ximage (ximage, &type, &format, &swap);
746     glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
747   }
748 #endif /* REFORMAT_IMAGE_DATA */
749
750   XFreePixmap (dpy, dd.pixmap);
751   dd.pixmap = 0;
752
753   if (debug_p)
754     tex_time = double_time();
755
756   if (! ximage)
757     ok = False;
758   else
759     {
760       iw = ximage->width;
761       ih = ximage->height;
762       if (dd.texid != -1)
763         glBindTexture (GL_TEXTURE_2D, dd.texid);
764
765       glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
766       ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
767                               dd.mipmap_p);
768       if (ok)
769         {
770           iw = ximage->width;   /* in case the image was shrunk */
771           ih = ximage->height;
772         }
773     }
774
775   if (ximage) XDestroyImage (ximage);
776
777   if (! ok)
778     iw = ih = tw = th = 0;
779
780   if (debug_p)
781     done_time = double_time();
782
783   if (debug_p)
784     fprintf (stderr,
785              /* prints: A + B + C = D
786                 A = file I/O time (happens in background)
787                 B = time to pull bits from server (this process)
788                 C = time to convert bits to GL textures (this process)
789                 D = total elapsed time from "want image" to "see image"
790
791                 B+C is responsible for any frame-rate glitches.
792               */
793              "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
794              progname,
795              cvt_time  - dd.load_time,
796              tex_time  - cvt_time,
797              done_time - tex_time,
798              done_time - dd.load_time);
799
800   if (dd.callback)
801     /* asynchronous mode */
802     dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
803   else
804     {
805       /* synchronous mode */
806       if (dd.filename_return)       *dd.filename_return       = (char *) name;
807       if (dd.geometry_return)       *dd.geometry_return       = *geometry;
808       if (dd.image_width_return)    *dd.image_width_return    = iw;
809       if (dd.image_height_return)   *dd.image_height_return   = ih;
810       if (dd.texture_width_return)  *dd.texture_width_return  = tw;
811       if (dd.texture_height_return) *dd.texture_height_return = th;
812     }
813 }