prevent popup deactivation while button_down
[goodguy/history.git] / cinelerra-5.0 / plugins / motion / motion.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2012 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 "affine.h"
23 #include "bcdisplayinfo.h"
24 #include "clip.h"
25 #include "bchash.h"
26 #include "bcsignals.h"
27 #include "filexml.h"
28 #include "format.inc"
29 #include "keyframe.h"
30 #include "language.h"
31 #include "motion.h"
32 #include "motionscan.h"
33 #include "motionwindow.h"
34 #include "mutex.h"
35 #include "overlayframe.h"
36 #include "rotateframe.h"
37 #include "transportque.h"
38
39
40 #include <errno.h>
41 #include <unistd.h>
42
43 REGISTER_PLUGIN(MotionMain)
44
45
46 #undef DEBUG
47
48 // #ifndef DEBUG
49 // #define DEBUG
50 // #endif
51
52
53
54 MotionConfig::MotionConfig()
55 {
56         global_range_w = 5;
57         global_range_h = 5;
58         rotation_range = 5;
59         rotation_center = 0;
60         block_count = 1;
61         global_block_w = MIN_BLOCK;
62         global_block_h = MIN_BLOCK;
63 //      rotation_block_w = MIN_BLOCK;
64 //      rotation_block_h = MIN_BLOCK;
65         block_x = 50;
66         block_y = 50;
67         global_positions = 256;
68         rotate_positions = 4;
69         magnitude = 100;
70         rotate_magnitude = 90;
71         return_speed = 0;
72         rotate_return_speed = 0;
73         action_type = MotionScan::STABILIZE;
74         global = 1;
75         rotate = 1;
76         addtrackedframeoffset = 0;
77         tracking_type = MotionScan::NO_CALCULATE;
78         draw_vectors = 1;
79         tracking_object = MotionScan::TRACK_SINGLE;
80         track_frame = 0;
81         bottom_is_master = 1;
82         horizontal_only = 0;
83         vertical_only = 0;
84 }
85
86 void MotionConfig::boundaries()
87 {
88         CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
89         CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS);
90         CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION);
91         CLAMP(rotation_center, -MAX_ROTATION, MAX_ROTATION);
92         CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
93         CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
94         CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
95 //      CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
96 //      CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
97 }
98
99 int MotionConfig::equivalent(MotionConfig &that)
100 {
101         return global_range_w == that.global_range_w &&
102                 global_range_h == that.global_range_h &&
103                 rotation_range == that.rotation_range &&
104                 rotation_center == that.rotation_center &&
105                 action_type == that.action_type &&
106                 global == that.global &&
107                 rotate == that.rotate &&
108                 addtrackedframeoffset == that.addtrackedframeoffset &&
109                 draw_vectors == that.draw_vectors &&
110                 block_count == that.block_count &&
111                 global_block_w == that.global_block_w &&
112                 global_block_h == that.global_block_h &&
113 //              rotation_block_w == that.rotation_block_w &&
114 //              rotation_block_h == that.rotation_block_h &&
115                 EQUIV(block_x, that.block_x) &&
116                 EQUIV(block_y, that.block_y) &&
117                 global_positions == that.global_positions &&
118                 rotate_positions == that.rotate_positions &&
119                 magnitude == that.magnitude &&
120                 return_speed == that.return_speed &&
121                 rotate_return_speed == that.rotate_return_speed &&
122                 rotate_magnitude == that.rotate_magnitude &&
123                 tracking_object == that.tracking_object &&
124                 track_frame == that.track_frame &&
125                 bottom_is_master == that.bottom_is_master &&
126                 horizontal_only == that.horizontal_only &&
127                 vertical_only == that.vertical_only;
128 }
129
130 void MotionConfig::copy_from(MotionConfig &that)
131 {
132         global_range_w = that.global_range_w;
133         global_range_h = that.global_range_h;
134         rotation_range = that.rotation_range;
135         rotation_center = that.rotation_center;
136         action_type = that.action_type;
137         global = that.global;
138         rotate = that.rotate;
139         addtrackedframeoffset = that.addtrackedframeoffset;
140         tracking_type = that.tracking_type;
141         draw_vectors = that.draw_vectors;
142         block_count = that.block_count;
143         block_x = that.block_x;
144         block_y = that.block_y;
145         global_positions = that.global_positions;
146         rotate_positions = that.rotate_positions;
147         global_block_w = that.global_block_w;
148         global_block_h = that.global_block_h;
149 //      rotation_block_w = that.rotation_block_w;
150 //      rotation_block_h = that.rotation_block_h;
151         magnitude = that.magnitude;
152         return_speed = that.return_speed;
153         rotate_magnitude = that.rotate_magnitude;
154         rotate_return_speed = that.rotate_return_speed;
155         tracking_object = that.tracking_object;
156         track_frame = that.track_frame;
157         bottom_is_master = that.bottom_is_master;
158         horizontal_only = that.horizontal_only;
159         vertical_only = that.vertical_only;
160 }
161
162 void MotionConfig::interpolate(MotionConfig &prev,
163         MotionConfig &next,
164         int64_t prev_frame,
165         int64_t next_frame,
166         int64_t current_frame)
167 {
168         //double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
169         //double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
170         this->block_x = prev.block_x;
171         this->block_y = prev.block_y;
172         global_range_w = prev.global_range_w;
173         global_range_h = prev.global_range_h;
174         rotation_range = prev.rotation_range;
175         rotation_center = prev.rotation_center;
176         action_type = prev.action_type;
177         global = prev.global;
178         rotate = prev.rotate;
179         addtrackedframeoffset = prev.addtrackedframeoffset;
180         tracking_type = prev.tracking_type;
181         draw_vectors = prev.draw_vectors;
182         block_count = prev.block_count;
183         global_positions = prev.global_positions;
184         rotate_positions = prev.rotate_positions;
185         global_block_w = prev.global_block_w;
186         global_block_h = prev.global_block_h;
187 //      rotation_block_w = prev.rotation_block_w;
188 //      rotation_block_h = prev.rotation_block_h;
189         magnitude = prev.magnitude;
190         return_speed = prev.return_speed;
191         rotate_magnitude = prev.rotate_magnitude;
192         rotate_return_speed = prev.rotate_return_speed;
193         tracking_object = prev.tracking_object;
194         track_frame = prev.track_frame;
195         bottom_is_master = prev.bottom_is_master;
196         horizontal_only = prev.horizontal_only;
197         vertical_only = prev.vertical_only;
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 MotionMain::MotionMain(PluginServer *server)
219  : PluginVClient(server)
220 {
221         engine = 0;
222         rotate_engine = 0;
223         motion_rotate = 0;
224         total_dx = 0;
225         total_dy = 0;
226         total_angle = 0;
227         overlayer = 0;
228         search_area = 0;
229         search_size = 0;
230         temp_frame = 0;
231         previous_frame_number = -1;
232
233         prev_global_ref = 0;
234         current_global_ref = 0;
235         global_target_src = 0;
236         global_target_dst = 0;
237
238         prev_rotate_ref = 0;
239         current_rotate_ref = 0;
240         rotate_target_src = 0;
241         rotate_target_dst = 0;
242 }
243
244 MotionMain::~MotionMain()
245 {
246
247         delete engine;
248         delete overlayer;
249         delete [] search_area;
250         delete temp_frame;
251         delete rotate_engine;
252         delete motion_rotate;
253
254
255         delete prev_global_ref;
256         delete current_global_ref;
257         delete global_target_src;
258         delete global_target_dst;
259
260         delete prev_rotate_ref;
261         delete current_rotate_ref;
262         delete rotate_target_src;
263         delete rotate_target_dst;
264 }
265
266 const char* MotionMain::plugin_title() { return _("Motion"); }
267 int MotionMain::is_realtime() { return 1; }
268 int MotionMain::is_multichannel() { return 1; }
269
270
271 NEW_WINDOW_MACRO(MotionMain, MotionWindow)
272
273 LOAD_CONFIGURATION_MACRO(MotionMain, MotionConfig)
274
275
276
277 void MotionMain::update_gui()
278 {
279         if(thread)
280         {
281                 if(load_configuration())
282                 {
283                         thread->window->lock_window("MotionMain::update_gui");
284
285                         char string[BCTEXTLEN];
286                         sprintf(string, "%d", config.global_positions);
287                         ((MotionWindow*)thread->window)->global_search_positions->set_text(string);
288                         sprintf(string, "%d", config.rotate_positions);
289                         ((MotionWindow*)thread->window)->rotation_search_positions->set_text(string);
290
291                         ((MotionWindow*)thread->window)->global_block_w->update(config.global_block_w);
292                         ((MotionWindow*)thread->window)->global_block_h->update(config.global_block_h);
293 //                      ((MotionWindow*)thread->window)->rotation_block_w->update(config.rotation_block_w);
294 //                      ((MotionWindow*)thread->window)->rotation_block_h->update(config.rotation_block_h);
295                         ((MotionWindow*)thread->window)->block_x->update(config.block_x);
296                         ((MotionWindow*)thread->window)->block_y->update(config.block_y);
297                         ((MotionWindow*)thread->window)->block_x_text->update((float)config.block_x);
298                         ((MotionWindow*)thread->window)->block_y_text->update((float)config.block_y);
299                         ((MotionWindow*)thread->window)->magnitude->update(config.magnitude);
300                         ((MotionWindow*)thread->window)->return_speed->update(config.return_speed);
301                         ((MotionWindow*)thread->window)->rotate_magnitude->update(config.rotate_magnitude);
302                         ((MotionWindow*)thread->window)->rotate_return_speed->update(config.rotate_return_speed);
303                         ((MotionWindow*)thread->window)->rotation_range->update(config.rotation_range);
304                         ((MotionWindow*)thread->window)->rotation_center->update(config.rotation_center);
305
306
307                         ((MotionWindow*)thread->window)->track_single->update(config.tracking_object == MotionScan::TRACK_SINGLE);
308                         ((MotionWindow*)thread->window)->track_frame_number->update(config.track_frame);
309                         ((MotionWindow*)thread->window)->track_previous->update(config.tracking_object == MotionScan::TRACK_PREVIOUS);
310                         ((MotionWindow*)thread->window)->previous_same->update(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK);
311                         if(config.tracking_object != MotionScan::TRACK_SINGLE)
312                                 ((MotionWindow*)thread->window)->track_frame_number->disable();
313                         else
314                                 ((MotionWindow*)thread->window)->track_frame_number->enable();
315
316                         ((MotionWindow*)thread->window)->action_type->set_text(
317                                 ActionType::to_text(config.action_type));
318                         ((MotionWindow*)thread->window)->tracking_type->set_text(
319                                 TrackingType::to_text(config.tracking_type));
320                         ((MotionWindow*)thread->window)->track_direction->set_text(
321                                 TrackDirection::to_text(config.horizontal_only, config.vertical_only));
322                         ((MotionWindow*)thread->window)->master_layer->set_text(
323                                 MasterLayer::to_text(config.bottom_is_master));
324
325
326                         ((MotionWindow*)thread->window)->update_mode();
327                         thread->window->unlock_window();
328                 }
329         }
330 }
331
332
333
334
335 void MotionMain::save_data(KeyFrame *keyframe)
336 {
337         FileXML output;
338
339 // cause data to be stored directly in text
340         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
341         output.tag.set_title("MOTION");
342
343         output.tag.set_property("BLOCK_COUNT", config.block_count);
344         output.tag.set_property("GLOBAL_POSITIONS", config.global_positions);
345         output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
346         output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
347         output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
348 //      output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
349 //      output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
350         output.tag.set_property("BLOCK_X", config.block_x);
351         output.tag.set_property("BLOCK_Y", config.block_y);
352         output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
353         output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h);
354         output.tag.set_property("ROTATION_RANGE", config.rotation_range);
355         output.tag.set_property("ROTATION_CENTER", config.rotation_center);
356         output.tag.set_property("MAGNITUDE", config.magnitude);
357         output.tag.set_property("RETURN_SPEED", config.return_speed);
358         output.tag.set_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
359         output.tag.set_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
360         output.tag.set_property("ACTION_TYPE", config.action_type);
361         output.tag.set_property("GLOBAL", config.global);
362         output.tag.set_property("ROTATE", config.rotate);
363         output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
364         output.tag.set_property("TRACKING_TYPE", config.tracking_type);
365         output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
366         output.tag.set_property("TRACKING_OBJECT", config.tracking_object);
367         output.tag.set_property("TRACK_FRAME", config.track_frame);
368         output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master);
369         output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only);
370         output.tag.set_property("VERTICAL_ONLY", config.vertical_only);
371         output.append_tag();
372         output.tag.set_title("/MOTION");
373         output.append_tag();
374         output.terminate_string();
375 }
376
377 void MotionMain::read_data(KeyFrame *keyframe)
378 {
379         FileXML input;
380
381         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
382
383         int result = 0;
384
385         while(!result)
386         {
387                 result = input.read_tag();
388
389                 if(!result)
390                 {
391                         if(input.tag.title_is("MOTION"))
392                         {
393                                 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
394                                 config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
395                                 config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
396                                 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
397                                 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
398 //                              config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
399 //                              config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
400                                 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
401                                 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
402                                 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
403                                 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
404                                 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
405                                 config.rotation_center = input.tag.get_property("ROTATION_CENTER", config.rotation_center);
406                                 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
407                                 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
408                                 config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
409                                 config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
410                                 config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type);
411                                 config.global = input.tag.get_property("GLOBAL", config.global);
412                                 config.rotate = input.tag.get_property("ROTATE", config.rotate);
413                                 config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
414                                 config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type);
415                                 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
416                                 config.tracking_object = input.tag.get_property("TRACKING_OBJECT", config.tracking_object);
417                                 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
418                                 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
419                                 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
420                                 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
421                         }
422                 }
423         }
424         config.boundaries();
425 }
426
427
428
429
430
431
432
433
434
435 void MotionMain::allocate_temp(int w, int h, int color_model)
436 {
437         if(temp_frame &&
438                 (temp_frame->get_w() != w ||
439                 temp_frame->get_h() != h))
440         {
441                 delete temp_frame;
442                 temp_frame = 0;
443         }
444         if(!temp_frame)
445                 temp_frame = new VFrame(w, h, color_model);
446 }
447
448
449 void MotionMain::process_global()
450 {
451
452         if(!engine) engine = new MotionScan(PluginClient::get_project_smp() + 1,
453                 PluginClient::get_project_smp() + 1);
454
455 // Determine if frames changed
456         engine->scan_frame(current_global_ref,
457                 prev_global_ref,
458                 config.global_range_w,
459                 config.global_range_h,
460                 config.global_block_w,
461                 config.global_block_h,
462                 config.block_x,
463                 config.block_y,
464                 config.tracking_object,
465                 config.tracking_type,
466                 config.action_type,
467                 config.horizontal_only,
468                 config.vertical_only,
469                 get_source_position(),
470                 config.global_positions,
471                 total_dx,
472                 total_dy,
473                 0,
474                 0);
475         current_dx = engine->dx_result;
476         current_dy = engine->dy_result;
477
478 // Add current motion vector to accumulation vector.
479         if(config.tracking_object != MotionScan::TRACK_SINGLE)
480         {
481 // Retract over time
482                 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
483                 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
484                 total_dx += engine->dx_result;
485                 total_dy += engine->dy_result;
486 // printf("MotionMain::process_global total_dx=%d engine->dx_result=%d\n",
487 // total_dx,
488 // engine->dx_result);
489         }
490         else
491 // Make accumulation vector current
492         {
493                 total_dx = engine->dx_result;
494                 total_dy = engine->dy_result;
495         }
496
497 // Clamp accumulation vector
498         if(config.magnitude < 100)
499         {
500                 //int block_w = (int64_t)config.global_block_w *
501                 //              current_global_ref->get_w() / 100;
502                 //int block_h = (int64_t)config.global_block_h *
503                 //              current_global_ref->get_h() / 100;
504                 int block_x_orig = (int64_t)(config.block_x *
505                         current_global_ref->get_w() /
506                         100);
507                 int block_y_orig = (int64_t)(config.block_y *
508                         current_global_ref->get_h() /
509                         100);
510
511                 int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
512                         OVERSAMPLE *
513                         config.magnitude /
514                         100;
515                 int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
516                         OVERSAMPLE *
517                         config.magnitude /
518                         100;
519                 int min_block_x = (int64_t)-block_x_orig *
520                         OVERSAMPLE *
521                         config.magnitude /
522                         100;
523                 int min_block_y = (int64_t)-block_y_orig *
524                         OVERSAMPLE *
525                         config.magnitude /
526                         100;
527
528                 CLAMP(total_dx, min_block_x, max_block_x);
529                 CLAMP(total_dy, min_block_y, max_block_y);
530         }
531
532 #ifdef DEBUG
533 printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
534 (float)total_dx / OVERSAMPLE,
535 (float)total_dy / OVERSAMPLE);
536 #endif
537
538         if(config.tracking_object != MotionScan::TRACK_SINGLE && !config.rotate)
539         {
540 // Transfer current reference frame to previous reference frame and update
541 // counter.  Must wait for rotate to compare.
542                 prev_global_ref->copy_from(current_global_ref);
543                 previous_frame_number = get_source_position();
544         }
545
546 // Decide what to do with target based on requested operation
547         int interpolation = NEAREST_NEIGHBOR;
548         float dx = 0.;
549         float dy = 0.;
550         switch(config.action_type)
551         {
552                 case MotionScan::NOTHING:
553                         global_target_dst->copy_from(global_target_src);
554                         break;
555                 case MotionScan::TRACK_PIXEL:
556                         interpolation = NEAREST_NEIGHBOR;
557                         dx = (int)(total_dx / OVERSAMPLE);
558                         dy = (int)(total_dy / OVERSAMPLE);
559                         break;
560                 case MotionScan::STABILIZE_PIXEL:
561                         interpolation = NEAREST_NEIGHBOR;
562                         dx = -(int)(total_dx / OVERSAMPLE);
563                         dy = -(int)(total_dy / OVERSAMPLE);
564                         break;
565                         break;
566                 case MotionScan::TRACK:
567                         interpolation = CUBIC_LINEAR;
568                         dx = (float)total_dx / OVERSAMPLE;
569                         dy = (float)total_dy / OVERSAMPLE;
570                         break;
571                 case MotionScan::STABILIZE:
572                         interpolation = CUBIC_LINEAR;
573                         dx = -(float)total_dx / OVERSAMPLE;
574                         dy = -(float)total_dy / OVERSAMPLE;
575                         break;
576         }
577
578
579         if(config.action_type != MotionScan::NOTHING)
580         {
581                 if(!overlayer)
582                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
583                 global_target_dst->clear_frame();
584                 overlayer->overlay(global_target_dst,
585                         global_target_src,
586                         0,
587                         0,
588                         global_target_src->get_w(),
589                         global_target_src->get_h(),
590                         dx,
591                         dy,
592                         (float)global_target_src->get_w() + dx,
593                         (float)global_target_src->get_h() + dy,
594                         1,
595                         TRANSFER_REPLACE,
596                         interpolation);
597         }
598 }
599
600
601
602 void MotionMain::process_rotation()
603 {
604         int block_x;
605         int block_y;
606
607 // Convert the previous global reference into the previous rotation reference.
608 // Convert global target destination into rotation target source.
609         if(config.global)
610         {
611                 if(!overlayer)
612                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
613                 float dx;
614                 float dy;
615                 if(config.tracking_object == MotionScan::TRACK_SINGLE)
616                 {
617                         dx = (float)total_dx / OVERSAMPLE;
618                         dy = (float)total_dy / OVERSAMPLE;
619                 }
620                 else
621                 {
622                         dx = (float)current_dx / OVERSAMPLE;
623                         dy = (float)current_dy / OVERSAMPLE;
624                 }
625
626                 prev_rotate_ref->clear_frame();
627                 overlayer->overlay(prev_rotate_ref,
628                         prev_global_ref,
629                         0,
630                         0,
631                         prev_global_ref->get_w(),
632                         prev_global_ref->get_h(),
633                         dx,
634                         dy,
635                         (float)prev_global_ref->get_w() + dx,
636                         (float)prev_global_ref->get_h() + dy,
637                         1,
638                         TRANSFER_REPLACE,
639                         CUBIC_LINEAR);
640 // Pivot is destination global position
641                 block_x = (int)(prev_rotate_ref->get_w() *
642                         config.block_x /
643                         100 +
644                         (float)total_dx /
645                         OVERSAMPLE);
646                 block_y = (int)(prev_rotate_ref->get_h() *
647                         config.block_y /
648                         100 +
649                         (float)total_dy /
650                         OVERSAMPLE);
651 // Use the global target output as the rotation target input
652                 rotate_target_src->copy_from(global_target_dst);
653 // Transfer current reference frame to previous reference frame for global.
654                 if(config.tracking_object != MotionScan::TRACK_SINGLE)
655                 {
656                         prev_global_ref->copy_from(current_global_ref);
657                         previous_frame_number = get_source_position();
658                 }
659         }
660         else
661         {
662 // Pivot is fixed
663                 block_x = (int)(prev_rotate_ref->get_w() *
664                         config.block_x /
665                         100);
666                 block_y = (int)(prev_rotate_ref->get_h() *
667                         config.block_y /
668                         100);
669         }
670
671
672
673 // Get rotation
674         if(!motion_rotate)
675                 motion_rotate = new RotateScan(this,
676                         get_project_smp() + 1,
677                         get_project_smp() + 1);
678
679         current_angle = motion_rotate->scan_frame(prev_rotate_ref,
680                 current_rotate_ref,
681                 block_x,
682                 block_y);
683
684
685
686 // Add current rotation to accumulation
687         if(config.tracking_object != MotionScan::TRACK_SINGLE)
688         {
689 // Retract over time
690                 total_angle = total_angle * (100 - config.rotate_return_speed) / 100;
691 // Accumulate current rotation
692                 total_angle += current_angle;
693
694 // Clamp rotation accumulation
695                 if(config.rotate_magnitude < 90)
696                 {
697                         CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude);
698                 }
699
700                 if(!config.global)
701                 {
702 // Transfer current reference frame to previous reference frame and update
703 // counter.
704                         prev_rotate_ref->copy_from(current_rotate_ref);
705                         previous_frame_number = get_source_position();
706                 }
707         }
708         else
709         {
710                 total_angle = current_angle;
711         }
712
713 #ifdef DEBUG
714 printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
715 #endif
716
717
718 // Calculate rotation parameters based on requested operation
719         float angle = 0.;
720         switch(config.action_type)
721         {
722                 case MotionScan::NOTHING:
723                         rotate_target_dst->copy_from(rotate_target_src);
724                         break;
725                 case MotionScan::TRACK:
726                 case MotionScan::TRACK_PIXEL:
727                         angle = total_angle;
728                         break;
729                 case MotionScan::STABILIZE:
730                 case MotionScan::STABILIZE_PIXEL:
731                         angle = -total_angle;
732                         break;
733         }
734
735
736
737         if(config.action_type != MotionScan::NOTHING)
738         {
739                 if(!rotate_engine)
740                         rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
741                                 PluginClient::get_project_smp() + 1);
742
743                 rotate_target_dst->clear_frame();
744
745 // Determine pivot based on a number of factors.
746                 switch(config.action_type)
747                 {
748                         case MotionScan::TRACK:
749                         case MotionScan::TRACK_PIXEL:
750 // Use destination of global tracking.
751 //                              rotate_engine->set_pivot(block_x, block_y);
752                                 rotate_engine->set_in_pivot(block_x, block_y);
753                                 rotate_engine->set_out_pivot(block_x, block_y);
754                                 break;
755
756                         case MotionScan::STABILIZE:
757                         case MotionScan::STABILIZE_PIXEL:
758                                 if(config.global)
759                                 {
760 // Use origin of global stabilize operation
761 //                                      rotate_engine->set_pivot((int)(rotate_target_dst->get_w() *
762 //                                                      config.block_x /
763 //                                                      100),
764 //                                              (int)(rotate_target_dst->get_h() *
765 //                                                      config.block_y /
766 //                                                      100));
767                                         rotate_engine->set_in_pivot((int)(rotate_target_dst->get_w() *
768                                                         config.block_x /
769                                                         100),
770                                                 (int)(rotate_target_dst->get_h() *
771                                                         config.block_y /
772                                                         100));
773                                         rotate_engine->set_out_pivot((int)(rotate_target_dst->get_w() *
774                                                         config.block_x /
775                                                         100),
776                                                 (int)(rotate_target_dst->get_h() *
777                                                         config.block_y /
778                                                         100));
779
780                                 }
781                                 else
782                                 {
783 // Use origin
784 //                                      rotate_engine->set_pivot(block_x, block_y);
785                                         rotate_engine->set_in_pivot(block_x, block_y);
786                                         rotate_engine->set_out_pivot(block_x, block_y);
787                                 }
788                                 break;
789                 }
790
791
792                 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
793 // overlayer->overlay(rotate_target_dst,
794 //      prev_rotate_ref,
795 //      0,
796 //      0,
797 //      prev_rotate_ref->get_w(),
798 //      prev_rotate_ref->get_h(),
799 //      0,
800 //      0,
801 //      prev_rotate_ref->get_w(),
802 //      prev_rotate_ref->get_h(),
803 //      1,
804 //      TRANSFER_NORMAL,
805 //      CUBIC_LINEAR);
806 // overlayer->overlay(rotate_target_dst,
807 //      current_rotate_ref,
808 //      0,
809 //      0,
810 //      prev_rotate_ref->get_w(),
811 //      prev_rotate_ref->get_h(),
812 //      0,
813 //      0,
814 //      prev_rotate_ref->get_w(),
815 //      prev_rotate_ref->get_h(),
816 //      1,
817 //      TRANSFER_NORMAL,
818 //      CUBIC_LINEAR);
819
820
821         }
822
823
824 }
825
826
827
828
829
830
831
832
833
834 int MotionMain::process_buffer(VFrame **frame,
835         int64_t start_position,
836         double frame_rate)
837 {
838         int need_reconfigure = load_configuration();
839         int color_model = frame[0]->get_color_model();
840         w = frame[0]->get_w();
841         h = frame[0]->get_h();
842
843
844 #ifdef DEBUG
845 printf("MotionMain::process_buffer %d start_position=%lld\n", __LINE__, start_position);
846 #endif
847
848
849 // Calculate the source and destination pointers for each of the operations.
850 // Get the layer to track motion in.
851         reference_layer = config.bottom_is_master ?
852                 PluginClient::total_in_buffers - 1 :
853                 0;
854 // Get the layer to apply motion in.
855         target_layer = config.bottom_is_master ?
856                 0 :
857                 PluginClient::total_in_buffers - 1;
858
859
860         output_frame = frame[target_layer];
861
862
863 // Get the position of previous reference frame.
864         int64_t actual_previous_number;
865 // Skip if match frame not available
866         int skip_current = 0;
867
868
869         if(config.tracking_object == MotionScan::TRACK_SINGLE)
870         {
871                 actual_previous_number = config.track_frame;
872                 if(get_direction() == PLAY_REVERSE)
873                         actual_previous_number++;
874                 if(actual_previous_number == start_position)
875                         skip_current = 1;
876         }
877         else
878         {
879                 actual_previous_number = start_position;
880                 if(get_direction() == PLAY_FORWARD)
881                 {
882                         actual_previous_number--;
883                         if(actual_previous_number < get_source_start())
884                                 skip_current = 1;
885                         else
886                         {
887                                 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
888                                 if(keyframe->position > 0 &&
889                                         actual_previous_number < keyframe->position)
890                                         skip_current = 1;
891                         }
892                 }
893                 else
894                 {
895                         actual_previous_number++;
896                         if(actual_previous_number >= get_source_start() + get_total_len())
897                                 skip_current = 1;
898                         else
899                         {
900                                 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
901                                 if(keyframe->position > 0 &&
902                                         actual_previous_number >= keyframe->position)
903                                         skip_current = 1;
904                         }
905                 }
906
907 // Only count motion since last keyframe
908
909
910         }
911
912
913         if(!config.global && !config.rotate) skip_current = 1;
914
915
916
917
918 // printf("process_realtime %d %lld %lld\n",
919 // skip_current,
920 // previous_frame_number,
921 // actual_previous_number);
922 // Load match frame and reset vectors
923         int need_reload = !skip_current &&
924                 (previous_frame_number != actual_previous_number ||
925                 need_reconfigure);
926         if(need_reload)
927         {
928                 total_dx = 0;
929                 total_dy = 0;
930                 total_angle = 0;
931                 previous_frame_number = actual_previous_number;
932         }
933
934
935         if(skip_current)
936         {
937                 total_dx = 0;
938                 total_dy = 0;
939                 current_dx = 0;
940                 current_dy = 0;
941                 total_angle = 0;
942                 current_angle = 0;
943         }
944
945
946
947
948 // Get the global pointers.  Here we walk through the sequence of events.
949         if(config.global)
950         {
951 // Assume global only.  Global reads previous frame and compares
952 // with current frame to get the current translation.
953 // The center of the search area is fixed in compensate mode or
954 // the user value + the accumulation vector in track mode.
955                 if(!prev_global_ref)
956                         prev_global_ref = new VFrame(w, h, color_model);
957                 if(!current_global_ref)
958                         current_global_ref = new VFrame(w, h, color_model);
959
960 // Global loads the current target frame into the src and
961 // writes it to the dst frame with desired translation.
962                 if(!global_target_src)
963                         global_target_src = new VFrame(w, h, color_model);
964                 if(!global_target_dst)
965                         global_target_dst = new VFrame(w, h, color_model);
966
967
968 // Load the global frames
969                 if(need_reload)
970                 {
971                         read_frame(prev_global_ref,
972                                 reference_layer,
973                                 previous_frame_number,
974                                 frame_rate,
975                                 0);
976                 }
977
978                 read_frame(current_global_ref,
979                         reference_layer,
980                         start_position,
981                         frame_rate,
982                         0);
983                 read_frame(global_target_src,
984                         target_layer,
985                         start_position,
986                         frame_rate,
987                         0);
988
989
990
991 // Global followed by rotate
992                 if(config.rotate)
993                 {
994 // Must translate the previous global reference by the current global
995 // accumulation vector to match the current global reference.
996 // The center of the search area is always the user value + the accumulation
997 // vector.
998                         if(!prev_rotate_ref)
999                                 prev_rotate_ref = new VFrame(w, h, color_model);
1000 // The current global reference is the current rotation reference.
1001                         if(!current_rotate_ref)
1002                                 current_rotate_ref = new VFrame(w, h, color_model);
1003                         current_rotate_ref->copy_from(current_global_ref);
1004
1005 // The global target destination is copied to the rotation target source
1006 // then written to the rotation output with rotation.
1007 // The pivot for the rotation is the center of the search area
1008 // if we're tracking.
1009 // The pivot is fixed to the user position if we're compensating.
1010                         if(!rotate_target_src)
1011                                 rotate_target_src = new VFrame(w, h, color_model);
1012                         if(!rotate_target_dst)
1013                                 rotate_target_dst = new VFrame(w, h, color_model);
1014                 }
1015         }
1016         else
1017 // Rotation only
1018         if(config.rotate)
1019         {
1020 // Rotation reads the previous reference frame and compares it with current
1021 // reference frame.
1022                 if(!prev_rotate_ref)
1023                         prev_rotate_ref = new VFrame(w, h, color_model);
1024                 if(!current_rotate_ref)
1025                         current_rotate_ref = new VFrame(w, h, color_model);
1026
1027 // Rotation loads target frame to temporary, rotates it, and writes it to the
1028 // target frame.  The pivot is always fixed.
1029                 if(!rotate_target_src)
1030                         rotate_target_src = new VFrame(w, h, color_model);
1031                 if(!rotate_target_dst)
1032                         rotate_target_dst = new VFrame(w, h, color_model);
1033
1034
1035 // Load the rotate frames
1036                 if(need_reload)
1037                 {
1038                         read_frame(prev_rotate_ref,
1039                                 reference_layer,
1040                                 previous_frame_number,
1041                                 frame_rate,
1042                                 0);
1043                 }
1044                 read_frame(current_rotate_ref,
1045                         reference_layer,
1046                         start_position,
1047                         frame_rate,
1048                         0);
1049                 read_frame(rotate_target_src,
1050                         target_layer,
1051                         start_position,
1052                         frame_rate,
1053                         0);
1054         }
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065         if(!skip_current)
1066         {
1067 // Get position change from previous frame to current frame
1068                 if(config.global) process_global();
1069 // Get rotation change from previous frame to current frame
1070                 if(config.rotate) process_rotation();
1071 //frame[target_layer]->copy_from(prev_rotate_ref);
1072 //frame[target_layer]->copy_from(current_rotate_ref);
1073         }
1074
1075
1076
1077
1078
1079
1080 // Transfer the relevant target frame to the output
1081         if(!skip_current)
1082         {
1083                 if(config.rotate)
1084                 {
1085                         frame[target_layer]->copy_from(rotate_target_dst);
1086                 }
1087                 else
1088                 {
1089                         frame[target_layer]->copy_from(global_target_dst);
1090                 }
1091         }
1092         else
1093 // Read the target destination directly
1094         {
1095                 read_frame(frame[target_layer],
1096                         target_layer,
1097                         start_position,
1098                         frame_rate,
1099                         0);
1100         }
1101
1102         if(config.draw_vectors)
1103         {
1104                 draw_vectors(frame[target_layer]);
1105         }
1106
1107 #ifdef DEBUG
1108 printf("MotionMain::process_buffer %d\n", __LINE__);
1109 #endif
1110         return 0;
1111 }
1112
1113
1114
1115 void MotionMain::draw_vectors(VFrame *frame)
1116 {
1117         int w = frame->get_w();
1118         int h = frame->get_h();
1119         int global_x1, global_y1;
1120         int global_x2, global_y2;
1121         int block_x, block_y;
1122         int block_w, block_h;
1123         int block_x1, block_y1;
1124         int block_x2, block_y2;
1125         int block_x3, block_y3;
1126         int block_x4, block_y4;
1127         int search_w, search_h;
1128         int search_x1, search_y1;
1129         int search_x2, search_y2;
1130
1131
1132         if(config.global)
1133         {
1134 // Get vector
1135 // Start of vector is center of previous block.
1136 // End of vector is total accumulation.
1137                 if(config.tracking_object == MotionScan::TRACK_SINGLE)
1138                 {
1139                         global_x1 = (int64_t)(config.block_x *
1140                                 w /
1141                                 100);
1142                         global_y1 = (int64_t)(config.block_y *
1143                                 h /
1144                                 100);
1145                         global_x2 = global_x1 + total_dx / OVERSAMPLE;
1146                         global_y2 = global_y1 + total_dy / OVERSAMPLE;
1147 //printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1148                 }
1149                 else
1150 // Start of vector is center of previous block.
1151 // End of vector is current change.
1152                 if(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK)
1153                 {
1154                         global_x1 = (int64_t)(config.block_x *
1155                                 w /
1156                                 100);
1157                         global_y1 = (int64_t)(config.block_y *
1158                                 h /
1159                                 100);
1160                         global_x2 = global_x1 + current_dx / OVERSAMPLE;
1161                         global_y2 = global_y1 + current_dy / OVERSAMPLE;
1162                 }
1163                 else
1164                 {
1165                         global_x1 = (int64_t)(config.block_x *
1166                                 w /
1167                                 100 +
1168                                 (total_dx - current_dx) /
1169                                 OVERSAMPLE);
1170                         global_y1 = (int64_t)(config.block_y *
1171                                 h /
1172                                 100 +
1173                                 (total_dy - current_dy) /
1174                                 OVERSAMPLE);
1175                         global_x2 = (int64_t)(config.block_x *
1176                                 w /
1177                                 100 +
1178                                 total_dx /
1179                                 OVERSAMPLE);
1180                         global_y2 = (int64_t)(config.block_y *
1181                                 h /
1182                                 100 +
1183                                 total_dy /
1184                                 OVERSAMPLE);
1185                 }
1186
1187                 block_x = global_x1;
1188                 block_y = global_y1;
1189                 block_w = config.global_block_w * w / 100;
1190                 block_h = config.global_block_h * h / 100;
1191                 block_x1 = block_x - block_w / 2;
1192                 block_y1 = block_y - block_h / 2;
1193                 block_x2 = block_x + block_w / 2;
1194                 block_y2 = block_y + block_h / 2;
1195                 search_w = config.global_range_w * w / 100;
1196                 search_h = config.global_range_h * h / 100;
1197                 search_x1 = block_x1 - search_w / 2;
1198                 search_y1 = block_y1 - search_h / 2;
1199                 search_x2 = block_x2 + search_w / 2;
1200                 search_y2 = block_y2 + search_h / 2;
1201
1202 // printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1203 // global_x1,
1204 // global_y1,
1205 // block_w,
1206 // block_h,
1207 // block_x1,
1208 // block_y1,
1209 // block_x2,
1210 // block_y2,
1211 // search_x1,
1212 // search_y1,
1213 // search_x2,
1214 // search_y2);
1215
1216                 MotionScan::clamp_scan(w,
1217                         h,
1218                         &block_x1,
1219                         &block_y1,
1220                         &block_x2,
1221                         &block_y2,
1222                         &search_x1,
1223                         &search_y1,
1224                         &search_x2,
1225                         &search_y2,
1226                         1);
1227
1228 // Vector
1229                 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1230
1231 // Macroblock
1232                 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1233                 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1234                 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1235                 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1236
1237
1238 // Search area
1239                 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1240                 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1241                 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1242                 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1243
1244 // Block should be endpoint of motion
1245                 if(config.rotate)
1246                 {
1247                         block_x = global_x2;
1248                         block_y = global_y2;
1249                 }
1250         }
1251         else
1252         {
1253                 block_x = (int64_t)(config.block_x * w / 100);
1254                 block_y = (int64_t)(config.block_y * h / 100);
1255         }
1256
1257         block_w = config.global_block_w * w / 100;
1258         block_h = config.global_block_h * h / 100;
1259         if(config.rotate)
1260         {
1261                 float angle = total_angle * 2 * M_PI / 360;
1262                 double base_angle1 = atan((float)block_h / block_w);
1263                 double base_angle2 = atan((float)block_w / block_h);
1264                 double target_angle1 = base_angle1 + angle;
1265                 double target_angle2 = base_angle2 + angle;
1266                 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1267                 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1268                 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1269                 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1270                 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1271                 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1272                 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1273                 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1274                 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1275
1276                 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1277                 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1278                 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1279                 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1280
1281
1282 // Center
1283                 if(!config.global)
1284                 {
1285                         draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1286                         draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1287                 }
1288         }
1289 }
1290
1291
1292
1293 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
1294 {
1295         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1296
1297 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1298 { \
1299         type **rows = (type**)frame->get_rows(); \
1300         rows[y][x * components] = max - rows[y][x * components]; \
1301         if(!do_yuv) \
1302         { \
1303                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1304                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1305         } \
1306         else \
1307         { \
1308                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1309                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1310         } \
1311         if(components == 4) \
1312                 rows[y][x * components + 3] = max; \
1313 }
1314
1315
1316         switch(frame->get_color_model())
1317         {
1318                 case BC_RGB888:
1319                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1320                         break;
1321                 case BC_RGBA8888:
1322                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1323                         break;
1324                 case BC_RGB_FLOAT:
1325                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1326                         break;
1327                 case BC_RGBA_FLOAT:
1328                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1329                         break;
1330                 case BC_YUV888:
1331                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1332                         break;
1333                 case BC_YUVA8888:
1334                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1335                         break;
1336                 case BC_RGB161616:
1337                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1338                         break;
1339                 case BC_YUV161616:
1340                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1341                         break;
1342                 case BC_RGBA16161616:
1343                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1344                         break;
1345                 case BC_YUVA16161616:
1346                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1347                         break;
1348         }
1349 }
1350
1351
1352 void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1353 {
1354         int w = labs(x2 - x1);
1355         int h = labs(y2 - y1);
1356 //printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1357
1358         if(!w && !h)
1359         {
1360                 draw_pixel(frame, x1, y1);
1361         }
1362         else
1363         if(w > h)
1364         {
1365 // Flip coordinates so x1 < x2
1366                 if(x2 < x1)
1367                 {
1368                         y2 ^= y1;
1369                         y1 ^= y2;
1370                         y2 ^= y1;
1371                         x1 ^= x2;
1372                         x2 ^= x1;
1373                         x1 ^= x2;
1374                 }
1375                 int numerator = y2 - y1;
1376                 int denominator = x2 - x1;
1377                 for(int i = x1; i < x2; i++)
1378                 {
1379                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1380                         draw_pixel(frame, i, y);
1381                 }
1382         }
1383         else
1384         {
1385 // Flip coordinates so y1 < y2
1386                 if(y2 < y1)
1387                 {
1388                         y2 ^= y1;
1389                         y1 ^= y2;
1390                         y2 ^= y1;
1391                         x1 ^= x2;
1392                         x2 ^= x1;
1393                         x1 ^= x2;
1394                 }
1395                 int numerator = x2 - x1;
1396                 int denominator = y2 - y1;
1397                 for(int i = y1; i < y2; i++)
1398                 {
1399                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1400                         draw_pixel(frame, x, i);
1401                 }
1402         }
1403 //printf("MotionMain::draw_line 2\n");
1404 }
1405
1406 #define ARROW_SIZE 10
1407 void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1408 {
1409         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1410         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1411         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1412         int x3;
1413         int y3;
1414         int x4;
1415         int y4;
1416         if(x2 < x1)
1417         {
1418                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1419                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1420                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1421                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1422         }
1423         else
1424         {
1425                 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1426                 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1427                 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1428                 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1429         }
1430
1431 // Main vector
1432         draw_line(frame, x1, y1, x2, y2);
1433 //      draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1434
1435 // Arrow line
1436         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1437 //      draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1438 // Arrow line
1439         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1440 //      draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1441 }
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458 RotateScanPackage::RotateScanPackage()
1459 {
1460 }
1461
1462
1463 RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin)
1464  : LoadClient(server)
1465 {
1466         this->server = server;
1467         this->plugin = plugin;
1468         rotater = 0;
1469         temp = 0;
1470 }
1471
1472 RotateScanUnit::~RotateScanUnit()
1473 {
1474         delete rotater;
1475         delete temp;
1476 }
1477
1478 void RotateScanUnit::process_package(LoadPackage *package)
1479 {
1480         if(server->skip) return;
1481         RotateScanPackage *pkg = (RotateScanPackage*)package;
1482
1483         if((pkg->difference = server->get_cache(pkg->angle)) < 0)
1484         {
1485 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1486                 int color_model = server->previous_frame->get_color_model();
1487                 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
1488                 int row_bytes = server->previous_frame->get_bytes_per_line();
1489
1490                 if(!rotater)
1491                         rotater = new AffineEngine(1, 1);
1492                 if(!temp) temp = new VFrame(0,
1493                         -1,
1494                         server->previous_frame->get_w(),
1495                         server->previous_frame->get_h(),
1496                         color_model,
1497                         -1);
1498 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1499
1500
1501 // Rotate original block size
1502 //              rotater->set_viewport(server->block_x1,
1503 //                      server->block_y1,
1504 //                      server->block_x2 - server->block_x1,
1505 //                      server->block_y2 - server->block_y1);
1506                 rotater->set_in_viewport(server->block_x1,
1507                         server->block_y1,
1508                         server->block_x2 - server->block_x1,
1509                         server->block_y2 - server->block_y1);
1510                 rotater->set_out_viewport(server->block_x1,
1511                         server->block_y1,
1512                         server->block_x2 - server->block_x1,
1513                         server->block_y2 - server->block_y1);
1514 //              rotater->set_pivot(server->block_x, server->block_y);
1515                 rotater->set_in_pivot(server->block_x, server->block_y);
1516                 rotater->set_out_pivot(server->block_x, server->block_y);
1517 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1518                 rotater->rotate(temp,
1519                         server->previous_frame,
1520                         pkg->angle);
1521
1522 // Scan reduced block size
1523 //plugin->output_frame->copy_from(server->current_frame);
1524 //plugin->output_frame->copy_from(temp);
1525 // printf("RotateScanUnit::process_package %d %d %d %d %d\n",
1526 // __LINE__,
1527 // server->scan_x,
1528 // server->scan_y,
1529 // server->scan_w,
1530 // server->scan_h);
1531 // Clamp coordinates
1532                 int x1 = server->scan_x;
1533                 int y1 = server->scan_y;
1534                 int x2 = x1 + server->scan_w;
1535                 int y2 = y1 + server->scan_h;
1536                 x2 = MIN(temp->get_w(), x2);
1537                 y2 = MIN(temp->get_h(), y2);
1538                 x2 = MIN(server->current_frame->get_w(), x2);
1539                 y2 = MIN(server->current_frame->get_h(), y2);
1540                 x1 = MAX(0, x1);
1541                 y1 = MAX(0, y1);
1542
1543                 if(x2 > x1 && y2 > y1)
1544                 {
1545                         pkg->difference = MotionScan::abs_diff(
1546                                 temp->get_rows()[y1] + x1 * pixel_size,
1547                                 server->current_frame->get_rows()[y1] + x1 * pixel_size,
1548                                 row_bytes,
1549                                 x2 - x1,
1550                                 y2 - y1,
1551                                 color_model);
1552 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1553                         server->put_cache(pkg->angle, pkg->difference);
1554                 }
1555
1556 // printf("RotateScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n",
1557 // server->block_x1,
1558 // server->block_y1,
1559 // server->block_x2 - server->block_x1,
1560 // server->block_y2 - server->block_y1,
1561 // server->block_x,
1562 // server->block_y,
1563 // pkg->angle,
1564 // server->scan_w,
1565 // server->scan_h,
1566 // pkg->difference);
1567         }
1568 }
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591 RotateScan::RotateScan(MotionMain *plugin,
1592         int total_clients,
1593         int total_packages)
1594  : LoadServer(
1595 //1, 1
1596 total_clients, total_packages
1597 )
1598 {
1599         this->plugin = plugin;
1600         cache_lock = new Mutex("RotateScan::cache_lock");
1601 }
1602
1603
1604 RotateScan::~RotateScan()
1605 {
1606         delete cache_lock;
1607 }
1608
1609 void RotateScan::init_packages()
1610 {
1611         for(int i = 0; i < get_total_packages(); i++)
1612         {
1613                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
1614                 pkg->angle = i *
1615                         (scan_angle2 - scan_angle1) /
1616                         (total_steps - 1) +
1617                         scan_angle1;
1618         }
1619 }
1620
1621 LoadClient* RotateScan::new_client()
1622 {
1623         return new RotateScanUnit(this, plugin);
1624 }
1625
1626 LoadPackage* RotateScan::new_package()
1627 {
1628         return new RotateScanPackage;
1629 }
1630
1631
1632 float RotateScan::scan_frame(VFrame *previous_frame,
1633         VFrame *current_frame,
1634         int block_x,
1635         int block_y)
1636 {
1637         skip = 0;
1638         this->block_x = block_x;
1639         this->block_y = block_y;
1640
1641 //printf("RotateScan::scan_frame %d\n", __LINE__);
1642         switch(plugin->config.tracking_type)
1643         {
1644                 case MotionScan::NO_CALCULATE:
1645                         result = plugin->config.rotation_center;
1646                         skip = 1;
1647                         break;
1648
1649                 case MotionScan::LOAD:
1650                 {
1651                         char string[BCTEXTLEN];
1652                         sprintf(string, "%s%06jd",
1653                                  ROTATION_FILE, plugin->get_source_position());
1654                         FILE *input = fopen(string, "r");
1655                         if(input)
1656                         {
1657                                 fscanf(input, "%f", &result);
1658                                 fclose(input);
1659                                 skip = 1;
1660                         }
1661                         else
1662                         {
1663                                 perror("RotateScan::scan_frame LOAD");
1664                         }
1665                         break;
1666                 }
1667         }
1668
1669
1670
1671
1672
1673
1674
1675
1676         this->previous_frame = previous_frame;
1677         this->current_frame = current_frame;
1678         int w = current_frame->get_w();
1679         int h = current_frame->get_h();
1680         int block_w = w * plugin->config.global_block_w / 100;
1681         int block_h = h * plugin->config.global_block_h / 100;
1682
1683         if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
1684         if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
1685         if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
1686         if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
1687
1688         block_x1 = this->block_x - block_w / 2;
1689         block_x2 = this->block_x + block_w / 2;
1690         block_y1 = this->block_y - block_h / 2;
1691         block_y2 = this->block_y + block_h / 2;
1692
1693 // Calculate the maximum area available to scan after rotation.
1694 // Must be calculated from the starting range because of cache.
1695 // Get coords of rectangle after rotation.
1696         double center_x = this->block_x;
1697         double center_y = this->block_y;
1698         double max_angle = plugin->config.rotation_range;
1699         double base_angle1 = atan((float)block_h / block_w);
1700         double base_angle2 = atan((float)block_w / block_h);
1701         double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
1702         double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
1703         double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1704         double x1 = center_x - cos(target_angle1) * radius;
1705         double y1 = center_y - sin(target_angle1) * radius;
1706         double x2 = center_x + sin(target_angle2) * radius;
1707         double y2 = center_y - cos(target_angle2) * radius;
1708         double x3 = center_x - sin(target_angle2) * radius;
1709         double y3 = center_y + cos(target_angle2) * radius;
1710
1711 // Track top edge to find greatest area.
1712         double max_area1 = 0;
1713         //double max_x1 = 0;
1714         double max_y1 = 0;
1715         for(double x = x1; x < x2; x++)
1716         {
1717                 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
1718                 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
1719                 {
1720                         double area = fabs(x - center_x) * fabs(y - center_y);
1721                         if(area > max_area1)
1722                         {
1723                                 max_area1 = area;
1724                                 //max_x1 = x;
1725                                 max_y1 = y;
1726                         }
1727                 }
1728         }
1729
1730 // Track left edge to find greatest area.
1731         double max_area2 = 0;
1732         double max_x2 = 0;
1733         //double max_y2 = 0;
1734         for(double y = y1; y < y3; y++)
1735         {
1736                 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
1737                 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
1738                 {
1739                         double area = fabs(x - center_x) * fabs(y - center_y);
1740                         if(area > max_area2)
1741                         {
1742                                 max_area2 = area;
1743                                 max_x2 = x;
1744                                 //max_y2 = y;
1745                         }
1746                 }
1747         }
1748
1749         double max_x, max_y;
1750         max_x = max_x2;
1751         max_y = max_y1;
1752
1753 // Get reduced scan coords
1754         scan_w = (int)(fabs(max_x - center_x) * 2);
1755         scan_h = (int)(fabs(max_y - center_y) * 2);
1756         scan_x = (int)(center_x - scan_w / 2);
1757         scan_y = (int)(center_y - scan_h / 2);
1758 // printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n",
1759 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
1760 // printf("    angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
1761
1762 // Determine min angle from size of block
1763         double angle1 = atan((double)block_h / block_w);
1764         double angle2 = atan((double)(block_h - 1) / (block_w + 1));
1765         double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
1766         min_angle = MAX(min_angle, MIN_ANGLE);
1767
1768 //printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle * 360 / 2 / M_PI);
1769
1770         cache.remove_all_objects();
1771
1772
1773         if(!skip)
1774         {
1775                 if(previous_frame->data_matches(current_frame))
1776                 {
1777 //printf("RotateScan::scan_frame: frames match.  Skipping.\n");
1778                         result = plugin->config.rotation_center;
1779                         skip = 1;
1780                 }
1781         }
1782
1783         if(!skip)
1784         {
1785 // Initial search range
1786                 float angle_range = max_angle;
1787                 result = plugin->config.rotation_center;
1788                 total_steps = plugin->config.rotate_positions;
1789
1790
1791                 while(angle_range >= min_angle * total_steps)
1792                 {
1793                         scan_angle1 = result - angle_range;
1794                         scan_angle2 = result + angle_range;
1795
1796
1797                         set_package_count(total_steps);
1798 //set_package_count(1);
1799                         process_packages();
1800
1801                         int64_t min_difference = -1;
1802                         for(int i = 0; i < get_total_packages(); i++)
1803                         {
1804                                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
1805                                 if(pkg->difference < min_difference || min_difference == -1)
1806                                 {
1807                                         min_difference = pkg->difference;
1808                                         result = pkg->angle;
1809                                 }
1810 //break;
1811                         }
1812
1813                         angle_range /= 2;
1814
1815 //break;
1816                 }
1817         }
1818
1819 //printf("RotateScan::scan_frame %d\n", __LINE__);
1820
1821         if(!skip && plugin->config.tracking_type == MotionScan::SAVE)
1822         {
1823                 char string[BCTEXTLEN];
1824                 sprintf(string, "%s%06jd",
1825                         ROTATION_FILE, plugin->get_source_position());
1826                 FILE *output = fopen(string, "w");
1827                 if(output)
1828                 {
1829                         fprintf(output, "%f\n", result);
1830                         fclose(output);
1831                 }
1832                 else
1833                 {
1834                         perror("RotateScan::scan_frame SAVE");
1835                 }
1836         }
1837
1838 //printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result);
1839
1840
1841
1842         return result;
1843 }
1844
1845 int64_t RotateScan::get_cache(float angle)
1846 {
1847         int64_t result = -1;
1848         cache_lock->lock("RotateScan::get_cache");
1849         for(int i = 0; i < cache.total; i++)
1850         {
1851                 RotateScanCache *ptr = cache.values[i];
1852                 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
1853                 {
1854                         result = ptr->difference;
1855                         break;
1856                 }
1857         }
1858         cache_lock->unlock();
1859         return result;
1860 }
1861
1862 void RotateScan::put_cache(float angle, int64_t difference)
1863 {
1864         RotateScanCache *ptr = new RotateScanCache(angle, difference);
1865         cache_lock->lock("RotateScan::put_cache");
1866         cache.append(ptr);
1867         cache_lock->unlock();
1868 }
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878 RotateScanCache::RotateScanCache(float angle, int64_t difference)
1879 {
1880         this->angle = angle;
1881         this->difference = difference;
1882 }
1883
1884
1885