ca612872a6
Our DBus API (and mostly every other API in existence) define an area as the top-left corner and width/height; glReadPixels on the other hand uses the bottom-left corner, so we have to transform the coordinates before passing them to GL. https://bugzilla.gnome.org/show_bug.cgi?id=670979
211 lines
6.5 KiB
C
211 lines
6.5 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
#include <string.h>
|
|
|
|
#include <clutter/clutter.h>
|
|
#include <cogl/cogl.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glx.h>
|
|
#include <GL/glext.h>
|
|
|
|
#include "shell-screen-grabber.h"
|
|
|
|
PFNGLBINDBUFFERARBPROC pf_glBindBufferARB;
|
|
PFNGLBUFFERDATAARBPROC pf_glBufferDataARB;
|
|
PFNGLDELETEBUFFERSARBPROC pf_glDeleteBuffersARB;
|
|
PFNGLGENBUFFERSARBPROC pf_glGenBuffersARB;
|
|
PFNGLMAPBUFFERARBPROC pf_glMapBufferARB;
|
|
PFNGLUNMAPBUFFERARBPROC pf_glUnmapBufferARB;
|
|
|
|
struct _ShellScreenGrabberClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
struct _ShellScreenGrabber
|
|
{
|
|
GObject parent_instance;
|
|
|
|
int have_pixel_buffers;
|
|
int have_pack_invert;
|
|
int width, height;
|
|
GLuint pixel_buffer;
|
|
};
|
|
|
|
G_DEFINE_TYPE(ShellScreenGrabber, shell_screen_grabber, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
shell_screen_grabber_finalize (GObject *gobject)
|
|
{
|
|
ShellScreenGrabber *grabber = SHELL_SCREEN_GRABBER (gobject);
|
|
|
|
if (grabber->pixel_buffer != 0)
|
|
pf_glDeleteBuffersARB (1, &grabber->pixel_buffer);
|
|
}
|
|
|
|
static void
|
|
shell_screen_grabber_class_init (ShellScreenGrabberClass *grabber_class)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (grabber_class);
|
|
|
|
gobject_class->finalize = shell_screen_grabber_finalize;
|
|
}
|
|
|
|
static void
|
|
shell_screen_grabber_init (ShellScreenGrabber *grabber)
|
|
{
|
|
grabber->have_pixel_buffers = -1;
|
|
grabber->width = -1;
|
|
grabber->height= -1;
|
|
grabber->pixel_buffer = 0;
|
|
}
|
|
|
|
ShellScreenGrabber *
|
|
shell_screen_grabber_new (void)
|
|
{
|
|
return g_object_new (SHELL_TYPE_SCREEN_GRABBER, NULL);
|
|
}
|
|
|
|
/**
|
|
* shell_screen_grabber_grab:
|
|
* x: X coordinate of the rectangle to grab
|
|
* y: Y coordinate of the rectangle to grab
|
|
* width: width of the rectangle to grab
|
|
* height: heigth of the rectangle to grab
|
|
*
|
|
* Grabs pixel data from a portion of the screen.
|
|
*
|
|
* Return value: buffer holding the grabbed data. The data is stored as 32-bit
|
|
* words with native-endian xRGB pixels (i.e., the same as CAIRO_FORMAT_RGB24)
|
|
* with no padding on the rows. So, the size of the buffer is width * height * 4
|
|
* bytes. Free with g_free().
|
|
**/
|
|
guchar *
|
|
shell_screen_grabber_grab (ShellScreenGrabber *grabber,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
guchar *data;
|
|
gsize row_bytes;
|
|
gsize data_size;
|
|
|
|
row_bytes = width * 4;
|
|
data_size = row_bytes * height;
|
|
data = g_malloc (data_size);
|
|
|
|
if (grabber->have_pixel_buffers == -1)
|
|
{
|
|
const GLubyte* extensions = glGetString (GL_EXTENSIONS);
|
|
grabber->have_pixel_buffers = strstr ((const char *)extensions, "GL_EXT_pixel_buffer_object") != NULL;
|
|
grabber->have_pack_invert = strstr ((const char *)extensions, "GL_MESA_pack_invert") != NULL;
|
|
}
|
|
|
|
if (grabber->have_pixel_buffers)
|
|
{
|
|
GLubyte *mapped_data;
|
|
GLint old_swap_bytes, old_lsb_first, old_row_length, old_skip_pixels, old_skip_rows, old_alignment;
|
|
GLint old_pack_invert = GL_FALSE;
|
|
GLint vp_size[4];
|
|
guchar *src_row, *dest_row;
|
|
int i;
|
|
|
|
cogl_flush ();
|
|
|
|
if (pf_glBindBufferARB == NULL)
|
|
{
|
|
pf_glBindBufferARB = (PFNGLBINDBUFFERARBPROC) cogl_get_proc_address ("glBindBufferARB");
|
|
pf_glBufferDataARB = (PFNGLBUFFERDATAARBPROC) cogl_get_proc_address ("glBufferDataARB");
|
|
pf_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) cogl_get_proc_address ("glDeleteBuffersARB");
|
|
pf_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) cogl_get_proc_address ("glGenBuffersARB");
|
|
pf_glMapBufferARB = (PFNGLMAPBUFFERARBPROC) cogl_get_proc_address ("glMapBufferARB");
|
|
pf_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) cogl_get_proc_address ("glUnmapBufferARB");
|
|
}
|
|
|
|
glGetIntegerv (GL_PACK_SWAP_BYTES, &old_swap_bytes);
|
|
glGetIntegerv (GL_PACK_LSB_FIRST, &old_lsb_first);
|
|
glGetIntegerv (GL_PACK_ROW_LENGTH, &old_row_length);
|
|
glGetIntegerv (GL_PACK_SKIP_PIXELS, &old_skip_pixels);
|
|
glGetIntegerv (GL_PACK_SKIP_ROWS, &old_skip_rows);
|
|
glGetIntegerv (GL_PACK_ALIGNMENT, &old_alignment);
|
|
|
|
glPixelStorei (GL_PACK_SWAP_BYTES, GL_FALSE);
|
|
glPixelStorei (GL_PACK_LSB_FIRST, GL_FALSE);
|
|
glPixelStorei (GL_PACK_ROW_LENGTH, 0);
|
|
glPixelStorei (GL_PACK_SKIP_PIXELS, 0);
|
|
glPixelStorei (GL_PACK_SKIP_ROWS, 0);
|
|
glPixelStorei (GL_PACK_ALIGNMENT, 1);
|
|
|
|
if (grabber->have_pack_invert)
|
|
{
|
|
glGetIntegerv (GL_PACK_INVERT_MESA, &old_pack_invert);
|
|
glPixelStorei (GL_PACK_INVERT_MESA, GL_FALSE);
|
|
}
|
|
|
|
if (grabber->pixel_buffer != 0 &&
|
|
(grabber->width != width ||
|
|
grabber->height != height))
|
|
{
|
|
pf_glDeleteBuffersARB (1, &grabber->pixel_buffer);
|
|
grabber->pixel_buffer = 0;
|
|
}
|
|
|
|
if (grabber->pixel_buffer == 0)
|
|
{
|
|
pf_glGenBuffersARB (1, &grabber->pixel_buffer);
|
|
|
|
pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer);
|
|
pf_glBufferDataARB (GL_PIXEL_PACK_BUFFER_ARB, data_size, 0, GL_STREAM_READ_ARB);
|
|
|
|
grabber->width = width;
|
|
grabber->height = height;
|
|
}
|
|
else
|
|
{
|
|
pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer);
|
|
}
|
|
|
|
/* In OpenGL, (x,y) specifies the bottom-left corner rather than the
|
|
* top-left */
|
|
glGetIntegerv (GL_VIEWPORT, vp_size);
|
|
y = vp_size[3] - (y + height);
|
|
glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
|
|
|
mapped_data = pf_glMapBufferARB (GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
|
|
|
|
src_row = mapped_data + (height - 1) * row_bytes;
|
|
dest_row = data;
|
|
|
|
for (i = 0; i < height; i++)
|
|
{
|
|
memcpy (dest_row, src_row, row_bytes);
|
|
src_row -= row_bytes;
|
|
dest_row += row_bytes;
|
|
}
|
|
|
|
pf_glUnmapBufferARB (GL_PIXEL_PACK_BUFFER_ARB);
|
|
pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, 0);
|
|
|
|
glPixelStorei (GL_PACK_SWAP_BYTES, old_swap_bytes);
|
|
glPixelStorei (GL_PACK_LSB_FIRST, old_lsb_first);
|
|
glPixelStorei (GL_PACK_ROW_LENGTH, old_row_length);
|
|
glPixelStorei (GL_PACK_SKIP_PIXELS, old_skip_pixels);
|
|
glPixelStorei (GL_PACK_SKIP_ROWS, old_skip_rows);
|
|
glPixelStorei (GL_PACK_ALIGNMENT, old_alignment);
|
|
|
|
if (grabber->have_pack_invert)
|
|
glPixelStorei (GL_PACK_INVERT_MESA, old_pack_invert);
|
|
}
|
|
else
|
|
{
|
|
cogl_read_pixels (x, y,
|
|
width, height,
|
|
COGL_READ_PIXELS_COLOR_BUFFER,
|
|
CLUTTER_CAIRO_FORMAT_ARGB32,
|
|
data);
|
|
}
|
|
|
|
return data;
|
|
}
|