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