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