gnome-shell-3.6.3.1/src/shell-screen-grabber.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 
  3 #include <string.h>
  4 
  5 #include <clutter/clutter.h>
  6 #include <cogl/cogl.h>
  7 #include <GL/gl.h>
  8 #include <GL/glx.h>
  9 #include <GL/glext.h>
 10 
 11 #include "shell-screen-grabber.h"
 12 
 13 PFNGLBINDBUFFERARBPROC pf_glBindBufferARB;
 14 PFNGLBUFFERDATAARBPROC pf_glBufferDataARB;
 15 PFNGLDELETEBUFFERSARBPROC pf_glDeleteBuffersARB;
 16 PFNGLGENBUFFERSARBPROC pf_glGenBuffersARB;
 17 PFNGLMAPBUFFERARBPROC pf_glMapBufferARB;
 18 PFNGLUNMAPBUFFERARBPROC pf_glUnmapBufferARB;
 19 
 20 struct _ShellScreenGrabberClass
 21 {
 22   GObjectClass parent_class;
 23 };
 24 
 25 struct _ShellScreenGrabber
 26 {
 27   GObject parent_instance;
 28 
 29   int have_pixel_buffers;
 30   int have_pack_invert;
 31   int width, height;
 32   GLuint pixel_buffer;
 33 };
 34 
 35 G_DEFINE_TYPE(ShellScreenGrabber, shell_screen_grabber, G_TYPE_OBJECT);
 36 
 37 static void
 38 shell_screen_grabber_finalize (GObject *gobject)
 39 {
 40   ShellScreenGrabber *grabber = SHELL_SCREEN_GRABBER (gobject);
 41 
 42   if (grabber->pixel_buffer != 0)
 43     pf_glDeleteBuffersARB (1, &grabber->pixel_buffer);
 44 }
 45 
 46 static void
 47 shell_screen_grabber_class_init (ShellScreenGrabberClass *grabber_class)
 48 {
 49   GObjectClass *gobject_class = G_OBJECT_CLASS (grabber_class);
 50 
 51   gobject_class->finalize = shell_screen_grabber_finalize;
 52 }
 53 
 54 static void
 55 shell_screen_grabber_init (ShellScreenGrabber *grabber)
 56 {
 57   grabber->have_pixel_buffers = -1;
 58   grabber->width = -1;
 59   grabber->height= -1;
 60   grabber->pixel_buffer = 0;
 61 }
 62 
 63 ShellScreenGrabber *
 64 shell_screen_grabber_new  (void)
 65 {
 66   return g_object_new (SHELL_TYPE_SCREEN_GRABBER, NULL);
 67 }
 68 
 69 /**
 70  * shell_screen_grabber_grab:
 71  * x: X coordinate of the rectangle to grab
 72  * y: Y coordinate of the rectangle to grab
 73  * width: width of the rectangle to grab
 74  * height: heigth of the rectangle to grab
 75  *
 76  * Grabs pixel data from a portion of the screen.
 77  *
 78  * Return value: buffer holding the grabbed data. The data is stored as 32-bit
 79  *  words with native-endian xRGB pixels (i.e., the same as CAIRO_FORMAT_RGB24)
 80  *  with no padding on the rows. So, the size of the buffer is width * height * 4
 81  *  bytes. Free with g_free().
 82  **/
 83 guchar *
 84 shell_screen_grabber_grab (ShellScreenGrabber *grabber,
 85                            int                 x,
 86                            int                 y,
 87                            int                 width,
 88                            int                 height)
 89 {
 90   guchar *data;
 91   gsize row_bytes;
 92   gsize data_size;
 93 
 94   row_bytes = width * 4;
 95   data_size = row_bytes * height;
 96   data = g_malloc (data_size);
 97 
 98   if (grabber->have_pixel_buffers == -1)
 99     {
100       const GLubyte* extensions = glGetString (GL_EXTENSIONS);
101       grabber->have_pixel_buffers = strstr ((const char *)extensions, "GL_EXT_pixel_buffer_object") != NULL;
102       grabber->have_pack_invert = strstr ((const char *)extensions, "GL_MESA_pack_invert") != NULL;
103     }
104 
105   if (grabber->have_pixel_buffers)
106     {
107       GLubyte *mapped_data;
108       GLint old_swap_bytes, old_lsb_first, old_row_length, old_skip_pixels, old_skip_rows, old_alignment;
109       GLint old_pack_invert = GL_FALSE;
110       GLint vp_size[4];
111       guchar *src_row, *dest_row;
112       int i;
113 
114       cogl_flush ();
115 
116       if (pf_glBindBufferARB == NULL)
117         {
118           pf_glBindBufferARB = (PFNGLBINDBUFFERARBPROC) cogl_get_proc_address ("glBindBufferARB");
119           pf_glBufferDataARB = (PFNGLBUFFERDATAARBPROC) cogl_get_proc_address ("glBufferDataARB");
120           pf_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) cogl_get_proc_address ("glDeleteBuffersARB");
121           pf_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) cogl_get_proc_address ("glGenBuffersARB");
122           pf_glMapBufferARB = (PFNGLMAPBUFFERARBPROC) cogl_get_proc_address ("glMapBufferARB");
123           pf_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) cogl_get_proc_address ("glUnmapBufferARB");
124         }
125 
126       glGetIntegerv (GL_PACK_SWAP_BYTES, &old_swap_bytes);
127       glGetIntegerv (GL_PACK_LSB_FIRST, &old_lsb_first);
128       glGetIntegerv (GL_PACK_ROW_LENGTH, &old_row_length);
129       glGetIntegerv (GL_PACK_SKIP_PIXELS, &old_skip_pixels);
130       glGetIntegerv (GL_PACK_SKIP_ROWS, &old_skip_rows);
131       glGetIntegerv (GL_PACK_ALIGNMENT, &old_alignment);
132 
133       glPixelStorei (GL_PACK_SWAP_BYTES, GL_FALSE);
134       glPixelStorei (GL_PACK_LSB_FIRST, GL_FALSE);
135       glPixelStorei (GL_PACK_ROW_LENGTH, 0);
136       glPixelStorei (GL_PACK_SKIP_PIXELS, 0);
137       glPixelStorei (GL_PACK_SKIP_ROWS, 0);
138       glPixelStorei (GL_PACK_ALIGNMENT, 1);
139 
140       if (grabber->have_pack_invert)
141         {
142           glGetIntegerv (GL_PACK_INVERT_MESA, &old_pack_invert);
143           glPixelStorei (GL_PACK_INVERT_MESA, GL_FALSE);
144         }
145 
146       if (grabber->pixel_buffer != 0 &&
147           (grabber->width != width ||
148            grabber->height != height))
149         {
150           pf_glDeleteBuffersARB (1, &grabber->pixel_buffer);
151           grabber->pixel_buffer = 0;
152         }
153 
154       if (grabber->pixel_buffer == 0)
155         {
156           pf_glGenBuffersARB (1, &grabber->pixel_buffer);
157 
158           pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer);
159           pf_glBufferDataARB (GL_PIXEL_PACK_BUFFER_ARB, data_size, 0, GL_STREAM_READ_ARB);
160 
161           grabber->width = width;
162           grabber->height = height;
163         }
164       else
165         {
166           pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, grabber->pixel_buffer);
167         }
168 
169       /* In OpenGL, (x,y) specifies the bottom-left corner rather than the
170        * top-left */
171       glGetIntegerv (GL_VIEWPORT, vp_size);
172       y = vp_size[3] - (y + height);
173 
174       /* the "big-endian" version actually works for both, but the litle-endian
175        * version has been better tested with a range of drivers, so we'll
176        * keep on using it on little-endian.
177        */
178 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
179       glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_BYTE, 0);
180 #else
181       glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
182 #endif
183 
184       mapped_data = pf_glMapBufferARB (GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
185 
186       src_row = mapped_data + (height - 1) * row_bytes;
187       dest_row = data;
188 
189       for (i = 0; i < height; i++)
190         {
191           memcpy (dest_row, src_row, row_bytes);
192           src_row -= row_bytes;
193           dest_row += row_bytes;
194         }
195 
196       pf_glUnmapBufferARB (GL_PIXEL_PACK_BUFFER_ARB);
197       pf_glBindBufferARB (GL_PIXEL_PACK_BUFFER_ARB, 0);
198 
199       glPixelStorei (GL_PACK_SWAP_BYTES, old_swap_bytes);
200       glPixelStorei (GL_PACK_LSB_FIRST, old_lsb_first);
201       glPixelStorei (GL_PACK_ROW_LENGTH, old_row_length);
202       glPixelStorei (GL_PACK_SKIP_PIXELS, old_skip_pixels);
203       glPixelStorei (GL_PACK_SKIP_ROWS, old_skip_rows);
204       glPixelStorei (GL_PACK_ALIGNMENT, old_alignment);
205 
206       if (grabber->have_pack_invert)
207         glPixelStorei (GL_PACK_INVERT_MESA, old_pack_invert);
208     }
209   else
210     {
211       cogl_read_pixels (x, y,
212                         width, height,
213                         COGL_READ_PIXELS_COLOR_BUFFER,
214                         CLUTTER_CAIRO_FORMAT_ARGB32,
215                         data);
216     }
217 
218   return data;
219 }