upgrade to ffmpeg-2.8
[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                 }
976
977                 read_frame(current_global_ref,
978                         reference_layer,
979                         start_position,
980                         frame_rate);
981                 read_frame(global_target_src,
982                         target_layer,
983                         start_position,
984                         frame_rate);
985
986
987
988 // Global followed by rotate
989                 if(config.rotate)
990                 {
991 // Must translate the previous global reference by the current global
992 // accumulation vector to match the current global reference.
993 // The center of the search area is always the user value + the accumulation
994 // vector.
995                         if(!prev_rotate_ref)
996                                 prev_rotate_ref = new VFrame(w, h, color_model);
997 // The current global reference is the current rotation reference.
998                         if(!current_rotate_ref)
999                                 current_rotate_ref = new VFrame(w, h, color_model);
1000                         current_rotate_ref->copy_from(current_global_ref);
1001
1002 // The global target destination is copied to the rotation target source
1003 // then written to the rotation output with rotation.
1004 // The pivot for the rotation is the center of the search area
1005 // if we're tracking.
1006 // The pivot is fixed to the user position if we're compensating.
1007                         if(!rotate_target_src)
1008                                 rotate_target_src = new VFrame(w, h, color_model);
1009                         if(!rotate_target_dst)
1010                                 rotate_target_dst = new VFrame(w, h, color_model);
1011                 }
1012         }
1013         else
1014 // Rotation only
1015         if(config.rotate)
1016         {
1017 // Rotation reads the previous reference frame and compares it with current
1018 // reference frame.
1019                 if(!prev_rotate_ref)
1020                         prev_rotate_ref = new VFrame(w, h, color_model);
1021                 if(!current_rotate_ref)
1022                         current_rotate_ref = new VFrame(w, h, color_model);
1023
1024 // Rotation loads target frame to temporary, rotates it, and writes it to the
1025 // target frame.  The pivot is always fixed.
1026                 if(!rotate_target_src)
1027                         rotate_target_src = new VFrame(w, h, color_model);
1028                 if(!rotate_target_dst)
1029                         rotate_target_dst = new VFrame(w, h, color_model);
1030
1031
1032 // Load the rotate frames
1033                 if(need_reload)
1034                 {
1035                         read_frame(prev_rotate_ref,
1036                                 reference_layer,
1037                                 previous_frame_number,
1038                                 frame_rate);
1039                 }
1040                 read_frame(current_rotate_ref,
1041                         reference_layer,
1042                         start_position,
1043                         frame_rate);
1044                 read_frame(rotate_target_src,
1045                         target_layer,
1046                         start_position,
1047                         frame_rate);
1048         }
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059         if(!skip_current)
1060         {
1061 // Get position change from previous frame to current frame
1062                 if(config.global) process_global();
1063 // Get rotation change from previous frame to current frame
1064                 if(config.rotate) process_rotation();
1065 //frame[target_layer]->copy_from(prev_rotate_ref);
1066 //frame[target_layer]->copy_from(current_rotate_ref);
1067         }
1068
1069
1070
1071
1072
1073
1074 // Transfer the relevant target frame to the output
1075         if(!skip_current)
1076         {
1077                 if(config.rotate)
1078                 {
1079                         frame[target_layer]->copy_from(rotate_target_dst);
1080                 }
1081                 else
1082                 {
1083                         frame[target_layer]->copy_from(global_target_dst);
1084                 }
1085         }
1086         else
1087 // Read the target destination directly
1088         {
1089                 read_frame(frame[target_layer],
1090                         target_layer,
1091                         start_position,
1092                         frame_rate);
1093         }
1094
1095         if(config.draw_vectors)
1096         {
1097                 draw_vectors(frame[target_layer]);
1098         }
1099
1100 #ifdef DEBUG
1101 printf("MotionMain::process_buffer %d\n", __LINE__);
1102 #endif
1103         return 0;
1104 }
1105
1106
1107
1108 void MotionMain::draw_vectors(VFrame *frame)
1109 {
1110         int w = frame->get_w();
1111         int h = frame->get_h();
1112         int global_x1, global_y1;
1113         int global_x2, global_y2;
1114         int block_x, block_y;
1115         int block_w, block_h;
1116         int block_x1, block_y1;
1117         int block_x2, block_y2;
1118         int block_x3, block_y3;
1119         int block_x4, block_y4;
1120         int search_w, search_h;
1121         int search_x1, search_y1;
1122         int search_x2, search_y2;
1123
1124
1125         if(config.global)
1126         {
1127 // Get vector
1128 // Start of vector is center of previous block.
1129 // End of vector is total accumulation.
1130                 if(config.tracking_object == MotionScan::TRACK_SINGLE)
1131                 {
1132                         global_x1 = (int64_t)(config.block_x *
1133                                 w /
1134                                 100);
1135                         global_y1 = (int64_t)(config.block_y *
1136                                 h /
1137                                 100);
1138                         global_x2 = global_x1 + total_dx / OVERSAMPLE;
1139                         global_y2 = global_y1 + total_dy / OVERSAMPLE;
1140 //printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1141                 }
1142                 else
1143 // Start of vector is center of previous block.
1144 // End of vector is current change.
1145                 if(config.tracking_object == MotionScan::PREVIOUS_SAME_BLOCK)
1146                 {
1147                         global_x1 = (int64_t)(config.block_x *
1148                                 w /
1149                                 100);
1150                         global_y1 = (int64_t)(config.block_y *
1151                                 h /
1152                                 100);
1153                         global_x2 = global_x1 + current_dx / OVERSAMPLE;
1154                         global_y2 = global_y1 + current_dy / OVERSAMPLE;
1155                 }
1156                 else
1157                 {
1158                         global_x1 = (int64_t)(config.block_x *
1159                                 w /
1160                                 100 +
1161                                 (total_dx - current_dx) /
1162                                 OVERSAMPLE);
1163                         global_y1 = (int64_t)(config.block_y *
1164                                 h /
1165                                 100 +
1166                                 (total_dy - current_dy) /
1167                                 OVERSAMPLE);
1168                         global_x2 = (int64_t)(config.block_x *
1169                                 w /
1170                                 100 +
1171                                 total_dx /
1172                                 OVERSAMPLE);
1173                         global_y2 = (int64_t)(config.block_y *
1174                                 h /
1175                                 100 +
1176                                 total_dy /
1177                                 OVERSAMPLE);
1178                 }
1179
1180                 block_x = global_x1;
1181                 block_y = global_y1;
1182                 block_w = config.global_block_w * w / 100;
1183                 block_h = config.global_block_h * h / 100;
1184                 block_x1 = block_x - block_w / 2;
1185                 block_y1 = block_y - block_h / 2;
1186                 block_x2 = block_x + block_w / 2;
1187                 block_y2 = block_y + block_h / 2;
1188                 search_w = config.global_range_w * w / 100;
1189                 search_h = config.global_range_h * h / 100;
1190                 search_x1 = block_x1 - search_w / 2;
1191                 search_y1 = block_y1 - search_h / 2;
1192                 search_x2 = block_x2 + search_w / 2;
1193                 search_y2 = block_y2 + search_h / 2;
1194
1195 // printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1196 // global_x1,
1197 // global_y1,
1198 // block_w,
1199 // block_h,
1200 // block_x1,
1201 // block_y1,
1202 // block_x2,
1203 // block_y2,
1204 // search_x1,
1205 // search_y1,
1206 // search_x2,
1207 // search_y2);
1208
1209                 MotionScan::clamp_scan(w,
1210                         h,
1211                         &block_x1,
1212                         &block_y1,
1213                         &block_x2,
1214                         &block_y2,
1215                         &search_x1,
1216                         &search_y1,
1217                         &search_x2,
1218                         &search_y2,
1219                         1);
1220
1221 // Vector
1222                 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1223
1224 // Macroblock
1225                 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1226                 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1227                 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1228                 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1229
1230
1231 // Search area
1232                 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1233                 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1234                 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1235                 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1236
1237 // Block should be endpoint of motion
1238                 if(config.rotate)
1239                 {
1240                         block_x = global_x2;
1241                         block_y = global_y2;
1242                 }
1243         }
1244         else
1245         {
1246                 block_x = (int64_t)(config.block_x * w / 100);
1247                 block_y = (int64_t)(config.block_y * h / 100);
1248         }
1249
1250         block_w = config.global_block_w * w / 100;
1251         block_h = config.global_block_h * h / 100;
1252         if(config.rotate)
1253         {
1254                 float angle = total_angle * 2 * M_PI / 360;
1255                 double base_angle1 = atan((float)block_h / block_w);
1256                 double base_angle2 = atan((float)block_w / block_h);
1257                 double target_angle1 = base_angle1 + angle;
1258                 double target_angle2 = base_angle2 + angle;
1259                 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1260                 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1261                 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1262                 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1263                 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1264                 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1265                 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1266                 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1267                 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1268
1269                 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1270                 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1271                 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1272                 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1273
1274
1275 // Center
1276                 if(!config.global)
1277                 {
1278                         draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1279                         draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1280                 }
1281         }
1282 }
1283
1284
1285
1286 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
1287 {
1288         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1289
1290 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1291 { \
1292         type **rows = (type**)frame->get_rows(); \
1293         rows[y][x * components] = max - rows[y][x * components]; \
1294         if(!do_yuv) \
1295         { \
1296                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1297                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1298         } \
1299         else \
1300         { \
1301                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1302                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1303         } \
1304         if(components == 4) \
1305                 rows[y][x * components + 3] = max; \
1306 }
1307
1308
1309         switch(frame->get_color_model())
1310         {
1311                 case BC_RGB888:
1312                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1313                         break;
1314                 case BC_RGBA8888:
1315                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1316                         break;
1317                 case BC_RGB_FLOAT:
1318                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1319                         break;
1320                 case BC_RGBA_FLOAT:
1321                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1322                         break;
1323                 case BC_YUV888:
1324                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1325                         break;
1326                 case BC_YUVA8888:
1327                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1328                         break;
1329                 case BC_RGB161616:
1330                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1331                         break;
1332                 case BC_YUV161616:
1333                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1334                         break;
1335                 case BC_RGBA16161616:
1336                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1337                         break;
1338                 case BC_YUVA16161616:
1339                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1340                         break;
1341         }
1342 }
1343
1344
1345 void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1346 {
1347         int w = labs(x2 - x1);
1348         int h = labs(y2 - y1);
1349 //printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1350
1351         if(!w && !h)
1352         {
1353                 draw_pixel(frame, x1, y1);
1354         }
1355         else
1356         if(w > h)
1357         {
1358 // Flip coordinates so x1 < x2
1359                 if(x2 < x1)
1360                 {
1361                         y2 ^= y1;
1362                         y1 ^= y2;
1363                         y2 ^= y1;
1364                         x1 ^= x2;
1365                         x2 ^= x1;
1366                         x1 ^= x2;
1367                 }
1368                 int numerator = y2 - y1;
1369                 int denominator = x2 - x1;
1370                 for(int i = x1; i < x2; i++)
1371                 {
1372                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1373                         draw_pixel(frame, i, y);
1374                 }
1375         }
1376         else
1377         {
1378 // Flip coordinates so y1 < y2
1379                 if(y2 < y1)
1380                 {
1381                         y2 ^= y1;
1382                         y1 ^= y2;
1383                         y2 ^= y1;
1384                         x1 ^= x2;
1385                         x2 ^= x1;
1386                         x1 ^= x2;
1387                 }
1388                 int numerator = x2 - x1;
1389                 int denominator = y2 - y1;
1390                 for(int i = y1; i < y2; i++)
1391                 {
1392                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1393                         draw_pixel(frame, x, i);
1394                 }
1395         }
1396 //printf("MotionMain::draw_line 2\n");
1397 }
1398
1399 #define ARROW_SIZE 10
1400 void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1401 {
1402         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1403         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1404         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1405         int x3;
1406         int y3;
1407         int x4;
1408         int y4;
1409         if(x2 < x1)
1410         {
1411                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1412                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1413                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1414                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1415         }
1416         else
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
1424 // Main vector
1425         draw_line(frame, x1, y1, x2, y2);
1426 //      draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1427
1428 // Arrow line
1429         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1430 //      draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1431 // Arrow line
1432         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1433 //      draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1434 }
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451 RotateScanPackage::RotateScanPackage()
1452 {
1453 }
1454
1455
1456 RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin)
1457  : LoadClient(server)
1458 {
1459         this->server = server;
1460         this->plugin = plugin;
1461         rotater = 0;
1462         temp = 0;
1463 }
1464
1465 RotateScanUnit::~RotateScanUnit()
1466 {
1467         delete rotater;
1468         delete temp;
1469 }
1470
1471 void RotateScanUnit::process_package(LoadPackage *package)
1472 {
1473         if(server->skip) return;
1474         RotateScanPackage *pkg = (RotateScanPackage*)package;
1475
1476         if((pkg->difference = server->get_cache(pkg->angle)) < 0)
1477         {
1478 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1479                 int color_model = server->previous_frame->get_color_model();
1480                 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
1481                 int row_bytes = server->previous_frame->get_bytes_per_line();
1482
1483                 if(!rotater)
1484                         rotater = new AffineEngine(1, 1);
1485                 if(!temp) temp = new VFrame(0,
1486                         -1,
1487                         server->previous_frame->get_w(),
1488                         server->previous_frame->get_h(),
1489                         color_model,
1490                         -1);
1491 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1492
1493
1494 // Rotate original block size
1495 //              rotater->set_viewport(server->block_x1,
1496 //                      server->block_y1,
1497 //                      server->block_x2 - server->block_x1,
1498 //                      server->block_y2 - server->block_y1);
1499                 rotater->set_in_viewport(server->block_x1,
1500                         server->block_y1,
1501                         server->block_x2 - server->block_x1,
1502                         server->block_y2 - server->block_y1);
1503                 rotater->set_out_viewport(server->block_x1,
1504                         server->block_y1,
1505                         server->block_x2 - server->block_x1,
1506                         server->block_y2 - server->block_y1);
1507 //              rotater->set_pivot(server->block_x, server->block_y);
1508                 rotater->set_in_pivot(server->block_x, server->block_y);
1509                 rotater->set_out_pivot(server->block_x, server->block_y);
1510 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1511                 rotater->rotate(temp,
1512                         server->previous_frame,
1513                         pkg->angle);
1514
1515 // Scan reduced block size
1516 //plugin->output_frame->copy_from(server->current_frame);
1517 //plugin->output_frame->copy_from(temp);
1518 // printf("RotateScanUnit::process_package %d %d %d %d %d\n",
1519 // __LINE__,
1520 // server->scan_x,
1521 // server->scan_y,
1522 // server->scan_w,
1523 // server->scan_h);
1524 // Clamp coordinates
1525                 int x1 = server->scan_x;
1526                 int y1 = server->scan_y;
1527                 int x2 = x1 + server->scan_w;
1528                 int y2 = y1 + server->scan_h;
1529                 x2 = MIN(temp->get_w(), x2);
1530                 y2 = MIN(temp->get_h(), y2);
1531                 x2 = MIN(server->current_frame->get_w(), x2);
1532                 y2 = MIN(server->current_frame->get_h(), y2);
1533                 x1 = MAX(0, x1);
1534                 y1 = MAX(0, y1);
1535
1536                 if(x2 > x1 && y2 > y1)
1537                 {
1538                         pkg->difference = MotionScan::abs_diff(
1539                                 temp->get_rows()[y1] + x1 * pixel_size,
1540                                 server->current_frame->get_rows()[y1] + x1 * pixel_size,
1541                                 row_bytes,
1542                                 x2 - x1,
1543                                 y2 - y1,
1544                                 color_model);
1545 //printf("RotateScanUnit::process_package %d\n", __LINE__);
1546                         server->put_cache(pkg->angle, pkg->difference);
1547                 }
1548
1549 // 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",
1550 // server->block_x1,
1551 // server->block_y1,
1552 // server->block_x2 - server->block_x1,
1553 // server->block_y2 - server->block_y1,
1554 // server->block_x,
1555 // server->block_y,
1556 // pkg->angle,
1557 // server->scan_w,
1558 // server->scan_h,
1559 // pkg->difference);
1560         }
1561 }
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584 RotateScan::RotateScan(MotionMain *plugin,
1585         int total_clients,
1586         int total_packages)
1587  : LoadServer(
1588 //1, 1
1589 total_clients, total_packages
1590 )
1591 {
1592         this->plugin = plugin;
1593         cache_lock = new Mutex("RotateScan::cache_lock");
1594 }
1595
1596
1597 RotateScan::~RotateScan()
1598 {
1599         delete cache_lock;
1600 }
1601
1602 void RotateScan::init_packages()
1603 {
1604         for(int i = 0; i < get_total_packages(); i++)
1605         {
1606                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
1607                 pkg->angle = i *
1608                         (scan_angle2 - scan_angle1) /
1609                         (total_steps - 1) +
1610                         scan_angle1;
1611         }
1612 }
1613
1614 LoadClient* RotateScan::new_client()
1615 {
1616         return new RotateScanUnit(this, plugin);
1617 }
1618
1619 LoadPackage* RotateScan::new_package()
1620 {
1621         return new RotateScanPackage;
1622 }
1623
1624
1625 float RotateScan::scan_frame(VFrame *previous_frame,
1626         VFrame *current_frame,
1627         int block_x,
1628         int block_y)
1629 {
1630         skip = 0;
1631         this->block_x = block_x;
1632         this->block_y = block_y;
1633
1634 //printf("RotateScan::scan_frame %d\n", __LINE__);
1635         switch(plugin->config.tracking_type)
1636         {
1637                 case MotionScan::NO_CALCULATE:
1638                         result = plugin->config.rotation_center;
1639                         skip = 1;
1640                         break;
1641
1642                 case MotionScan::LOAD:
1643                 {
1644                         char string[BCTEXTLEN];
1645                         sprintf(string, "%s" _LDv(06),
1646                                  ROTATION_FILE, plugin->get_source_position());
1647                         FILE *input = fopen(string, "r");
1648                         if(input)
1649                         {
1650                                 fscanf(input, "%f", &result);
1651                                 fclose(input);
1652                                 skip = 1;
1653                         }
1654                         else
1655                         {
1656                                 perror("RotateScan::scan_frame LOAD");
1657                         }
1658                         break;
1659                 }
1660         }
1661
1662
1663
1664
1665
1666
1667
1668
1669         this->previous_frame = previous_frame;
1670         this->current_frame = current_frame;
1671         int w = current_frame->get_w();
1672         int h = current_frame->get_h();
1673         int block_w = w * plugin->config.global_block_w / 100;
1674         int block_h = h * plugin->config.global_block_h / 100;
1675
1676         if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
1677         if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
1678         if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
1679         if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
1680
1681         block_x1 = this->block_x - block_w / 2;
1682         block_x2 = this->block_x + block_w / 2;
1683         block_y1 = this->block_y - block_h / 2;
1684         block_y2 = this->block_y + block_h / 2;
1685
1686 // Calculate the maximum area available to scan after rotation.
1687 // Must be calculated from the starting range because of cache.
1688 // Get coords of rectangle after rotation.
1689         double center_x = this->block_x;
1690         double center_y = this->block_y;
1691         double max_angle = plugin->config.rotation_range;
1692         double base_angle1 = atan((float)block_h / block_w);
1693         double base_angle2 = atan((float)block_w / block_h);
1694         double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
1695         double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
1696         double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1697         double x1 = center_x - cos(target_angle1) * radius;
1698         double y1 = center_y - sin(target_angle1) * radius;
1699         double x2 = center_x + sin(target_angle2) * radius;
1700         double y2 = center_y - cos(target_angle2) * radius;
1701         double x3 = center_x - sin(target_angle2) * radius;
1702         double y3 = center_y + cos(target_angle2) * radius;
1703
1704 // Track top edge to find greatest area.
1705         double max_area1 = 0;
1706         //double max_x1 = 0;
1707         double max_y1 = 0;
1708         for(double x = x1; x < x2; x++)
1709         {
1710                 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
1711                 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
1712                 {
1713                         double area = fabs(x - center_x) * fabs(y - center_y);
1714                         if(area > max_area1)
1715                         {
1716                                 max_area1 = area;
1717                                 //max_x1 = x;
1718                                 max_y1 = y;
1719                         }
1720                 }
1721         }
1722
1723 // Track left edge to find greatest area.
1724         double max_area2 = 0;
1725         double max_x2 = 0;
1726         //double max_y2 = 0;
1727         for(double y = y1; y < y3; y++)
1728         {
1729                 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
1730                 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
1731                 {
1732                         double area = fabs(x - center_x) * fabs(y - center_y);
1733                         if(area > max_area2)
1734                         {
1735                                 max_area2 = area;
1736                                 max_x2 = x;
1737                                 //max_y2 = y;
1738                         }
1739                 }
1740         }
1741
1742         double max_x, max_y;
1743         max_x = max_x2;
1744         max_y = max_y1;
1745
1746 // Get reduced scan coords
1747         scan_w = (int)(fabs(max_x - center_x) * 2);
1748         scan_h = (int)(fabs(max_y - center_y) * 2);
1749         scan_x = (int)(center_x - scan_w / 2);
1750         scan_y = (int)(center_y - scan_h / 2);
1751 // printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n",
1752 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
1753 // printf("    angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
1754
1755 // Determine min angle from size of block
1756         double angle1 = atan((double)block_h / block_w);
1757         double angle2 = atan((double)(block_h - 1) / (block_w + 1));
1758         double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
1759         min_angle = MAX(min_angle, MIN_ANGLE);
1760
1761 //printf("RotateScan::scan_frame %d min_angle=%f\n", __LINE__, min_angle * 360 / 2 / M_PI);
1762
1763         cache.remove_all_objects();
1764
1765
1766         if(!skip)
1767         {
1768                 if(previous_frame->data_matches(current_frame))
1769                 {
1770 //printf("RotateScan::scan_frame: frames match.  Skipping.\n");
1771                         result = plugin->config.rotation_center;
1772                         skip = 1;
1773                 }
1774         }
1775
1776         if(!skip)
1777         {
1778 // Initial search range
1779                 float angle_range = max_angle;
1780                 result = plugin->config.rotation_center;
1781                 total_steps = plugin->config.rotate_positions;
1782
1783
1784                 while(angle_range >= min_angle * total_steps)
1785                 {
1786                         scan_angle1 = result - angle_range;
1787                         scan_angle2 = result + angle_range;
1788
1789
1790                         set_package_count(total_steps);
1791 //set_package_count(1);
1792                         process_packages();
1793
1794                         int64_t min_difference = -1;
1795                         for(int i = 0; i < get_total_packages(); i++)
1796                         {
1797                                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
1798                                 if(pkg->difference < min_difference || min_difference == -1)
1799                                 {
1800                                         min_difference = pkg->difference;
1801                                         result = pkg->angle;
1802                                 }
1803 //break;
1804                         }
1805
1806                         angle_range /= 2;
1807
1808 //break;
1809                 }
1810         }
1811
1812 //printf("RotateScan::scan_frame %d\n", __LINE__);
1813
1814         if(!skip && plugin->config.tracking_type == MotionScan::SAVE)
1815         {
1816                 char string[BCTEXTLEN];
1817                 sprintf(string, "%s" _LDv(06),
1818                         ROTATION_FILE, plugin->get_source_position());
1819                 FILE *output = fopen(string, "w");
1820                 if(output)
1821                 {
1822                         fprintf(output, "%f\n", result);
1823                         fclose(output);
1824                 }
1825                 else
1826                 {
1827                         perror("RotateScan::scan_frame SAVE");
1828                 }
1829         }
1830
1831 //printf("RotateScan::scan_frame %d angle=%f\n", __LINE__, result);
1832
1833
1834
1835         return result;
1836 }
1837
1838 int64_t RotateScan::get_cache(float angle)
1839 {
1840         int64_t result = -1;
1841         cache_lock->lock("RotateScan::get_cache");
1842         for(int i = 0; i < cache.total; i++)
1843         {
1844                 RotateScanCache *ptr = cache.values[i];
1845                 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
1846                 {
1847                         result = ptr->difference;
1848                         break;
1849                 }
1850         }
1851         cache_lock->unlock();
1852         return result;
1853 }
1854
1855 void RotateScan::put_cache(float angle, int64_t difference)
1856 {
1857         RotateScanCache *ptr = new RotateScanCache(angle, difference);
1858         cache_lock->lock("RotateScan::put_cache");
1859         cache.append(ptr);
1860         cache_lock->unlock();
1861 }
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871 RotateScanCache::RotateScanCache(float angle, int64_t difference)
1872 {
1873         this->angle = angle;
1874         this->difference = difference;
1875 }
1876
1877
1878