summaryrefslogtreecommitdiffstats
path: root/hacks/glx/grab-ximage.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/grab-ximage.c')
-rw-r--r--hacks/glx/grab-ximage.c468
1 files changed, 421 insertions, 47 deletions
diff --git a/hacks/glx/grab-ximage.c b/hacks/glx/grab-ximage.c
index 0f71847..7675302 100644
--- a/hacks/glx/grab-ximage.c
+++ b/hacks/glx/grab-ximage.c
@@ -1,5 +1,7 @@
/* grab-ximage.c --- grab the screen to an XImage for use with OpenGL.
- * xscreensaver, Copyright (c) 2001-2008 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 2001-2021 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Modified by Richard Weeks <rtweeks21@gmail.com> Copyright (c) 2020
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -10,39 +12,17 @@
* implied warranty.
*/
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifdef HAVE_ANDROID
-#include <GLES/gl.h>
-#endif
-
-#ifdef HAVE_JWXYZ
-# include "jwxyz.h"
-# ifndef HAVE_JWZGLES
-# include <OpenGL/glu.h>
-# endif
-#else
-# include <X11/Xlib.h>
-# include <X11/Xutil.h>
-# include <GL/gl.h> /* only for GLfloat */
-# include <GL/glu.h> /* for gluBuild2DMipmaps */
-# include <GL/glx.h> /* for glXMakeCurrent() */
-#endif
-
-#ifdef HAVE_JWZGLES
-# include "jwzgles.h"
-#endif /* HAVE_JWZGLES */
-
+#include "xlockmoreI.h"
#include "grab-ximage.h"
#include "grabscreen.h"
#include "pow2.h"
#include "visual.h"
+#include "xshm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
/* If REFORMAT_IMAGE_DATA is defined, then we convert Pixmaps to textures
like this:
@@ -65,27 +45,11 @@
*/
#define REFORMAT_IMAGE_DATA
-
-#include "xshm.h"
-
-extern char *progname;
-
-#include <sys/time.h>
-
-#ifdef HAVE_JWXYZ
-# include "jwxyz.h"
-#else
-# include <X11/Xutil.h>
-#endif
-
#undef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
-#undef countof
-#define countof(x) (sizeof((x))/sizeof((*x)))
-
-
static int debug_p = 0;
+static double perf = 32768000.0; /* initially assume 2^19 pixels in 1/60/sec */
static Bool
bigendian (void)
@@ -95,6 +59,12 @@ bigendian (void)
return !u.c[0];
}
+#if defined(HAVE_IPHONE) || defined(HAVE_ANDROID)
+# undef GENERATE_MIPMAPS
+#else
+# define GENERATE_MIPMAPS
+#endif
+
#ifdef REFORMAT_IMAGE_DATA
@@ -492,6 +462,27 @@ typedef struct {
} img_closure;
+typedef enum { TLP_LOADING = 0, TLP_IMPORTING, TLP_COMPLETE, TLP_ERROR } texture_loader_phase;
+struct texture_loader_t {
+ texture_loader_phase phase;
+ Screen *screen;
+ Window window;
+ XShmSegmentInfo shm_info;
+ XImage *ximage;
+ img_closure load_closure;
+ Bool pixmap_valid_p;
+ int img_width, img_height, tex_width, tex_height;
+ XRectangle geometry;
+ int y;
+ unsigned int stripe_height;
+ char *name;
+
+ /* debugging */
+ int steps; /* number of calls to step_texture_loader() that loaded part of the texture */
+ int stripes; /* number of stripes put into the texture so far */
+ double loaded_time, work_seconds;
+};
+
/* Returns the current time in seconds as a double.
*/
@@ -678,6 +669,95 @@ load_texture_async (Screen *screen, Window window,
}
+static void incremental_load_texture_async_cb (Screen *screen,
+ Window window,
+ Drawable drawable,
+ const char *name,
+ XRectangle *geometry,
+ void *closure);
+
+
+/* Allocate a texture loader to grab the image of a Window and load the image
+ into GL's texture memory.
+
+ The returned texture loader should be stepped with step_texture_loader
+ until that function calls the callback function passed to it.
+ */
+texture_loader_t*
+alloc_texture_loader (Screen *screen, Window window,
+ GLXContext glx_context,
+ int desired_width, int desired_height,
+ Bool mipmap_p,
+ GLuint texid)
+{
+ texture_loader_t *loader = (texture_loader_t*) calloc (1, sizeof(*loader));
+ Display *dpy = DisplayOfScreen (screen);
+ XWindowAttributes xgwa;
+ img_closure *data = &loader->load_closure;
+
+ if (debug_p)
+ data->load_time = double_time();
+
+ data->texid = texid;
+ data->mipmap_p = mipmap_p;
+ data->glx_context = glx_context;
+ loader->screen = screen;
+ loader->window = window;
+
+ XGetWindowAttributes (dpy, window, &xgwa);
+ data->pix_width = xgwa.width;
+ data->pix_height = xgwa.height;
+ data->pix_depth = xgwa.depth;
+
+ /* Allow the pixmap to be larger than the window. Esper wants this. */
+ if (desired_width /* && desired_width < xgwa.width */)
+ data->pix_width = desired_width;
+ if (desired_height /* && desired_height < xgwa.height */)
+ data->pix_height = desired_height;
+
+ data->pixmap = XCreatePixmap (dpy, window, data->pix_width, data->pix_height,
+ data->pix_depth);
+ loader->pixmap_valid_p = True;
+ load_image_async (screen, window, data->pixmap,
+ incremental_load_texture_async_cb, loader);
+
+ return loader;
+}
+
+
+/* Free the texture loader
+ */
+void
+free_texture_loader (texture_loader_t *loader)
+{
+ Display *dpy = DisplayOfScreen(loader->screen);
+
+ /* If the loader is still awaiting asynchronous completion of loading the
+ XImage, there is no way to recover from this request.
+ */
+ if (loader->phase == TLP_LOADING)
+ abort();
+
+ if (loader->ximage)
+ {
+ XImage *ximage = loader->ximage;
+ loader->ximage = 0;
+ destroy_xshm_image (dpy, ximage, &loader->shm_info);
+ }
+
+ if (loader->pixmap_valid_p)
+ {
+ loader->pixmap_valid_p = False;
+ XFreePixmap (dpy, loader->load_closure.pixmap);
+ }
+
+ free ((char *) loader->name);
+ loader->name = 0;
+
+ free (loader);
+}
+
+
/* Once we have an XImage, this loads it into GL.
This is used in both synchronous and asynchronous mode.
*/
@@ -811,3 +891,297 @@ load_texture_async_cb (Screen *screen, Window window, Drawable drawable,
if (dd.texture_height_return) *dd.texture_height_return = th;
}
}
+
+
+/* Once we have a pixmap, this sets us up to step-load it into a GL texture.
+ */
+static void
+incremental_load_texture_async_cb (Screen *screen, Window window,
+ Drawable drawable, const char *name,
+ XRectangle *geometry,
+ void *closure)
+{
+ texture_loader_t *loader = (texture_loader_t *) closure;
+ Display *dpy = DisplayOfScreen (screen);
+ img_closure dd = loader->load_closure;
+ GLenum err = 0;
+ GLsizei tex_width = 0, tex_height = 0;
+
+ loader->load_closure.texid = dd.texid;
+ loader->phase = TLP_IMPORTING;
+
+ if (debug_p)
+ loader->loaded_time = double_time();
+
+ /* Like load_texture_async_cb() until it calls pixmap_to_gl_ximage() */
+
+ if (dd.glx_context)
+ glXMakeCurrent (dpy, window, dd.glx_context);
+
+ if (geometry->width <= 0 || geometry->height <= 0)
+ {
+ geometry->x = 0;
+ geometry->y = 0;
+ geometry->width = dd.pix_width;
+ geometry->height = dd.pix_height;
+ }
+
+ if (geometry->width <= 0 || geometry->height <= 0)
+ abort();
+
+ loader->geometry = *geometry;
+
+ /* Like pixmap_to_gl_ximage(): */
+ {
+ Visual *visual = DefaultVisualOfScreen (screen);
+ unsigned int width, height, depth;
+
+ {
+ Window root;
+ int x, y;
+ unsigned int bw;
+ XGetGeometry (dpy, dd.pixmap, &root, &x, &y, &width, &height, &bw, &depth);
+ }
+
+ if (width < 5 || height < 5)
+ {
+ loader->phase = TLP_ERROR;
+ return;
+ }
+
+ loader->ximage = create_xshm_image (dpy, visual, depth, ZPixmap,
+ &loader->shm_info, width, height);
+
+ get_xshm_image (dpy, dd.pixmap, loader->ximage, 0, 0, ~0L, &loader->shm_info);
+ }
+
+ loader->img_width = loader->ximage->width;
+ loader->img_height = loader->ximage->height;
+ loader->stripe_height = (1 << 19) / loader->img_width;
+ if (dd.texid != -1)
+ glBindTexture (GL_TEXTURE_2D, dd.texid);
+
+ /* as much of ximage_to_texture() functionality as we can precompute */
+ tex_width = (GLsizei) to_pow2 (loader->ximage->width);
+ tex_height = (GLsizei) to_pow2 (loader->ximage->height);
+
+ /* glTexImage2D() to allocate OpenGL texture */
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ err = glGetError();
+
+ if (err)
+ {
+ loader->phase = TLP_ERROR;
+ loader->img_width = loader->img_height = 0;
+ return;
+ }
+
+ /* Capture texture dimensions and name in loader */
+ loader->tex_width = tex_width;
+ loader->tex_height = tex_height;
+ loader->name = name ? strdup(name) : 0;
+}
+
+
+static void
+advance_texture_loader (texture_loader_t *loader, double allowed_seconds);
+static void
+complete_texture_load (texture_loader_t *loader,
+ void (*callback) (const char *filename,
+ XRectangle *geometry,
+ int image_width,
+ int image_height,
+ int texture_width,
+ int texture_height,
+ void *closure),
+ void *closure);
+
+
+void
+step_texture_loader (texture_loader_t *loader, double allowed_seconds,
+ void (*callback) (const char *filename,
+ XRectangle *geometry,
+ int image_width,
+ int image_height,
+ int texture_width,
+ int texture_height,
+ void *closure),
+ void *closure)
+{
+ if (loader->phase != TLP_IMPORTING)
+ return;
+
+ if (loader->y < loader->ximage->height)
+ {
+ loader->steps++;
+ if (loader->steps == 1)
+ /* Initial tune on the loader for the number of allowed_seconds */
+ loader->stripe_height = ((unsigned int) (perf * allowed_seconds / loader->ximage->width)) / 8 + 1;
+ else
+ advance_texture_loader (loader, allowed_seconds);
+ }
+ else
+ {
+ loader->phase = TLP_COMPLETE;
+ complete_texture_load (loader, callback, closure);
+ }
+}
+
+
+static unsigned int
+texture_loader_next_stripe_height (texture_loader_t *loader)
+{
+ if (loader->y + loader->stripe_height <= loader->img_height)
+ return loader->stripe_height;
+ else
+ return loader->img_height - loader->y;
+}
+
+
+static void
+advance_texture_loader (texture_loader_t *loader, double allowed_seconds)
+{
+ Display *dpy = loader->screen ? DisplayOfScreen (loader->screen) : 0;
+ double start_time = double_time(), elapsed_seconds = 0;
+ double step_end = start_time + allowed_seconds;
+ int iter_count = 0;
+ unsigned int lines_processed = 0;
+
+ if (loader->load_closure.glx_context)
+ glXMakeCurrent (dpy, loader->window, loader->load_closure.glx_context);
+
+ for (
+ ;
+ (double_time() < step_end) && (loader->y < loader->img_height);
+ loader->y += loader->stripe_height, ++iter_count
+ )
+ {
+ /*
+ * Use XSubImage() to extract a section of the shared image
+ loader->stripe_height tall
+ * Use convert_ximage_to_rgba32() to convert that to the desired format
+ * Use XDestroyImage() to free the extracted section of the shared image
+ * glBindTexture(GL_TEXTURE_2D, loader->load_closure.texid)
+ * Import the data to the next stripe of the texture with glTexSubImage2D()
+ * XDestroyImage() to destroy the converted image
+ * Increment loader->y by loader->stripe_height
+ */
+ unsigned int patch_height = texture_loader_next_stripe_height (loader);
+ XImage* patch = XSubImage (loader->ximage,
+ 0, loader->y,
+ loader->img_width, patch_height);
+ XImage* cvt_patch = convert_ximage_to_rgba32 (loader->screen, patch);
+ Bool use_old_mipmap_p = False;
+# ifdef GENERATE_MIPMAPS
+ use_old_mipmap_p = (loader->load_closure.mipmap_p &&
+ (loader->y + loader->stripe_height >=
+ loader->ximage->height));
+# endif
+
+ loader->stripes++;
+
+ XDestroyImage (patch);
+
+ glBindTexture (GL_TEXTURE_2D, loader->load_closure.texid);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, cvt_patch->bitmap_pad / 8);
+
+ if (use_old_mipmap_p)
+ /* Use old GL_GENERATE_MIPMAP (if before OpenGL 3.0) */
+ glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
+
+ /* CHECK: loader->y or -loader->y? */
+ glTexSubImage2D (GL_TEXTURE_2D, 0, 0, loader->y,
+ cvt_patch->width, cvt_patch->height,
+ GL_RGBA, GL_UNSIGNED_BYTE, cvt_patch->data);
+
+ if (use_old_mipmap_p)
+ /* Turn off GL_GENERATE_MIPMAP if we turned it on */
+ glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
+
+ XDestroyImage (cvt_patch);
+
+ lines_processed += patch_height;
+ }
+
+ if (iter_count == 1 && loader->y < loader->img_height && loader->stripe_height > 1)
+ {
+ loader->stripe_height >>= 1;
+ }
+
+ elapsed_seconds = double_time() - start_time;
+ /* monitor perf for use setting the next initial stripe_height (px/s) */
+ if (elapsed_seconds > 0.001)
+ {
+ perf = lines_processed * (double) loader->ximage->width / elapsed_seconds;
+ if (perf > 2e8)
+ perf = 2e8;
+ else if (perf < 1e6)
+ perf = 1e6;
+ }
+
+ if (debug_p)
+ loader->work_seconds += elapsed_seconds;
+}
+
+
+static void
+complete_texture_load (texture_loader_t *loader,
+ void (*callback) (const char *filename,
+ XRectangle *geometry,
+ int image_width,
+ int image_height,
+ int texture_width,
+ int texture_height,
+ void *closure),
+ void *closure)
+{
+ XImage *ximage = loader->ximage;
+ Display *dpy = loader->screen ? DisplayOfScreen (loader->screen) : 0;
+ char *name = loader->name;
+ XRectangle geometry = loader->geometry;
+
+ loader->name = 0;
+
+ if (loader->load_closure.glx_context)
+ glXMakeCurrent (dpy, loader->window, loader->load_closure.glx_context);
+
+ loader->ximage = 0;
+ destroy_xshm_image (dpy, ximage, &loader->shm_info);
+
+ loader->pixmap_valid_p = False;
+ XFreePixmap (dpy, loader->load_closure.pixmap);
+
+ if (loader->load_closure.mipmap_p)
+ {
+ glBindTexture (GL_TEXTURE_2D, loader->load_closure.texid);
+# ifdef GENERATE_MIPMAPS
+ glGenerateMipmap (GL_TEXTURE_2D);
+# endif
+ }
+
+ if (debug_p)
+ {
+ double done_time = double_time();
+ fprintf (stderr,
+ "%s: texture loading: [load %.2f sec] [import %.2f sec, %d stripes, %d steps] [elapsed %2.f sec]\n",
+ progname,
+ loader->loaded_time - loader->load_closure.load_time,
+ loader->work_seconds,
+ loader->stripes,
+ loader->steps,
+ done_time - loader->load_closure.load_time);
+ }
+
+ callback (name, &geometry,
+ loader->img_width, loader->img_height,
+ loader->tex_width, loader->tex_height,
+ closure);
+
+ free (name);
+}
+
+Bool texture_loader_failed (texture_loader_t *loader)
+{
+ return loader->phase == TLP_ERROR;
+}