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 }