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