version update
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bccapture.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bccapture.h"
23 #include "bcresources.h"
24 #include "bcwindowbase.h"
25 #include "bccmodels.h"
26 #include "bccolors.h"
27 #include "clip.h"
28 #include "language.h"
29 #include "vframe.h"
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <X11/Xutil.h>
33 #include <X11/extensions/Xfixes.h>
34
35 // Byte orders:
36 // 24 bpp packed:         bgr
37 // 24 bpp unpacked:       0bgr
38
39
40 BC_Capture::BC_Capture(int w, int h, const char *display_path)
41 {
42         this->w = w;
43         this->h = h;
44
45         data = 0;
46         use_shm = 1;
47         for( int i=0; i<4; ++i ) border[i] = 0;
48         bar_w = 0;  bar_color = 0;
49         init_window(display_path);
50         allocate_data();
51 }
52
53 BC_Capture::~BC_Capture()
54 {
55         bars_off();
56         delete_data();
57         XCloseDisplay(display);
58 }
59
60
61 int BC_Capture::init_window(const char *display_path)
62 {
63         int bits_per_pixel;
64         if( display_path && display_path[0] == 0 ) display_path = NULL;
65         if( (display = XOpenDisplay(display_path)) == NULL ) {
66                 printf(_("cannot connect to X server.\n"));
67                 if( getenv("DISPLAY") == NULL )
68                 printf(_("'DISPLAY' environment variable not set.\n"));
69                 exit(-1);
70                 return 1;
71         }
72
73         screen = DefaultScreen(display);
74         rootwin = RootWindow(display, screen);
75         vis = DefaultVisual(display, screen);
76         default_depth = DefaultDepth(display, screen);
77         client_byte_order = (*(const u_int32_t*)"a   ") & 0x00000001;
78         server_byte_order = (XImageByteOrder(display) == MSBFirst) ? 0 : 1;
79         char *data = 0;
80         XImage *ximage;
81         ximage = XCreateImage(display, vis, default_depth,
82                         ZPixmap, 0, data, 16, 16, 8, 0);
83         bits_per_pixel = ximage->bits_per_pixel;
84         XDestroyImage(ximage);
85         bitmap_color_model = BC_WindowBase::evaluate_color_model(client_byte_order, server_byte_order, bits_per_pixel);
86
87 // test shared memory
88 // This doesn't ensure the X Server is on the local host
89         if( use_shm && !XShmQueryExtension(display) )
90                 use_shm = 0;
91         return 0;
92 }
93
94 Window BC_Capture::bar(int x, int y, int w, int h, int color)
95 {
96         unsigned long mask = CWEventMask | CWBackPixel |
97                         CWOverrideRedirect | CWSaveUnder;
98         XSetWindowAttributes attr;
99         memset(&attr, 0, sizeof(attr));
100         Screen *scr = XDefaultScreenOfDisplay(display);
101         Window root = RootWindowOfScreen(scr);
102         Visual *vis = DefaultVisualOfScreen(scr);
103         int depth = DefaultDepthOfScreen(scr);
104         attr.background_pixel = color;
105         attr.override_redirect = True;
106         attr.save_under = True;
107         Window win = XCreateWindow(display, root, x,y,w,h, 0,depth,
108                         InputOutput, vis, mask, &attr);
109         XMapWindow(display, win);
110         return win;
111 }
112
113 void BC_Capture::bars_on(int bw, int color, int x, int y, int w, int h)
114 {
115         this->bar_w = bw;
116         this->bar_color = color;
117         border[0] = bar(x-bw, y-bw, w+2*bw, bw, color);
118         border[1] = bar(x-bw, y,    bw,     h,  color);
119         border[2] = bar(x-bw, y+h,  w+2*bw, bw, color);
120         border[3] = bar(x+w,  y,    bw,     h,  color);
121         XFlush(display);
122 }
123
124 void BC_Capture::bars_off()
125 {
126         for( int i=0; i<4; ++i ) {
127                 if( !border[i] ) continue;
128                 XUnmapWindow(display, border[i]);
129         }
130         for( int i=0; i<4; ++i ) {
131                 if( !border[i] ) continue;
132                 XDestroyWindow(display, border[i]);
133                 border[i] = 0;
134         }
135         XFlush(display);
136 }
137
138 void BC_Capture::bars_reposition(int x, int y, int w, int h)
139 {
140         int bw = this->bar_w;
141         if( border[0] )
142                 XMoveResizeWindow(display, border[0], x-bw, y-bw, w+2*bw, bw);
143         if( border[1] )
144                 XMoveResizeWindow(display, border[1], x-bw, y,    bw,     h );
145         if( border[2] )
146                 XMoveResizeWindow(display, border[2], x-bw, y+h,  w+2*bw, bw);
147         if( border[3] )
148                 XMoveResizeWindow(display, border[3], x+w,  y,    bw,     h );
149 }
150
151 int BC_Capture::allocate_data()
152 {
153 // try shared memory
154         if( !display ) return 1;
155         if( use_shm ) {
156                 ximage = XShmCreateImage(display, vis, default_depth, ZPixmap, (char*)NULL, &shm_info, w, h);
157
158                 shm_info.shmid = shmget(IPC_PRIVATE, h * ximage->bytes_per_line, IPC_CREAT | 0600);
159                 if( shm_info.shmid == -1 ) {
160                         perror("BC_Capture::allocate_data shmget");
161                         abort();
162                 }
163                 data = (unsigned char *)shmat(shm_info.shmid, NULL, 0);
164                 shmctl(shm_info.shmid, IPC_RMID, 0);
165                 ximage->data = shm_info.shmaddr = (char*)data;  // setting ximage->data stops BadValue
166                 shm_info.readOnly = 0;
167
168 // Crashes here if remote server.
169                 BC_Resources::error = 0;
170                 XShmAttach(display, &shm_info);
171                 XSync(display, False);
172                 if( BC_Resources::error ) {
173                         XDestroyImage(ximage);
174                         shmdt(shm_info.shmaddr);
175                         use_shm = 0;
176                 }
177         }
178
179         if( !use_shm ) {
180 // need to use bytes_per_line for some X servers
181                 data = 0;
182                 ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, (char*)data, w, h, 8, 0);
183                 data = (unsigned char*)malloc(h * ximage->bytes_per_line);
184                 XDestroyImage(ximage);
185
186                 ximage = XCreateImage(display, vis, default_depth, ZPixmap, 0, (char*)data, w, h, 8, 0);
187         }
188
189         row_data = new unsigned char*[h];
190         for( int i = 0; i < h; i++ ) {
191                 row_data[i] = &data[i * ximage->bytes_per_line];
192         }
193 // This differs from the depth parameter of the top_level.
194         bits_per_pixel = ximage->bits_per_pixel;
195         return 0;
196 }
197
198 int BC_Capture::delete_data()
199 {
200         if( !display ) return 1;
201         if( data ) {
202                 if( use_shm ) {
203                         XShmDetach(display, &shm_info);
204                         XDestroyImage(ximage);
205                         shmdt(shm_info.shmaddr);
206                 }
207                 else {
208                         XDestroyImage(ximage);
209                 }
210
211 // data is automatically freed by XDestroyImage
212                 data = 0;
213                 delete [] row_data;
214         }
215         return 0;
216 }
217
218
219 int BC_Capture::get_w() { return w; }
220 int BC_Capture::get_h() { return h; }
221
222 // Capture a frame from the screen
223 #define RGB_TO_YUV(y, u, v, r, g, b) { \
224  YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \
225  bclamp(y, 0,0xff); bclamp(u, 0,0xff); bclamp(v, 0,0xff); }
226
227 int BC_Capture::capture_frame(VFrame *frame, int &x1, int &y1, 
228         int do_cursor) // the scale of the cursor if nonzero
229 {
230         if( !display ) return 1;
231         if( x1 < 0 ) x1 = 0;
232         if( y1 < 0 ) y1 = 0;
233         if( x1 > get_top_w() - w ) x1 = get_top_w() - w;
234         if( y1 > get_top_h() - h ) y1 = get_top_h() - h;
235
236
237 // Read the raw data
238         if( use_shm )
239                 XShmGetImage(display, rootwin, ximage, x1, y1, 0xffffffff);
240         else
241                 XGetSubImage(display, rootwin, x1, y1, w, h, 0xffffffff, ZPixmap, ximage, 0, 0);
242
243         BC_CModels::transfer(frame->get_rows(), row_data,
244                 frame->get_y(), frame->get_u(), frame->get_v(), 0,
245                 0, 0, 0, 0, w, h, 0, 0,
246                 frame->get_w(), frame->get_h(),
247                 bitmap_color_model, frame->get_color_model(),
248                 0, frame->get_w(), w);
249         
250         if( do_cursor ) {
251                 XFixesCursorImage *cursor;
252                 cursor = XFixesGetCursorImage(display);
253                 if( cursor ) {
254 //printf("BC_Capture::capture_frame %d cursor=%p colormodel=%d\n", 
255 // __LINE__, cursor, frame->get_color_model());
256                         int scale = do_cursor;
257                         int cursor_x = cursor->x - x1 - cursor->xhot * scale;
258                         int cursor_y = cursor->y - y1 - cursor->yhot * scale;
259                         int w = frame->get_w();
260                         int h = frame->get_h();
261                         for( int i = 0; i < cursor->height; i++ ) {
262                                 for( int yscale = 0; yscale < scale; yscale++ ) {
263                                         if( cursor_y + i * scale + yscale >= 0 && 
264                                                 cursor_y + i * scale + yscale < h ) {
265                                                 unsigned char *src = (unsigned char*)(cursor->pixels + 
266                                                         i * cursor->width);
267                                                 int dst_y = cursor_y + i * scale + yscale;
268                                                 int dst_x = cursor_x;
269                                                 for( int j = 0; j < cursor->width; j++ ) {
270                                                         for( int xscale = 0; xscale < scale ; xscale++ ) {
271                                                                 if( cursor_x + j * scale + xscale >= 0 && 
272                                                                         cursor_x + j * scale + xscale < w ) {
273                                                                         int a = src[3];
274                                                                         int invert_a = 0xff - a;
275                                                                         int r = src[2];
276                                                                         int g = src[1];
277                                                                         int b = src[0];
278                                                                         switch( frame->get_color_model() ) {
279                                                                         case BC_RGB888: {
280                                                                                 unsigned char *dst = frame->get_rows()[dst_y] +
281                                                                                         dst_x * 3;
282                                                                                 dst[0] = (r * a + dst[0] * invert_a) / 0xff;
283                                                                                 dst[1] = (g * a + dst[1] * invert_a) / 0xff;
284                                                                                 dst[2] = (b * a + dst[2] * invert_a) / 0xff;
285                                                                                 break; }
286
287                                                                         case BC_YUV420P: {
288                                                                                 unsigned char *dst_y_ = frame->get_y() + 
289                                                                                         dst_y * w + dst_x;
290                                                                                 unsigned char *dst_u = frame->get_u() + 
291                                                                                         (dst_y / 2) * (w / 2) + (dst_x / 2);
292                                                                                 unsigned char *dst_v = frame->get_v() + 
293                                                                                         (dst_y / 2) * (w / 2) + (dst_x / 2);
294                                                                                 int y, u, v;
295                                                                                 RGB_TO_YUV(y, u, v, r, g, b);
296                                                                                         
297                                                                                 *dst_y_ = (y * a + *dst_y_ * invert_a) / 0xff;
298                                                                                 *dst_u = (u * a + *dst_u * invert_a) / 0xff;
299                                                                                 *dst_v = (v * a + *dst_v * invert_a) / 0xff;
300                                                                                 break; }
301                                                                         }
302                                                                 }
303                                                                 dst_x++;
304                                                         }
305                                                         src += sizeof(long);
306                                                 }
307                                         }
308                                 }
309                         }
310
311 // This frees cursor->pixels
312                         XFree(cursor);
313                 }
314         }
315
316         return 0;
317 }
318
319 int BC_Capture::get_top_w()
320 {
321         Screen *screen_ptr = XDefaultScreenOfDisplay(display);
322         return WidthOfScreen(screen_ptr);
323 }
324
325 int BC_Capture::get_top_h()
326 {
327         Screen *screen_ptr = XDefaultScreenOfDisplay(display);
328         return HeightOfScreen(screen_ptr);
329 }