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
28 # include <OpenGL/glu.h>
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() */
40 #endif /* HAVE_JWZGLES */
42 #include "grab-ximage.h"
43 #include "grabscreen.h"
47 /* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
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.
54 If undefined, we do this:
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.
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!
66 #define REFORMAT_IMAGE_DATA
71 extern char *progname;
78 # include <X11/Xutil.h>
82 #define MAX(a,b) ((a)>(b)?(a):(b))
85 #define countof(x) (sizeof((x))/sizeof((*x)))
88 static int debug_p = 0;
93 union { int i; char c[sizeof(int)]; } u;
99 #ifdef REFORMAT_IMAGE_DATA
101 /* Given a bitmask, returns the position and width of the field.
104 decode_mask (unsigned long mask, unsigned long *pos_ret,
105 unsigned long *size_ret)
108 for (i = 0; i < 32; i++)
109 if (mask & (1L << i))
113 for (; i < 32; i++, j++)
114 if (! (mask & (1L << i)))
122 /* Given a value and a field-width, expands the field to fill out 8 bits.
125 spread_bits (unsigned char value, unsigned char width)
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;
142 convert_ximage_to_rgba32 (Screen *screen, XImage *image)
144 Display *dpy = DisplayOfScreen (screen);
145 Visual *visual = DefaultVisualOfScreen (screen);
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;
153 unsigned char spread_map[3][256];
155 /* Note: height+2 in "to" to work around an array bounds overrun
156 in gluBuild2DMipmaps / gluScaleImage.
158 XImage *from = image;
159 XImage *to = XCreateImage (dpy, visual, 32, /* depth */
160 ZPixmap, 0, 0, from->width, from->height,
163 to->data = (char *) calloc (to->height + 2, to->bytes_per_line);
165 /* Set the bit order in the XImage structure to whatever the
166 local host's native bit order is.
168 to->bitmap_bit_order =
170 (bigendian() ? MSBFirst : LSBFirst);
172 if (visual_class (screen, visual) == PseudoColor ||
173 visual_class (screen, visual) == GrayScale)
175 Colormap cmap = DefaultColormapOfScreen (screen);
176 int ncolors = visual_cells (screen, visual);
178 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
179 for (i = 0; i < ncolors; i++)
181 XQueryColors (dpy, cmap, colors, ncolors);
184 if (colors == 0) /* truecolor */
186 srmsk = to->red_mask;
187 sgmsk = to->green_mask;
188 sbmsk = to->blue_mask;
190 decode_mask (srmsk, &srpos, &srsiz);
191 decode_mask (sgmsk, &sgpos, &sgsiz);
192 decode_mask (sbmsk, &sbpos, &sbsiz);
195 /* Pack things in "RGBA" order in client endianness. */
197 crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
199 crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
201 if (colors == 0) /* truecolor */
204 for (i = 0; i < 256; i++)
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);
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();
216 for (y = 0; y < from->height; y++)
217 for (x = 0; x < from->width; x++)
219 unsigned long sp = XGetPixel (from, x, y);
220 unsigned char sr, sg, sb;
225 sr = colors[sp].red & 0xFF;
226 sg = colors[sp].green & 0xFF;
227 sb = colors[sp].blue & 0xFF;
231 sr = (sp & srmsk) >> srpos;
232 sg = (sp & sgmsk) >> sgpos;
233 sb = (sp & sbmsk) >> sbpos;
235 sr = spread_map[0][sr];
236 sg = spread_map[1][sg];
237 sb = spread_map[2][sb];
240 cp = ((sr << crpos) |
245 XPutPixel (to, x, y, cp);
248 if (colors) free (colors);
253 #endif /* REFORMAT_IMAGE_DATA */
255 /* Shrinks the XImage by a factor of two.
256 We use this when mipmapping fails on large textures.
259 halve_image (XImage *ximage, XRectangle *geom)
261 int w2 = ximage->width/2;
262 int h2 = ximage->height/2;
266 if (w2 <= 32 || h2 <= 32) /* let's not go crazy here, man. */
270 fprintf (stderr, "%s: shrinking image %dx%d -> %dx%d\n",
271 progname, ximage->width, ximage->height, w2, h2);
273 ximage2 = (XImage *) calloc (1, sizeof (*ximage2));
276 ximage2->height = h2;
277 ximage2->bytes_per_line = 0;
279 XInitImage (ximage2);
281 ximage2->data = (char *) calloc (h2, ximage2->bytes_per_line);
284 fprintf (stderr, "%s: out of memory (scaling %dx%d image to %dx%d)\n",
285 progname, ximage->width, ximage->height, w2, h2);
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));
308 #ifdef REFORMAT_IMAGE_DATA
310 /* Pulls the Pixmap bits from the server and returns an XImage
311 in some format acceptable to OpenGL.
314 pixmap_to_gl_ximage (Screen *screen, Window window, Pixmap pixmap)
316 Display *dpy = DisplayOfScreen (screen);
317 Visual *visual = DefaultVisualOfScreen (screen);
318 unsigned int width, height, depth;
320 XShmSegmentInfo shm_info;
322 XImage *server_ximage = 0;
323 XImage *client_ximage = 0;
329 XGetGeometry (dpy, pixmap, &root, &x, &y, &width, &height, &bw, &depth);
332 if (width < 5 || height < 5) /* something's gone wrong somewhere... */
335 /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
337 server_ximage = create_xshm_image (dpy, visual, depth, ZPixmap, &shm_info,
339 get_xshm_image (dpy, pixmap, server_ximage, 0, 0, ~0L, &shm_info);
341 client_ximage = convert_ximage_to_rgba32 (screen, server_ximage);
343 destroy_xshm_image (dpy, server_ximage, &shm_info);
345 return client_ximage;
349 # else /* ! REFORMAT_IMAGE_DATA */
352 unsigned int depth, red_mask, green_mask, blue_mask; /* when this... */
353 GLint type, format; /* ...use this. */
356 /* Abbreviate these so that the table entries all fit on one line...
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
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 },
402 /* Given an XImage, returns the GL settings to use its data as a texture.
405 gl_settings_for_ximage (XImage *image,
406 GLint *type_ret, GLint *format_ret, GLint *swap_ret)
409 for (i = 0; i < countof(ctable); ++i)
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)
416 *type_ret = ctable[i].type;
417 *format_ret = ctable[i].format;
419 if (image->bits_per_pixel == 24)
421 /* don't know how to test this... */
422 *type_ret = (ctable[i].type == GL_RGB) ? GL_BGR : GL_RGB;
427 *swap_ret = !!(image->byte_order == MSBFirst) ^ !!bigendian();
432 fprintf (stderr, "%s: using %s %s %d for %d %08lX %08lX %08lX\n",
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" :
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" :
455 image->bits_per_pixel,
456 image->red_mask, image->green_mask, image->blue_mask);
463 /* Unknown RGB fields? */
467 #endif /* ! REFORMAT_IMAGE_DATA */
470 GLXContext glx_context;
472 int pix_width, pix_height, pix_depth;
477 /* Used in async mode
479 void (*callback) (const char *filename, XRectangle *geometry,
480 int iw, int ih, int tw, int th,
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;
496 /* Returns the current time in seconds as a double.
502 # ifdef GETTIMEOFDAY_TWO_ARGS
504 gettimeofday(&now, &tzp);
509 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
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.
519 ximage_to_texture (XImage *ximage,
520 GLint type, GLint format,
523 XRectangle *geometry,
526 int max_reduction = 7;
529 int orig_width = ximage->width;
530 int orig_height = ximage->height;
531 GLsizei tex_width = 0;
532 GLsizei tex_height = 0;
538 /* gluBuild2DMipmaps doesn't require textures to be a power of 2. */
539 tex_width = ximage->width;
540 tex_height = ximage->height;
543 fprintf (stderr, "%s: mipmap %d x %d\n",
544 progname, ximage->width, ximage->height);
546 gluBuild2DMipmaps (GL_TEXTURE_2D, 3, ximage->width, ximage->height,
547 format, type, ximage->data);
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
556 tex_width = (GLsizei) to_pow2 (ximage->width);
557 tex_height = (GLsizei) to_pow2 (ximage->height);
560 fprintf (stderr, "%s: texture %d x %d (%d x %d)\n",
561 progname, ximage->width, ximage->height,
562 tex_width, tex_height);
564 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0,
568 /* Now load our non-power-of-2 image data into the existing texture. */
571 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
572 ximage->width, ximage->height,
573 GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
581 const char *s = (char *) gluErrorString (err);
585 sprintf (buf, "unknown error %d", (int) err);
589 while (glGetError() != GL_NO_ERROR)
590 ; /* clear any lingering errors */
592 if (++err_count > max_reduction)
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,
609 fprintf (stderr, "%s: mipmap error (%dx%d): %s\n",
610 progname, ximage->width, ximage->height, s);
611 halve_image (ximage, geometry);
616 if (width_return) *width_return = tex_width;
617 if (height_return) *height_return = tex_height;
622 static void load_texture_async_cb (Screen *screen,
623 Window window, Drawable drawable,
624 const char *name, XRectangle *geometry,
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.)
633 If an error occurred, width/height will be 0.
636 load_texture_async (Screen *screen, Window window,
637 GLXContext glx_context,
638 int desired_width, int desired_height,
641 void (*callback) (const char *filename,
642 XRectangle *geometry,
650 Display *dpy = DisplayOfScreen (screen);
651 XWindowAttributes xgwa;
652 img_closure *data = (img_closure *) calloc (1, sizeof(*data));
655 data->load_time = double_time();
658 data->mipmap_p = mipmap_p;
659 data->glx_context = glx_context;
660 data->callback = callback;
661 data->closure = closure;
663 XGetWindowAttributes (dpy, window, &xgwa);
664 data->pix_width = xgwa.width;
665 data->pix_height = xgwa.height;
666 data->pix_depth = xgwa.depth;
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;
673 data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
675 load_image_async (screen, window, data->pixmap,
676 load_texture_async_cb, data);
680 /* Once we have an XImage, this loads it into GL.
681 This is used in both synchronous and asynchronous mode.
684 load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
685 const char *name, XRectangle *geometry, void *closure)
687 Display *dpy = DisplayOfScreen (screen);
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));
701 glXMakeCurrent (dpy, window, dd.glx_context);
703 if (geometry->width <= 0 || geometry->height <= 0)
705 /* This can happen if an old version of xscreensaver-getimage
706 is installed. Or if we have no image (checkerboard). */
709 geometry->width = dd.pix_width;
710 geometry->height = dd.pix_height;
713 if (geometry->width <= 0 || geometry->height <= 0)
717 cvt_time = double_time();
719 # ifdef REFORMAT_IMAGE_DATA
720 ximage = pixmap_to_gl_ximage (screen, window, dd.pixmap);
722 type = GL_UNSIGNED_BYTE;
724 #else /* ! REFORMAT_IMAGE_DATA */
726 Visual *visual = DefaultVisualOfScreen (screen);
729 ximage = XCreateImage (dpy, visual, dd.pix_depth, ZPixmap, 0, 0,
730 dd.pix_width, dd.pix_height, 32, 0);
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);
737 !XGetSubImage (dpy, dd.pixmap, 0, 0, ximage->width, ximage->height,
738 ~0L, ximage->format, ximage, 0, 0))
740 XDestroyImage (ximage);
744 gl_settings_for_ximage (ximage, &type, &format, &swap);
745 glPixelStorei (GL_UNPACK_SWAP_BYTES, !swap);
747 #endif /* REFORMAT_IMAGE_DATA */
749 XFreePixmap (dpy, dd.pixmap);
753 tex_time = double_time();
762 glBindTexture (GL_TEXTURE_2D, dd.texid);
764 glPixelStorei (GL_UNPACK_ALIGNMENT, ximage->bitmap_pad / 8);
765 ok = ximage_to_texture (ximage, type, format, &tw, &th, geometry,
769 iw = ximage->width; /* in case the image was shrunk */
774 if (ximage) XDestroyImage (ximage);
777 iw = ih = tw = th = 0;
780 done_time = double_time();
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"
790 B+C is responsible for any frame-rate glitches.
792 "%s: loading elapsed: %.2f + %.2f + %.2f = %.2f sec\n",
794 cvt_time - dd.load_time,
796 done_time - tex_time,
797 done_time - dd.load_time);
800 /* asynchronous mode */
801 dd.callback (name, geometry, iw, ih, tw, th, dd.closure);
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;