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>
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
24 # include <OpenGL/glu.h>
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() */
36 #endif /* HAVE_JWZGLES */
38 #include "grab-ximage.h"
39 #include "grabscreen.h"
42 /* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
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.
49 If undefined, we do this:
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.
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!
61 #define REFORMAT_IMAGE_DATA
64 #ifdef HAVE_XSHM_EXTENSION
65 # include "resources.h"
67 #endif /* HAVE_XSHM_EXTENSION */
69 extern char *progname;
76 # include <X11/Xutil.h>
80 #define MAX(a,b) ((a)>(b)?(a):(b))
83 #define countof(x) (sizeof((x))/sizeof((*x)))
86 static int debug_p = 0;
91 union { int i; char c[sizeof(int)]; } u;
97 #ifdef REFORMAT_IMAGE_DATA
99 /* Given a bitmask, returns the position and width of the field.
102 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
105 for (i = 0; i < 32; i++)
106 if (mask & (1L << i))
110 for (; i < 32; i++, j++)
111 if (! (mask & (1L << i)))
119 /* Given a value and a field-width, expands the field to fill out 8 bits.
122 spread_bits (unsigned char value, unsigned char width)
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;
139 convert_ximage_to_rgba32 (Screen *screen, XImage *image)
141 Display *dpy = DisplayOfScreen (screen);
142 Visual *visual = DefaultVisualOfScreen (screen);
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;
150 unsigned char spread_map[3][256];
152 /* Note: height+2 in "to" to work around an array bounds overrun
153 in gluBuild2DMipmaps / gluScaleImage.
155 XImage *from = image;
156 XImage *to = XCreateImage (dpy, visual, 32, /* depth */
157 ZPixmap, 0, 0, from->width, from->height + 2,
160 to->data = (char *) calloc (to->height, to->bytes_per_line);
162 /* Set the bit order in the XImage structure to whatever the
163 local host's native bit order is.
165 to->bitmap_bit_order =
167 (bigendian() ? MSBFirst : LSBFirst);
169 if (visual_class (screen, visual) == PseudoColor ||
170 visual_class (screen, visual) == GrayScale)
172 Colormap cmap = DefaultColormapOfScreen (screen);
173 int ncolors = visual_cells (screen, visual);
175 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
176 for (i = 0; i < ncolors; i++)
178 XQueryColors (dpy, cmap, colors, ncolors);
181 if (colors == 0) /* truecolor */
183 srmsk = to->red_mask;
184 sgmsk = to->green_mask;
185 sbmsk = to->blue_mask;
187 decode_mask (srmsk, &srpos, &srsiz);
188 decode_mask (sgmsk, &sgpos, &sgsiz);
189 decode_mask (sbmsk, &sbpos, &sbsiz);
192 /* Pack things in "RGBA" order in client endianness. */
194 crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
196 crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
198 if (colors == 0) /* truecolor */
201 for (i = 0; i < 256; i++)
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);
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();
213 for (y = 0; y < from->height; y++)
214 for (x = 0; x < from->width; x++)
216 unsigned long sp = XGetPixel (from, x, y);
217 unsigned char sr, sg, sb;
222 sr = colors[sp].red & 0xFF;
223 sg = colors[sp].green & 0xFF;
224 sb = colors[sp].blue & 0xFF;
228 sr = (sp & srmsk) >> srpos;
229 sg = (sp & sgmsk) >> sgpos;
230 sb = (sp & sbmsk) >> sbpos;
232 sr = spread_map[0][sr];
233 sg = spread_map[1][sg];
234 sb = spread_map[2][sb];
237 cp = ((sr << crpos) |
242 XPutPixel (to, x, y, cp);
245 if (colors) free (colors);
250 #endif /* REFORMAT_IMAGE_DATA */
252 /* Shrinks the XImage by a factor of two.
253 We use this when mipmapping fails on large textures.
256 halve_image (XImage *ximage, XRectangle *geom)
258 int w2 = ximage->width/2;
259 int h2 = ximage->height/2;
263 if (w2 <= 32 || h2 <= 32) /* let's not go crazy here, man. */
267 fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
268 progname, ximage->width, ximage->height, w2, h2);
270 ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
273 ximage2->height = h2;
274 ximage2->bytes_per_line = 0;
276 XInitImage (ximage2);
278 ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
281 fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
282 progname, ximage->width, ximage->height, w2, h2);
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));
305 #ifdef REFORMAT_IMAGE_DATA
307 /* Pulls the Pixmap bits from the server and returns an XImage
308 in some format acceptable to OpenGL.
311 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
313 Display *dpy = DisplayOfScreen (screen);
314 unsigned int width, height, depth;
316 # ifdef HAVE_XSHM_EXTENSION
317 Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
318 XShmSegmentInfo shm_info;
319 # endif /* HAVE_XSHM_EXTENSION */
321 XImage *server_ximage = 0;
322 XImage *client_ximage = 0;
328 XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
331 if (width < 5 || height < 5) /* something's gone wrong somewhere... */
334 /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
336 # ifdef HAVE_XSHM_EXTENSION
339 Visual *visual = DefaultVisualOfScreen (screen);
340 server_ximage = create_xshm_image (dpy, visual, depth,
341 ZPixmap, 0, &shm_info,
344 XShmGetImage (dpy, pixmap, server_ximage, 0, 0, ~0L);
348 # endif /* HAVE_XSHM_EXTENSION */
351 server_ximage = XGetImage (dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
353 client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
355 # ifdef HAVE_XSHM_EXTENSION
357 destroy_xshm_image (dpy, server_ximage, &shm_info);
359 # endif /* HAVE_XSHM_EXTENSION */
360 XDestroyImage (server_ximage);
362 return client_ximage;
366 # else /* ! REFORMAT_IMAGE_DATA */
369 unsigned int depth, red_mask, green_mask, blue_mask; /* when this... */
370 GLint type, format; /* ...use this. */
373 /* Abbreviate these so that the table entries all fit on one line...
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
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 },
419 /* Given an XImage, returns the GL settings to use its data as a texture.
422 gl_settings_for_ximage (XImage *image,
423 GLint *type_ret, GLint *format_ret, GLint *swap_ret)
426 for (i = 0; i < countof(ctable); ++i)
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)
433 *type_ret = ctable[i].type;
434 *format_ret = ctable[i].format;
436 if (image->bits_per_pixel == 24)
438 /* don't know how to test this... */
439 *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
444 *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
449 fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
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" :
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" :
472 image->bits_per_pixel,
473 image->red_mask, image->green_mask, image->blue_mask);
480 /* Unknown RGB fields? */
484 #endif /* ! REFORMAT_IMAGE_DATA */
487 GLXContext glx_context;
489 int pix_width, pix_height, pix_depth;
494 /* Used in async mode
496 void (*callback) (const char *filename, XRectangle *geometry,
497 int iw, int ih, int tw, int th,
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;
513 /* Returns the current time in seconds as a double.
519 # ifdef GETTIMEOFDAY_TWO_ARGS
521 gettimeofday(&now, &tzp);
526 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
530 /* return the next larger power of 2. */
535 while (i < value) i <<= 1;
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.
546 ximage_to_texture (XImage *ximage,
547 GLint type, GLint format,
550 XRectangle *geometry,
553 int max_reduction = 7;
556 int orig_width = ximage->width;
557 int orig_height = ximage->height;
565 /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
566 tex_width = ximage->width;
567 tex_height = ximage->height;
570 fprintf (stderr, "%s: mipmap %d x %d\n",
571 progname, ximage->width, ximage->height);
573 gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
574 format, type, ximage->data);
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
583 tex_width = to_pow2 (ximage->width);
584 tex_height = to_pow2 (ximage->height);
587 fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
588 progname, ximage->width, ximage->height,
589 tex_width, tex_height);
591 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0,
595 /* Now load our non-power-of-2 image data into the existing texture. */
598 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
599 ximage->width, ximage->height,
600 GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
608 const char *s = (char *) gluErrorString (err);
612 sprintf (buf, "unknown error %d", (int) err);
616 while (glGetError() != GL_NO_ERROR)
617 ; /* clear any lingering errors */
619 if (++err_count > max_reduction)
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,
636 fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
637 progname, ximage->width, ximage->height, s);
638 halve_image (ximage, geometry);
643 if (width_return) *width_return = tex_width;
644 if (height_return) *height_return = tex_height;
649 static void load_texture_async_cb (Screen *screen,
650 Window window, Drawable drawable,
651 const char *name, XRectangle *geometry,
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.)
660 If an error occurred, width/height will be 0.
663 load_texture_async (Screen *screen, Window window,
664 GLXContext glx_context,
665 int desired_width, int desired_height,
668 void (*callback) (const char *filename,
669 XRectangle *geometry,
677 Display *dpy = DisplayOfScreen (screen);
678 XWindowAttributes xgwa;
679 img_closure *data = (img_closure *) calloc (1, sizeof(*data));
682 data->load_time = double_time();
685 data->mipmap_p = mipmap_p;
686 data->glx_context = glx_context;
687 data->callback = callback;
688 data->closure = closure;
690 XGetWindowAttributes (dpy, window, &xgwa);
691 data->pix_width = xgwa.width;
692 data->pix_height = xgwa.height;
693 data->pix_depth = xgwa.depth;
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;
700 data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
702 load_image_async (screen, window, data->pixmap,
703 load_texture_async_cb, data);
707 /* Once we have an XImage, this loads it into GL.
708 This is used in both synchronous and asynchronous mode.
711 load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
712 const char *name, XRectangle *geometry, void *closure)
714 Display *dpy = DisplayOfScreen (screen);
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));
728 glXMakeCurrent (dpy, window, dd.glx_context);
730 if (geometry->width <= 0 || geometry->height <= 0)
732 /* This can happen if an old version of xscreensaver-getimage
736 geometry->width = dd.pix_width;
737 geometry->height = dd.pix_height;
740 if (geometry->width <= 0 || geometry->height <= 0)
744 cvt_time = double_time();
746 # ifdef REFORMAT_IMAGE_DATA
747 ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
749 type = GL_UNSIGNED_BYTE;
751 #else /* ! REFORMAT_IMAGE_DATA */
753 Visual *visual = DefaultVisualOfScreen (screen);
756 ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
757 dd.pix_width, dd.pix_height, 32, 0);
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);
764 !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
765 ~0L, ximage->format, ximage, 0, 0))
767 XDestroyImage (ximage);
771 gl_settings_for_ximage (ximage, &type, &format, &swap);
772 glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
774 #endif /* REFORMAT_IMAGE_DATA */
776 XFreePixmap (dpy, dd.pixmap);
780 tex_time = double_time();
789 glBindTexture (GL_TEXTURE_2D, dd.texid);
791 glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
792 ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
796 iw = ximage->width; /* in case the image was shrunk */
801 if (ximage) XDestroyImage (ximage);
804 iw = ih = tw = th = 0;
807 done_time = double_time();
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"
817 B+C is responsible for any frame-rate glitches.
819 "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
821 cvt_time - dd.load_time,
823 done_time - tex_time,
824 done_time - dd.load_time);
827 /* asynchronous mode */
828 dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
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;