fix for X11/direct with jpeg background rendering
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / vicon.C
1 #include "vicon.h"
2
3 #include "bctimer.h"
4 #include "bcwindow.h"
5 #include "bccolors.h"
6 #include "keys.h"
7 #include "mutex.h"
8 #include "condition.h"
9
10 VIcon::
11 VIcon(int w, int h, double rate)
12 {
13         this->w = w;
14         this->h = h;
15         this->frame_rate = rate;
16
17         cycle_start = 0;
18         age = 0;
19         seq_no = 0;
20         in_use = 1;
21         hidden = 0;
22         audio_data = 0;
23         audio_size = 0;
24         playing_audio = 0;
25 }
26
27 VIcon::
28 ~VIcon()
29 {
30         clear_images();
31         delete [] audio_data;
32 }
33
34 void VIcon::
35 add_image(VFrame *frm, int ww, int hh, int vcmdl)
36 {
37         VIFrame *vifrm = new VIFrame(ww, hh, vcmdl);
38         vifrm->vfrm->transfer_from(frm);
39         images.append(vifrm);
40 }
41
42 void VIcon::
43 draw_vframe(VIconThread *vt, BC_WindowBase *wdw, int x, int y)
44 {
45         VFrame *vfrm = frame();
46         if( !vfrm ) return;
47         int sx0 = 0, sx1 = sx0 + vt->vw;
48         int sy0 = 0, sy1 = sy0 + vt->vh;
49         int dx0 = x, dx1 = dx0 + w;
50         int dy0 = y, dy1 = dy0 + h;
51         if( (x=vt->draw_x0-dx0) > 0 ) { sx0 += (x*vt->vw)/w;  dx0 = vt->draw_x0; }
52         if( (x=dx1-vt->draw_x1) > 0 ) { sx1 -= (x*vt->vw)/w;  dx1 = vt->draw_x1; }
53         if( (y=vt->draw_y0-dy0) > 0 ) { sy0 += (y*vt->vh)/h;  dy0 = vt->draw_y0; }
54         if( (y=dy1-vt->draw_y1) > 0 ) { sy1 -= (y*vt->vh)/h;  dy1 = vt->draw_y1; }
55         int sw = sx1 - sx0, sh = sy1 - sy0;
56         int dw = dx1 - dx0, dh = dy1 - dy0;
57         if( dw > 0 && dh > 0 && sw > 0 && sh > 0 )
58                 wdw->draw_vframe(vfrm, dx0,dy0, dw,dh, sx0,sy0, sw,sh);
59 }
60
61
62 int VIconThread::cursor_inside(int x, int y)
63 {
64         if( !viewing ) return 0;
65         int vx = viewing->get_vx();
66         if( x < vx || x >= vx+vw ) return 0;
67         int vy = viewing->get_vy();
68         if( y < vy || y >= vy+vh ) return 0;
69         return 1;
70 }
71
72 void VIconThread::
73 set_drawing_area(int x0, int y0, int x1, int y1)
74 {
75         draw_x0 = x0;  draw_y0 = y0;
76         draw_x1 = x1;  draw_y1 = y1;
77 }
78
79 VIcon *VIconThread::low_vicon()
80 {
81         if( !t_heap.size() ) return 0;
82         VIcon *vip = t_heap[0];
83         remove_vicon(0);
84         return vip;
85 }
86
87 void VIconThread::remove_vicon(int i)
88 {
89         if( t_heap[i] == solo ) solo = 0;
90         int sz = t_heap.size();
91         for( int k; (k=2*(i+1)) < sz; i=k ) {
92                 if( t_heap[k]->age > t_heap[k-1]->age ) --k;
93                 t_heap[i] = t_heap[k];
94         }
95         VIcon *last = t_heap[--sz];
96         t_heap.remove_number(sz);
97         double age = last->age;
98         for( int k; i>0 && age<t_heap[k=(i-1)/2]->age; i=k )
99                 t_heap[i] = t_heap[k];
100         t_heap[i] = last;
101 }
102
103
104 VIconThread::
105 VIconThread(BC_WindowBase *wdw, int vw, int vh, int view_w, int view_h)
106  : Thread(1, 0, 0)
107 {
108         this->wdw = wdw;
109         this->vw = vw;         this->vh = vh;
110         this->view_w = view_w; this->view_h = view_h;
111         this->view_win = 0;    this->vicon = 0;
112         this->viewing = 0;     this->solo = 0;
113         this->draw_x0 = 0;     this->draw_x1 = wdw->get_w();
114         this->draw_y0 = 0;     this->draw_y1 = wdw->get_h();
115         draw_lock = new Condition(0, "VIconThread::draw_lock", 1);
116         timer = new Timer();
117         this->refresh_rate = VICON_RATE;
118         this->draw_flash = 0;
119         this->seq_no = 0;
120         this->now = 0;
121         done = 0;
122         interrupted = -1;
123         stop_age = 0;
124 }
125
126 VIconThread::
127 ~VIconThread()
128 {
129         stop_drawing();
130         done = 1;
131         draw_lock->unlock();
132         if( Thread::running() ) {
133                 Thread::cancel();
134         }
135         Thread::join();
136         t_heap.remove_all_objects();
137         delete timer;
138         delete draw_lock;
139 }
140
141 void VIconThread::
142 start_drawing()
143 {
144         wdw->lock_window("VIconThread::start_drawing");
145         if( view_win )
146                 wdw->set_active_subwindow(view_win);
147         if( interrupted < 0 )
148                 draw_lock->unlock();
149         timer->update();
150         timer->subtract(-stop_age);
151         interrupted = 0;
152         wdw->unlock_window();
153 }
154
155 void VIconThread::
156 stop_drawing()
157 {
158         wdw->lock_window("VIconThread::stop_drawing");
159         close_view_popup();
160         if( !interrupted )
161                 interrupted = 1;
162         stop_age = timer->get_difference();
163         wdw->unlock_window();
164 }
165
166 void VIconThread::
167 stop_viewing()
168 {
169         if( viewing ) {
170                 viewing->stop_audio();
171                 viewing = 0;
172         }
173 }
174
175 int VIconThread::keypress_event(int key)
176 {
177         if( key != ESC ) return 0;
178         close_view_popup();
179         return 1;
180 }
181
182 bool VIconThread::
183 visible(VIcon *vicon, int x, int y)
184 {
185         if( vicon->hidden ) return false;
186         if( y+vicon->h <= draw_y0 ) return false;
187         if( y >= draw_y1 ) return false;
188         if( x+vicon->w <= draw_x0 ) return false;
189         if( x >= draw_x1 ) return false;
190         return true;
191 }
192
193 int ViewPopup::keypress_event()
194 {
195         int key = get_keypress();
196         return vt->keypress_event(key);
197 }
198
199
200 ViewPopup::ViewPopup(VIconThread *vt, int x, int y, int w, int h)
201  : BC_Popup(vt->wdw, x, y, w, h, BLACK)
202 {
203         this->vt = vt;
204 }
205
206 ViewPopup::~ViewPopup()
207 {
208         vt->wdw->set_active_subwindow(0);
209 }
210
211 ViewPopup *VIconThread::new_view_window(ViewPopup *vpopup)
212 {
213         BC_WindowBase *parent = wdw->get_parent();
214         XineramaScreenInfo *info = parent->get_xinerama_info(-1);
215         int cx = info ? info->x_org + info->width/2 : parent->get_root_w(0)/2;
216         int cy = info ? info->y_org + info->height/2 : parent->get_root_h(0)/2;
217         int vx = viewing->get_vx(), rx = 0;
218         int vy = viewing->get_vy(), ry = 0;
219         wdw->get_root_coordinates(vx, vy, &rx, &ry);
220         rx += (rx >= cx ? -view_w+viewing->w/4 : viewing->w-viewing->w/4);
221         ry += (ry >= cy ? -view_h+viewing->h/4 : viewing->h-viewing->h/4);
222         if( vpopup )
223                 vpopup->reposition_window(rx, ry, view_w, view_h);
224         else
225                 vpopup = new ViewPopup(this, rx, ry, view_w, view_h);
226         wdw->set_active_subwindow(vpopup);
227         return vpopup;
228 }
229
230
231 void VIconThread::
232 reset_images()
233 {
234         for( int i=t_heap.size(); --i>=0; ) t_heap[i]->reset();
235         timer->update();
236         img_dirty = win_dirty = 0;
237 }
238
239 void VIconThread::add_vicon(VIcon *vip)
240 {
241         double age = vip->age;
242         int i = t_heap.size();  t_heap.append(vip);
243         for( int k; i>0 && age<t_heap[(k=(i-1)/2)]->age; i=k )
244                 t_heap[i] = t_heap[k];
245         t_heap[i] = vip;
246 }
247
248 int VIconThread::del_vicon(VIcon *vicon)
249 {
250         int i = t_heap.size();
251         while( --i >= 0 && t_heap[i] != vicon );
252         if( i < 0 ) return 0;
253         remove_vicon(i);
254         return 1;
255 }
256
257 void ViewPopup::draw_vframe(VFrame *frame)
258 {
259         if( !frame ) return;
260         BC_WindowBase::draw_vframe(frame, 0,0, get_w(),get_h());
261 }
262
263 void VIconThread::set_view_popup(VIcon *vicon)
264 {
265         if( viewing == vicon && !this->vicon ) return;
266         this->vicon = vicon;
267         if( interrupted ) update_view(vicon ? 1 : 0);
268
269 }
270
271 void VIconThread::close_view_popup()
272 {
273         set_view_popup(0);
274 }
275
276 int VIconThread::
277 update_view(int do_audio)
278 {
279         if( viewing ) viewing->stop_audio();
280         delete view_win;  view_win = 0;
281         VFrame *vfrm;
282         if( (viewing=vicon) != 0 && (vfrm=viewing->frame()) != 0 ) {
283                 view_win = new_view_window(0);
284                 view_win->draw_vframe(vfrm);
285                 view_win->flash(0);
286                 view_win->show_window();
287                 if( do_audio ) vicon->start_audio();
288         }
289         wdw->set_active_subwindow(view_win);
290         return 1;
291 }
292
293 int VIconThread::zoom_scale(int dir)
294 {
295         VFrame *vfrm;
296         if( !viewing || !view_win || !(vfrm=viewing->frame()) ) return 0;
297         int view_h = this->view_h;
298         view_h += dir*view_h/10 + dir;
299         bclamp(view_h, 16,512);
300         this->view_h = view_h;
301         this->view_w = view_h * vw/vh;
302         new_view_window(view_win);
303         view_win->draw_vframe(vfrm);
304         view_win->flash(1);
305         return 1;
306 }
307
308
309 void VIconThread::
310 draw_images()
311 {
312         for( int i=0; i<t_heap.size(); ++i )
313                 draw(t_heap[i]);
314 }
315
316 void VIconThread::
317 flash()
318 {
319         if( !img_dirty && !win_dirty ) return;
320         if( img_dirty ) wdw->flash();
321         if( win_dirty && view_win ) view_win->flash();
322         win_dirty = img_dirty = 0;
323 }
324
325 int VIconThread::
326 draw(VIcon *vicon)
327 {
328         int x = vicon->get_vx(), y = vicon->get_vy();
329         int draw_img = visible(vicon, x, y);
330         int draw_win = view_win && viewing == vicon ? 1 : 0;
331         if( !draw_img && !draw_win ) return 0;
332         VFrame *vfrm = vicon->frame();
333         if( !vfrm ) return 0;
334         if( draw_img ) {
335                 vicon->draw_vframe(this, wdw, x, y);
336                 img_dirty = 1;
337         }
338         if( draw_win ) {
339                 view_win->draw_vframe(vfrm);
340                 win_dirty = 1;
341         }
342         return 1;
343 }
344
345 void VIconThread::hide_vicons(int v)
346 {
347         for( int i=0; i<t_heap.size(); ++i ) {
348                 t_heap[i]->hidden = v;
349                 t_heap[i]->age = 0;
350         }
351 }
352
353 int VIconThread::show_vicon(VIcon *next)
354 {
355         now = timer->get_difference();
356         if( now >= draw_flash ) return 1;
357         draw(next);
358         if( !next->seq_no ) {
359                 next->cycle_start = now;
360                 if( next->playing_audio > 0 )
361                         next->start_audio();
362         }
363         int64_t ref_no = (now - next->cycle_start) / 1000. * refresh_rate;
364         int count = ref_no - next->seq_no;
365         if( count < 1 ) count = 1;
366         ref_no = next->seq_no + count;
367         next->age =  next->cycle_start + 1000. * ref_no / refresh_rate;
368         if( !next->set_seq_no(ref_no) )
369                 next->age = now + 1000.;
370         return 0;
371 }
372
373 void VIconThread::
374 run()
375 {
376         while(!done) {
377                 draw_lock->lock("VIconThread::run 0");
378                 if( done ) break;
379                 wdw->lock_window("BC_WindowBase::run 1");
380                 drawing_started();
381                 reset_images();
382                 draw_flash = 1000 / refresh_rate;
383                 seq_no = 0;  now = 0;
384                 while( !interrupted ) {
385                         if( viewing != vicon )
386                                 update_view(1);
387                         if( !solo ) {
388                                 VIcon *next = low_vicon();
389                                 while( !interrupted && next && next->age < draw_flash ) {
390                                         if( show_vicon(next) ) break;
391                                         add_vicon(next);
392                                         next = low_vicon();
393                                 }
394                                 if( !next ) break;
395                                 add_vicon(next);
396                                 if( draw_flash < now+1 )
397                                         draw_flash = now+1;
398                         }
399                         else
400                                 show_vicon(solo);
401                         wdw->unlock_window();
402                         while( !interrupted ) {
403                                 now = timer->get_difference();
404                                 int64_t ms = draw_flash - now;
405                                 if( ms <= 0 ) break;
406                                 if( ms > 100 ) ms = 100;
407                                 Timer::delay(ms);
408                         }
409                         wdw->lock_window("BC_WindowBase::run 2");
410                         now = timer->get_difference();
411                         int64_t late = now - draw_flash;
412                         if( late < 1000 ) flash();
413                         int64_t ref_no = now / 1000. * refresh_rate;
414                         int64_t count = ref_no - seq_no;
415                         if( count < 1 ) count = 1;
416                         seq_no += count;
417                         draw_flash = seq_no * 1000. / refresh_rate;
418                 }
419                 if( viewing != vicon )
420                         update_view(1);
421                 drawing_stopped();
422                 interrupted = -1;
423                 wdw->unlock_window();
424         }
425 }
426
427
428 void VIcon::init_audio(int audio_size)
429 {
430         this->audio_size = audio_size;
431         audio_data = new uint8_t[audio_size];
432         memset(audio_data, 0, audio_size);
433 }
434
435 void VIcon::dump(const char *dir)
436 {
437         mkdir(dir,0777);
438         for( int i=0; i<images.size(); ++i ) {
439                 VFrame *vfrm = images[i]->vfrm;
440                 if( !vfrm ) continue;
441                 char fn[1024];  sprintf(fn,"%s/img%05d.png",dir,i);
442                 printf("\r%s",fn);
443                 vfrm->write_png(fn);
444         }
445         printf("\n");
446 }
447