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