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