Condense the xml-cleanup into a short feature-branch
[cinelerra_cv/mob.git] / plugins / motion / motion.C
blob6827e510d08826f9757ccb8a9e84e445d42a9501
1 #include "affine.h"
2 #include "bcdisplayinfo.h"
3 #include "clip.h"
4 #include "bchash.h"
5 #include "filexml.h"
6 #include "keyframe.h"
7 #include "language.h"
8 #include "motion.h"
9 #include "motionwindow.h"
10 #include "mutex.h"
11 #include "overlayframe.h"
12 #include "picon_png.h"
13 #include "rotateframe.h"
14 #include "transportque.h"
17 #include <errno.h>
18 #include <unistd.h>
20 REGISTER_PLUGIN(MotionMain)
22 //#undef DEBUG
24 #ifndef DEBUG
25 #define DEBUG
26 #endif
28 static void sort(int *array, int total)
30         int done = 0;
31         while(!done)
32         {
33                 done = 1;
34                 for(int i = 0; i < total - 1; i++)
35                 {
36                         if(array[i] > array[i + 1])
37                         {
38                                 array[i] ^= array[i + 1];
39                                 array[i + 1] ^= array[i];
40                                 array[i] ^= array[i + 1];
41                                 done = 0;
42                         }
43                 }
44         }
49 MotionConfig::MotionConfig()
51         global_range_w = 5;
52         global_range_h = 5;
53         rotation_range = 5;
54         block_count = 1;
55         global_block_w = MIN_BLOCK;
56         global_block_h = MIN_BLOCK;
57         rotation_block_w = MIN_BLOCK;
58         rotation_block_h = MIN_BLOCK;
59         block_x = 50;
60         block_y = 50;
61         global_positions = 256;
62         rotate_positions = 4;
63         magnitude = 100;
64         return_speed = 0;
65         mode1 = STABILIZE;
66         global = 1;
67         rotate = 1;
68         mode2 = NO_CALCULATE;
69         draw_vectors = 1;
70         mode3 = MotionConfig::TRACK_SINGLE;
71         track_frame = 0;
72         bottom_is_master = 1;
73         horizontal_only = 0;
74         vertical_only = 0;
77 void MotionConfig::boundaries()
79         CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
80         CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS);
81         CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION);
82         CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
83         CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
84         CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
85         CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
86         CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
89 int MotionConfig::equivalent(MotionConfig &that)
91         return global_range_w == that.global_range_w &&
92                 global_range_h == that.global_range_h &&
93                 rotation_range == that.rotation_range &&
94                 mode1 == that.mode1 &&
95                 global == that.global &&
96                 rotate == that.rotate &&
97                 draw_vectors == that.draw_vectors &&
98                 block_count == that.block_count &&
99                 global_block_w == that.global_block_w &&
100                 global_block_h == that.global_block_h &&
101                 rotation_block_w == that.rotation_block_w &&
102                 rotation_block_h == that.rotation_block_h &&
103                 EQUIV(block_x, that.block_x) &&
104                 EQUIV(block_y, that.block_y) &&
105                 global_positions == that.global_positions &&
106                 rotate_positions == that.rotate_positions &&
107                 magnitude == that.magnitude &&
108                 return_speed == that.return_speed &&
109                 mode3 == that.mode3 &&
110                 track_frame == that.track_frame &&
111                 bottom_is_master == that.bottom_is_master &&
112                 horizontal_only == that.horizontal_only &&
113                 vertical_only == that.vertical_only;
116 void MotionConfig::copy_from(MotionConfig &that)
118         global_range_w = that.global_range_w;
119         global_range_h = that.global_range_h;
120         rotation_range = that.rotation_range;
121         mode1 = that.mode1;
122         global = that.global;
123         rotate = that.rotate;
124         mode2 = that.mode2;
125         draw_vectors = that.draw_vectors;
126         block_count = that.block_count;
127         block_x = that.block_x;
128         block_y = that.block_y;
129         global_positions = that.global_positions;
130         rotate_positions = that.rotate_positions;
131         global_block_w = that.global_block_w;
132         global_block_h = that.global_block_h;
133         rotation_block_w = that.rotation_block_w;
134         rotation_block_h = that.rotation_block_h;
135         magnitude = that.magnitude;
136         return_speed = that.return_speed;
137         mode3 = that.mode3;
138         track_frame = that.track_frame;
139         bottom_is_master = that.bottom_is_master;
140         horizontal_only = that.horizontal_only;
141         vertical_only = that.vertical_only;
144 void MotionConfig::interpolate(MotionConfig &prev, 
145         MotionConfig &next, 
146         int64_t prev_frame, 
147         int64_t next_frame, 
148         int64_t current_frame)
150         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
151         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
152         this->block_x = prev.block_x;
153         this->block_y = prev.block_y;
154         global_range_w = prev.global_range_w;
155         global_range_h = prev.global_range_h;
156         rotation_range = prev.rotation_range;
157         mode1 = prev.mode1;
158         global = prev.global;
159         rotate = prev.rotate;
160         mode2 = prev.mode2;
161         draw_vectors = prev.draw_vectors;
162         block_count = prev.block_count;
163         global_positions = prev.global_positions;
164         rotate_positions = prev.rotate_positions;
165         global_block_w = prev.global_block_w;
166         global_block_h = prev.global_block_h;
167         rotation_block_w = prev.rotation_block_w;
168         rotation_block_h = prev.rotation_block_h;
169         magnitude = prev.magnitude;
170         return_speed = prev.return_speed;
171         mode3 = prev.mode3;
172         track_frame = prev.track_frame;
173         bottom_is_master = prev.bottom_is_master;
174         horizontal_only = prev.horizontal_only;
175         vertical_only = prev.vertical_only;
196 MotionMain::MotionMain(PluginServer *server)
197  : PluginVClient(server)
199         PLUGIN_CONSTRUCTOR_MACRO
200         engine = 0;
201         rotate_engine = 0;
202         motion_rotate = 0;
203         total_dx = 0;
204         total_dy = 0;
205         total_angle = 0;
206         overlayer = 0;
207         search_area = 0;
208         search_size = 0;
209         temp_frame = 0;
210         previous_frame_number = -1;
212         prev_global_ref = 0;
213         current_global_ref = 0;
214         global_target_src = 0;
215         global_target_dst = 0;
217         prev_rotate_ref = 0;
218         current_rotate_ref = 0;
219         rotate_target_src = 0;
220         rotate_target_dst = 0;
223 MotionMain::~MotionMain()
225         PLUGIN_DESTRUCTOR_MACRO
226         delete engine;
227         delete overlayer;
228         delete [] search_area;
229         delete temp_frame;
230         delete rotate_engine;
231         delete motion_rotate;
234         delete prev_global_ref;
235         delete current_global_ref;
236         delete global_target_src;
237         delete global_target_dst;
239         delete prev_rotate_ref;
240         delete current_rotate_ref;
241         delete rotate_target_src;
242         delete rotate_target_dst;
245 char* MotionMain::plugin_title() { return N_("Motion"); }
246 int MotionMain::is_realtime() { return 1; }
247 int MotionMain::is_multichannel() { return 1; }
249 NEW_PICON_MACRO(MotionMain)
251 SHOW_GUI_MACRO(MotionMain, MotionThread)
253 SET_STRING_MACRO(MotionMain)
255 RAISE_WINDOW_MACRO(MotionMain)
257 LOAD_CONFIGURATION_MACRO(MotionMain, MotionConfig)
261 void MotionMain::update_gui()
263         if(thread)
264         {
265                 if(load_configuration())
266                 {
267                         thread->window->lock_window("MotionMain::update_gui");
268                         
269                         char string[BCTEXTLEN];
270                         sprintf(string, "%d", config.global_positions);
271                         thread->window->global_search_positions->set_text(string);
272                         sprintf(string, "%d", config.rotate_positions);
273                         thread->window->rotation_search_positions->set_text(string);
275                         thread->window->global_block_w->update(config.global_block_w);
276                         thread->window->global_block_h->update(config.global_block_h);
277                         thread->window->rotation_block_w->update(config.rotation_block_w);
278                         thread->window->rotation_block_h->update(config.rotation_block_h);
279                         thread->window->block_x->update(config.block_x);
280                         thread->window->block_y->update(config.block_y);
281                         thread->window->block_x_text->update((float)config.block_x);
282                         thread->window->block_y_text->update((float)config.block_y);
283                         thread->window->magnitude->update(config.magnitude);
284                         thread->window->return_speed->update(config.return_speed);
287                         thread->window->track_single->update(config.mode3 == MotionConfig::TRACK_SINGLE);
288                         thread->window->track_frame_number->update(config.track_frame);
289                         thread->window->track_previous->update(config.mode3 == MotionConfig::TRACK_PREVIOUS);
290                         thread->window->previous_same->update(config.mode3 == MotionConfig::PREVIOUS_SAME_BLOCK);
291                         if(config.mode3 != MotionConfig::TRACK_SINGLE)
292                                 thread->window->track_frame_number->disable();
293                         else
294                                 thread->window->track_frame_number->enable();
296                         thread->window->mode1->set_text(
297                                 Mode1::to_text(config.mode1));
298                         thread->window->mode2->set_text(
299                                 Mode2::to_text(config.mode2));
300                         thread->window->mode3->set_text(
301                                 Mode3::to_text(config.horizontal_only, config.vertical_only));
302                         thread->window->master_layer->set_text(
303                                 MasterLayer::to_text(config.bottom_is_master));
306                         thread->window->update_mode();
307                         thread->window->unlock_window();
308                 }
309         }
313 int MotionMain::load_defaults()
315         char directory[BCTEXTLEN], string[BCTEXTLEN];
316 // set the default directory
317         sprintf(directory, "%smotion.rc", BCASTDIR);
319 // load the defaults
320         defaults = new BC_Hash(directory);
321         defaults->load();
323         config.block_count = defaults->get("BLOCK_COUNT", config.block_count);
324         config.global_positions = defaults->get("GLOBAL_POSITIONS", config.global_positions);
325         config.rotate_positions = defaults->get("ROTATE_POSITIONS", config.rotate_positions);
326         config.global_block_w = defaults->get("GLOBAL_BLOCK_W", config.global_block_w);
327         config.global_block_h = defaults->get("GLOBAL_BLOCK_H", config.global_block_h);
328         config.rotation_block_w = defaults->get("ROTATION_BLOCK_W", config.rotation_block_w);
329         config.rotation_block_h = defaults->get("ROTATION_BLOCK_H", config.rotation_block_h);
330         config.block_x = defaults->get("BLOCK_X", config.block_x);
331         config.block_y = defaults->get("BLOCK_Y", config.block_y);
332         config.global_range_w = defaults->get("GLOBAL_RANGE_W", config.global_range_w);
333         config.global_range_h = defaults->get("GLOBAL_RANGE_H", config.global_range_h);
334         config.rotation_range = defaults->get("ROTATION_RANGE", config.rotation_range);
335         config.magnitude = defaults->get("MAGNITUDE", config.magnitude);
336         config.return_speed = defaults->get("RETURN_SPEED", config.return_speed);
337         config.mode1 = defaults->get("MODE1", config.mode1);
338         config.global = defaults->get("GLOBAL", config.global);
339         config.rotate = defaults->get("ROTATE", config.rotate);
340         config.mode2 = defaults->get("MODE2", config.mode2);
341         config.draw_vectors = defaults->get("DRAW_VECTORS", config.draw_vectors);
342         config.mode3 = defaults->get("MODE3", config.mode3);
343         config.track_frame = defaults->get("TRACK_FRAME", config.track_frame);
344         config.bottom_is_master = defaults->get("BOTTOM_IS_MASTER", config.bottom_is_master);
345         config.horizontal_only = defaults->get("HORIZONTAL_ONLY", config.horizontal_only);
346         config.vertical_only = defaults->get("VERTICAL_ONLY", config.vertical_only);
347         config.boundaries();
348         return 0;
352 int MotionMain::save_defaults()
354         defaults->update("BLOCK_COUNT", config.block_count);
355         defaults->update("GLOBAL_POSITIONS", config.global_positions);
356         defaults->update("ROTATE_POSITIONS", config.rotate_positions);
357         defaults->update("GLOBAL_BLOCK_W", config.global_block_w);
358         defaults->update("GLOBAL_BLOCK_H", config.global_block_h);
359         defaults->update("ROTATION_BLOCK_W", config.rotation_block_w);
360         defaults->update("ROTATION_BLOCK_H", config.rotation_block_h);
361         defaults->update("BLOCK_X", config.block_x);
362         defaults->update("BLOCK_Y", config.block_y);
363         defaults->update("GLOBAL_RANGE_W", config.global_range_w);
364         defaults->update("GLOBAL_RANGE_H", config.global_range_h);
365         defaults->update("ROTATION_RANGE", config.rotation_range);
366         defaults->update("MAGNITUDE", config.magnitude);
367         defaults->update("RETURN_SPEED", config.return_speed);
368         defaults->update("MODE1", config.mode1);
369         defaults->update("GLOBAL", config.global);
370         defaults->update("ROTATE", config.rotate);
371         defaults->update("MODE2", config.mode2);
372         defaults->update("DRAW_VECTORS", config.draw_vectors);
373         defaults->update("MODE3", config.mode3);
374         defaults->update("TRACK_FRAME", config.track_frame);
375         defaults->update("BOTTOM_IS_MASTER", config.bottom_is_master);
376         defaults->update("HORIZONTAL_ONLY", config.horizontal_only);
377         defaults->update("VERTICAL_ONLY", config.vertical_only);
378         defaults->save();
379         return 0;
384 void MotionMain::save_data(KeyFrame *keyframe)
386         FileXML output;
388 // cause data to be stored directly in text
389         output.set_shared_string(keyframe->data, MESSAGESIZE);
390         output.tag.set_title("MOTION");
392         output.tag.set_property("BLOCK_COUNT", config.block_count);
393         output.tag.set_property("GLOBAL_POSITIONS", config.global_positions);
394         output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
395         output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
396         output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
397         output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
398         output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
399         output.tag.set_property("BLOCK_X", config.block_x);
400         output.tag.set_property("BLOCK_Y", config.block_y);
401         output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
402         output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h);
403         output.tag.set_property("ROTATION_RANGE", config.rotation_range);
404         output.tag.set_property("MAGNITUDE", config.magnitude);
405         output.tag.set_property("RETURN_SPEED", config.return_speed);
406         output.tag.set_property("MODE1", config.mode1);
407         output.tag.set_property("GLOBAL", config.global);
408         output.tag.set_property("ROTATE", config.rotate);
409         output.tag.set_property("MODE2", config.mode2);
410         output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
411         output.tag.set_property("MODE3", config.mode3);
412         output.tag.set_property("TRACK_FRAME", config.track_frame);
413         output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master);
414         output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only);
415         output.tag.set_property("VERTICAL_ONLY", config.vertical_only);
416         output.append_tag();
417         output.tag.set_title("/MOTION");
418         output.append_tag();
419         output.terminate_string();
422 void MotionMain::read_data(KeyFrame *keyframe)
424         FileXML input;
426         input.set_shared_string(keyframe->data, strlen(keyframe->data));
428         int result = 0;
430         while(!result)
431         {
432                 result = input.read_tag();
434                 if(!result)
435                 {
436                         if(input.tag.title_is("MOTION"))
437                         {
438                                 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
439                                 config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
440                                 config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
441                                 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
442                                 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
443                                 config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
444                                 config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
445                                 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
446                                 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
447                                 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
448                                 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
449                                 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
450                                 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
451                                 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
452                                 config.mode1 = input.tag.get_property("MODE1", config.mode1);
453                                 config.global = input.tag.get_property("GLOBAL", config.global);
454                                 config.rotate = input.tag.get_property("ROTATE", config.rotate);
455                                 config.mode2 = input.tag.get_property("MODE2", config.mode2);
456                                 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
457                                 config.mode3 = input.tag.get_property("MODE3", config.mode3);
458                                 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
459                                 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
460                                 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
461                                 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
462                         }
463                 }
464         }
465         config.boundaries();
476 void MotionMain::allocate_temp(int w, int h, int color_model)
478         if(temp_frame && 
479                 (temp_frame->get_w() != w ||
480                 temp_frame->get_h() != h))
481         {
482                 delete temp_frame;
483                 temp_frame = 0;
484         }
485         if(!temp_frame)
486                 temp_frame = new VFrame(0, w, h, color_model);
491 void MotionMain::process_global()
493         if(!engine) engine = new MotionScan(this,
494                 PluginClient::get_project_smp() + 1,
495                 PluginClient::get_project_smp() + 1);
497 // Get the current motion vector between the previous and current frame
498         engine->scan_frame(current_global_ref, prev_global_ref);
499         current_dx = engine->dx_result;
500         current_dy = engine->dy_result;
502 // Add current motion vector to accumulation vector.
503         if(config.mode3 != MotionConfig::TRACK_SINGLE)
504         {
505 // Retract over time
506                 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
507                 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
508                 total_dx += engine->dx_result;
509                 total_dy += engine->dy_result;
510         }
511         else
512 // Make accumulation vector current
513         {
514                 total_dx = engine->dx_result;
515                 total_dy = engine->dy_result;
516         }
518 // Clamp accumulation vector
519         if(config.magnitude < 100)
520         {
521                 int block_w = (int64_t)config.global_block_w * 
522                                 current_global_ref->get_w() / 100;
523                 int block_h = (int64_t)config.global_block_h * 
524                                 current_global_ref->get_h() / 100;
525                 int block_x_orig = (int64_t)(config.block_x * 
526                         current_global_ref->get_w() / 
527                         100);
528                 int block_y_orig = (int64_t)(config.block_y *
529                         current_global_ref->get_h() / 
530                         100);
532                 int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
533                         OVERSAMPLE * 
534                         config.magnitude / 
535                         100;
536                 int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
537                         OVERSAMPLE *
538                         config.magnitude / 
539                         100;
540                 int min_block_x = (int64_t)-block_x_orig * 
541                         OVERSAMPLE * 
542                         config.magnitude / 
543                         100;
544                 int min_block_y = (int64_t)-block_y_orig * 
545                         OVERSAMPLE * 
546                         config.magnitude / 
547                         100;
549                 CLAMP(total_dx, min_block_x, max_block_x);
550                 CLAMP(total_dy, min_block_y, max_block_y);
551         }
553 #ifdef DEBUG
554 printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", 
555 (float)total_dx / OVERSAMPLE,
556 (float)total_dy / OVERSAMPLE);
557 #endif
559         if(config.mode3 != MotionConfig::TRACK_SINGLE && !config.rotate)
560         {
561 // Transfer current reference frame to previous reference frame and update
562 // counter.  Must wait for rotate to compare.
563                 prev_global_ref->copy_from(current_global_ref);
564                 previous_frame_number = get_source_position();
565         }
567 // Decide what to do with target based on requested operation
568         int interpolation;
569         float dx;
570         float dy;
571         switch(config.mode1)
572         {
573                 case MotionConfig::NOTHING:
574                         global_target_dst->copy_from(global_target_src);
575                         break;
576                 case MotionConfig::TRACK_PIXEL:
577                         interpolation = NEAREST_NEIGHBOR;
578                         dx = (int)(total_dx / OVERSAMPLE);
579                         dy = (int)(total_dy / OVERSAMPLE);
580                         break;
581                 case MotionConfig::STABILIZE_PIXEL:
582                         interpolation = NEAREST_NEIGHBOR;
583                         dx = -(int)(total_dx / OVERSAMPLE);
584                         dy = -(int)(total_dy / OVERSAMPLE);
585                         break;
586                         break;
587                 case MotionConfig::TRACK:
588                         interpolation = CUBIC_LINEAR;
589                         dx = (float)total_dx / OVERSAMPLE;
590                         dy = (float)total_dy / OVERSAMPLE;
591                         break;
592                 case MotionConfig::STABILIZE:
593                         interpolation = CUBIC_LINEAR;
594                         dx = -(float)total_dx / OVERSAMPLE;
595                         dy = -(float)total_dy / OVERSAMPLE;
596                         break;
597         }
600         if(config.mode1 != MotionConfig::NOTHING)
601         {
602                 if(!overlayer) 
603                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
604                 global_target_dst->clear_frame();
605                 overlayer->overlay(global_target_dst,
606                         global_target_src,
607                         0,
608                         0,
609                         global_target_src->get_w(),
610                         global_target_src->get_h(),
611                         dx,
612                         dy,
613                         (float)global_target_src->get_w() + dx,
614                         (float)global_target_src->get_h() + dy,
615                         1,
616                         TRANSFER_REPLACE,
617                         interpolation);
618         }
623 void MotionMain::process_rotation()
625         int block_x;
626         int block_y;
628 // Convert the previous global reference into the previous rotation reference.
629 // Convert global target destination into rotation target source.
630         if(config.global)
631         {
632                 if(!overlayer) 
633                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
634                 float dx;
635                 float dy;
636                 if(config.mode3 == MotionConfig::TRACK_SINGLE)
637                 {
638                         dx = (float)total_dx / OVERSAMPLE;
639                         dy = (float)total_dy / OVERSAMPLE;
640                 }
641                 else
642                 {
643                         dx = (float)current_dx / OVERSAMPLE;
644                         dy = (float)current_dy / OVERSAMPLE;
645                 }
647                 prev_rotate_ref->clear_frame();
648                 overlayer->overlay(prev_rotate_ref,
649                         prev_global_ref,
650                         0,
651                         0,
652                         prev_global_ref->get_w(),
653                         prev_global_ref->get_h(),
654                         dx,
655                         dy,
656                         (float)prev_global_ref->get_w() + dx,
657                         (float)prev_global_ref->get_h() + dy,
658                         1,
659                         TRANSFER_REPLACE,
660                         CUBIC_LINEAR);
661 // Pivot is destination global position
662                 block_x = (int)(prev_rotate_ref->get_w() * 
663                         config.block_x / 
664                         100 +
665                         (float)total_dx / 
666                         OVERSAMPLE);
667                 block_y = (int)(prev_rotate_ref->get_h() * 
668                         config.block_y / 
669                         100 +
670                         (float)total_dy / 
671                         OVERSAMPLE);
672 // Use the global target output as the rotation target input
673                 rotate_target_src->copy_from(global_target_dst);
674 // Transfer current reference frame to previous reference frame for global.
675                 if(config.mode3 != MotionConfig::TRACK_SINGLE)
676                 {
677                         prev_global_ref->copy_from(current_global_ref);
678                         previous_frame_number = get_source_position();
679                 }
680         }
681         else
682         {
683 // Pivot is fixed
684                 block_x = (int)(prev_rotate_ref->get_w() * 
685                         config.block_x / 
686                         100);
687                 block_y = (int)(prev_rotate_ref->get_h() * 
688                         config.block_y / 
689                         100);
690         }
694 // Get rotation
695         if(!motion_rotate)
696                 motion_rotate = new RotateScan(this, 
697                         get_project_smp() + 1, 
698                         get_project_smp() + 1);
700         current_angle = motion_rotate->scan_frame(prev_rotate_ref, 
701                 current_rotate_ref,
702                 block_x,
703                 block_y);
707 // Add current rotation to accumulation
708         if(config.mode3 != MotionConfig::TRACK_SINGLE)
709         {
710 // Retract over time
711                 total_angle = total_angle * (100 - config.return_speed) / 100;
712                 total_angle += current_angle;
714                 if(!config.global)
715                 {
716 // Transfer current reference frame to previous reference frame and update
717 // counter.
718                         prev_rotate_ref->copy_from(current_rotate_ref);
719                         previous_frame_number = get_source_position();
720                 }
721         }
722         else
723         {
724                 total_angle = current_angle;
725         }
727 #ifdef DEBUG
728 printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
729 #endif
732 // Calculate rotation parameters based on requested operation
733         float angle;
734         switch(config.mode1)
735         {
736                 case MotionConfig::NOTHING:
737                         rotate_target_dst->copy_from(rotate_target_src);
738                         break;
739                 case MotionConfig::TRACK:
740                 case MotionConfig::TRACK_PIXEL:
741                         angle = total_angle;
742                         break;
743                 case MotionConfig::STABILIZE:
744                 case MotionConfig::STABILIZE_PIXEL:
745                         angle = -total_angle;
746                         break;
747         }
751         if(config.mode1 != MotionConfig::NOTHING)
752         {
753                 if(!rotate_engine)
754                         rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
755                                 PluginClient::get_project_smp() + 1);
757                 rotate_target_dst->clear_frame();
759 // Determine pivot based on a number of factors.
760                 switch(config.mode1)
761                 {
762                         case MotionConfig::TRACK:
763                         case MotionConfig::TRACK_PIXEL:
764 // Use destination of global tracking.
765                                 rotate_engine->set_pivot(block_x, block_y);
766                                 break;
768                         case MotionConfig::STABILIZE:
769                         case MotionConfig::STABILIZE_PIXEL:
770                                 if(config.global)
771                                 {
772 // Use origin of global stabilize operation
773                                         rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * 
774                                                         config.block_x / 
775                                                         100),
776                                                 (int)(rotate_target_dst->get_h() * 
777                                                         config.block_y / 
778                                                         100));
779                                 
780                                 }
781                                 else
782                                 {
783 // Use origin
784                                         rotate_engine->set_pivot(block_x, block_y);
785                                 }
786                                 break;
787                 }
790                 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
791 // overlayer->overlay(rotate_target_dst,
792 //      prev_rotate_ref,
793 //      0,
794 //      0,
795 //      prev_rotate_ref->get_w(),
796 //      prev_rotate_ref->get_h(),
797 //      0,
798 //      0,
799 //      prev_rotate_ref->get_w(),
800 //      prev_rotate_ref->get_h(),
801 //      1,
802 //      TRANSFER_NORMAL,
803 //      CUBIC_LINEAR);
804 // overlayer->overlay(rotate_target_dst,
805 //      current_rotate_ref,
806 //      0,
807 //      0,
808 //      prev_rotate_ref->get_w(),
809 //      prev_rotate_ref->get_h(),
810 //      0,
811 //      0,
812 //      prev_rotate_ref->get_w(),
813 //      prev_rotate_ref->get_h(),
814 //      1,
815 //      TRANSFER_NORMAL,
816 //      CUBIC_LINEAR);
819         }
832 int MotionMain::process_buffer(VFrame **frame,
833         int64_t start_position,
834         double frame_rate)
836         int need_reconfigure = load_configuration();
837         int color_model = frame[0]->get_color_model();
838         w = frame[0]->get_w();
839         h = frame[0]->get_h();
840         
842 #ifdef DEBUG
843 printf("MotionMain::process_buffer 1 start_position=%lld\n", start_position);
844 #endif
847 // Calculate the source and destination pointers for each of the operations.
848 // Get the layer to track motion in.
849         reference_layer = config.bottom_is_master ?
850                 PluginClient::total_in_buffers - 1 :
851                 0;
852 // Get the layer to apply motion in.
853         target_layer = config.bottom_is_master ?
854                 0 :
855                 PluginClient::total_in_buffers - 1;
858         output_frame = frame[target_layer];
861 // Get the position of previous reference frame.
862         int64_t actual_previous_number;
863 // Skip if match frame not available
864         int skip_current = 0;
867         if(config.mode3 == MotionConfig::TRACK_SINGLE)
868         {
869                 actual_previous_number = config.track_frame;
870                 if(get_direction() == PLAY_REVERSE)
871                         actual_previous_number++;
872                 if(actual_previous_number == start_position)
873                         skip_current = 1;
874         }
875         else
876         {
877                 actual_previous_number = start_position;
878                 if(get_direction() == PLAY_FORWARD)
879                 {
880                         actual_previous_number--;
881                         if(actual_previous_number < get_source_start())
882                                 skip_current = 1;
883                         else
884                         {
885                                 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
886                                 if(keyframe->position > 0 &&
887                                         actual_previous_number < keyframe->position)
888                                         skip_current = 1;
889                         }
890                 }
891                 else
892                 {
893                         actual_previous_number++;
894                         if(actual_previous_number >= get_source_start() + get_total_len())
895                                 skip_current = 1;
896                         else
897                         {
898                                 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
899                                 if(keyframe->position > 0 &&
900                                         actual_previous_number >= keyframe->position)
901                                         skip_current = 1;
902                         }
903                 }
905 // Only count motion since last keyframe
906                 
908         }
911         if(!config.global && !config.rotate) skip_current = 1;
916 // printf("process_realtime %d %lld %lld\n", 
917 // skip_current, 
918 // previous_frame_number, 
919 // actual_previous_number);
920 // Load match frame and reset vectors
921         int need_reload = !skip_current && 
922                 (previous_frame_number != actual_previous_number ||
923                 need_reconfigure);
924         if(need_reload)
925         {
926                 total_dx = 0;
927                 total_dy = 0;
928                 total_angle = 0;
929                 previous_frame_number = actual_previous_number;
930         }
933         if(skip_current)
934         {
935                 total_dx = 0;
936                 total_dy = 0;
937                 current_dx = 0;
938                 current_dy = 0;
939                 total_angle = 0;
940                 current_angle = 0;
941         }
946 // Get the global pointers.  Here we walk through the sequence of events.
947         if(config.global)
948         {
949 // Assume global only.  Global reads previous frame and compares
950 // with current frame to get the current translation.
951 // The center of the search area is fixed in compensate mode or
952 // the user value + the accumulation vector in track mode.
953                 if(!prev_global_ref)
954                         prev_global_ref = new VFrame(0, w, h, color_model);
955                 if(!current_global_ref)
956                         current_global_ref = new VFrame(0, w, h, color_model);
958 // Global loads the current target frame into the src and 
959 // writes it to the dst frame with desired translation.
960                 if(!global_target_src)
961                         global_target_src = new VFrame(0, w, h, color_model);
962                 if(!global_target_dst)
963                         global_target_dst = new VFrame(0, w, h, color_model);
966 // Load the global frames
967                 if(need_reload)
968                 {
969                         read_frame(prev_global_ref, 
970                                 reference_layer, 
971                                 previous_frame_number, 
972                                 frame_rate);
973                 }
975                 read_frame(current_global_ref, 
976                         reference_layer, 
977                         start_position, 
978                         frame_rate);
979                 read_frame(global_target_src,
980                         target_layer,
981                         start_position,
982                         frame_rate);
986 // Global followed by rotate
987                 if(config.rotate)
988                 {
989 // Must translate the previous global reference by the current global
990 // accumulation vector to match the current global reference.
991 // The center of the search area is always the user value + the accumulation
992 // vector.
993                         if(!prev_rotate_ref)
994                                 prev_rotate_ref = new VFrame(0, w, h, color_model);
995 // The current global reference is the current rotation reference.
996                         if(!current_rotate_ref)
997                                 current_rotate_ref = new VFrame(0, w, h, color_model);
998                         current_rotate_ref->copy_from(current_global_ref);
1000 // The global target destination is copied to the rotation target source
1001 // then written to the rotation output with rotation.
1002 // The pivot for the rotation is the center of the search area 
1003 // if we're tracking.
1004 // The pivot is fixed to the user position if we're compensating.
1005                         if(!rotate_target_src)
1006                                 rotate_target_src = new VFrame(0, w, h, color_model);
1007                         if(!rotate_target_dst)
1008                                 rotate_target_dst = new VFrame(0, w,h , color_model);
1009                 }
1010         }
1011         else
1012 // Rotation only
1013         if(config.rotate)
1014         {
1015 // Rotation reads the previous reference frame and compares it with current 
1016 // reference frame.
1017                 if(!prev_rotate_ref)
1018                         prev_rotate_ref = new VFrame(0, w, h, color_model);
1019                 if(!current_rotate_ref)
1020                         current_rotate_ref = new VFrame(0, w, h, color_model);
1022 // Rotation loads target frame to temporary, rotates it, and writes it to the
1023 // target frame.  The pivot is always fixed.
1024                 if(!rotate_target_src)
1025                         rotate_target_src = new VFrame(0, w, h, color_model);
1026                 if(!rotate_target_dst)
1027                         rotate_target_dst = new VFrame(0, w,h , color_model);
1030 // Load the rotate frames
1031                 if(need_reload)
1032                 {
1033                         read_frame(prev_rotate_ref, 
1034                                 reference_layer, 
1035                                 previous_frame_number, 
1036                                 frame_rate);
1037                 }
1038                 read_frame(current_rotate_ref, 
1039                         reference_layer, 
1040                         start_position, 
1041                         frame_rate);
1042                 read_frame(rotate_target_src,
1043                         target_layer,
1044                         start_position,
1045                         frame_rate);
1046         }
1057         if(!skip_current)
1058         {
1059 // Get position change from previous frame to current frame
1060                 if(config.global) process_global();
1061 // Get rotation change from previous frame to current frame
1062                 if(config.rotate) process_rotation();
1063 //frame[target_layer]->copy_from(prev_rotate_ref);
1064 //frame[target_layer]->copy_from(current_rotate_ref);
1065         }
1072 // Transfer the relevant target frame to the output
1073         if(!skip_current)
1074         {
1075                 if(config.rotate)
1076                 {
1077                         frame[target_layer]->copy_from(rotate_target_dst);
1078                 }
1079                 else
1080                 {
1081                         frame[target_layer]->copy_from(global_target_dst);
1082                 }
1083         }
1084         else
1085 // Read the target destination directly
1086         {
1087                 read_frame(frame[target_layer],
1088                         target_layer,
1089                         start_position,
1090                         frame_rate);
1091         }
1093         if(config.draw_vectors)
1094         {
1095                 draw_vectors(frame[target_layer]);
1096         }
1098 #ifdef DEBUG
1099 printf("MotionMain::process_buffer 100\n");
1100 #endif
1101         return 0;
1105 void MotionMain::clamp_scan(int w, 
1106         int h, 
1107         int *block_x1,
1108         int *block_y1,
1109         int *block_x2,
1110         int *block_y2,
1111         int *scan_x1,
1112         int *scan_y1,
1113         int *scan_x2,
1114         int *scan_y2,
1115         int use_absolute)
1117 // printf("MotionMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1118 // w,
1119 // h,
1120 // *block_x1,
1121 // *block_y1,
1122 // *block_x2,
1123 // *block_y2,
1124 // *scan_x1,
1125 // *scan_y1,
1126 // *scan_x2,
1127 // *scan_y2,
1128 // use_absolute);
1130         if(use_absolute)
1131         {
1132 // scan is always out of range before block.
1133                 if(*scan_x1 < 0)
1134                 {
1135                         int difference = -*scan_x1;
1136                         *block_x1 += difference;
1137                         *scan_x1 = 0;
1138                 }
1140                 if(*scan_y1 < 0)
1141                 {
1142                         int difference = -*scan_y1;
1143                         *block_y1 += difference;
1144                         *scan_y1 = 0;
1145                 }
1147                 if(*scan_x2 > w)
1148                 {
1149                         int difference = *scan_x2 - w;
1150                         *block_x2 -= difference;
1151                         *scan_x2 -= difference;
1152                 }
1154                 if(*scan_y2 > h)
1155                 {
1156                         int difference = *scan_y2 - h;
1157                         *block_y2 -= difference;
1158                         *scan_y2 -= difference;
1159                 }
1161                 CLAMP(*scan_x1, 0, w);
1162                 CLAMP(*scan_y1, 0, h);
1163                 CLAMP(*scan_x2, 0, w);
1164                 CLAMP(*scan_y2, 0, h);
1165         }
1166         else
1167         {
1168                 if(*scan_x1 < 0)
1169                 {
1170                         int difference = -*scan_x1;
1171                         *block_x1 += difference;
1172                         *scan_x2 += difference;
1173                         *scan_x1 = 0;
1174                 }
1176                 if(*scan_y1 < 0)
1177                 {
1178                         int difference = -*scan_y1;
1179                         *block_y1 += difference;
1180                         *scan_y2 += difference;
1181                         *scan_y1 = 0;
1182                 }
1184                 if(*scan_x2 - *block_x1 + *block_x2 > w)
1185                 {
1186                         int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1187                         *block_x2 -= difference;
1188                 }
1190                 if(*scan_y2 - *block_y1 + *block_y2 > h)
1191                 {
1192                         int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1193                         *block_y2 -= difference;
1194                 }
1196 //              CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1197 //              CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1198 //              CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1199 //              CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1200         }
1202 // Sanity checks which break the calculation but should never happen if the
1203 // center of the block is inside the frame.
1204         CLAMP(*block_x1, 0, w);
1205         CLAMP(*block_x2, 0, w);
1206         CLAMP(*block_y1, 0, h);
1207         CLAMP(*block_y2, 0, h);
1209 // printf("MotionMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1210 // w,
1211 // h,
1212 // *block_x1,
1213 // *block_y1,
1214 // *block_x2,
1215 // *block_y2,
1216 // *scan_x1,
1217 // *scan_y1,
1218 // *scan_x2,
1219 // *scan_y2,
1220 // use_absolute);
1225 void MotionMain::draw_vectors(VFrame *frame)
1227         int w = frame->get_w();
1228         int h = frame->get_h();
1229         int global_x1, global_y1;
1230         int global_x2, global_y2;
1231         int block_x, block_y;
1232         int block_w, block_h;
1233         int block_x1, block_y1;
1234         int block_x2, block_y2;
1235         int block_x3, block_y3;
1236         int block_x4, block_y4;
1237         int search_w, search_h;
1238         int search_x1, search_y1;
1239         int search_x2, search_y2;
1240         int search_x3, search_y3;
1241         int search_x4, search_y4;
1243         if(config.global)
1244         {
1245 // Get vector
1246 // Start of vector is center of previous block.
1247 // End of vector is total accumulation.
1248                 if(config.mode3 == MotionConfig::TRACK_SINGLE)
1249                 {
1250                         global_x1 = (int64_t)(config.block_x * 
1251                                 w / 
1252                                 100);
1253                         global_y1 = (int64_t)(config.block_y *
1254                                 h / 
1255                                 100);
1256                         global_x2 = global_x1 + total_dx / OVERSAMPLE;
1257                         global_y2 = global_y1 + total_dy / OVERSAMPLE;
1258 //printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1259                 }
1260                 else
1261 // Start of vector is center of previous block.
1262 // End of vector is current change.
1263                 if(config.mode3 == MotionConfig::PREVIOUS_SAME_BLOCK)
1264                 {
1265                         global_x1 = (int64_t)(config.block_x * 
1266                                 w / 
1267                                 100);
1268                         global_y1 = (int64_t)(config.block_y *
1269                                 h / 
1270                                 100);
1271                         global_x2 = global_x1 + current_dx / OVERSAMPLE;
1272                         global_y2 = global_y1 + current_dy / OVERSAMPLE;
1273                 }
1274                 else
1275                 {
1276                         global_x1 = (int64_t)(config.block_x * 
1277                                 w / 
1278                                 100 + 
1279                                 (total_dx - current_dx) / 
1280                                 OVERSAMPLE);
1281                         global_y1 = (int64_t)(config.block_y *
1282                                 h / 
1283                                 100 +
1284                                 (total_dy - current_dy) /
1285                                 OVERSAMPLE);
1286                         global_x2 = (int64_t)(config.block_x * 
1287                                 w / 
1288                                 100 + 
1289                                 total_dx / 
1290                                 OVERSAMPLE);
1291                         global_y2 = (int64_t)(config.block_y *
1292                                 h / 
1293                                 100 +
1294                                 total_dy /
1295                                 OVERSAMPLE);
1296                 }
1298                 block_x = global_x1;
1299                 block_y = global_y1;
1300                 block_w = config.global_block_w * w / 100;
1301                 block_h = config.global_block_h * h / 100;
1302                 block_x1 = block_x - block_w / 2;
1303                 block_y1 = block_y - block_h / 2;
1304                 block_x2 = block_x + block_w / 2;
1305                 block_y2 = block_y + block_h / 2;
1306                 search_w = config.global_range_w * w / 100;
1307                 search_h = config.global_range_h * h / 100;
1308                 search_x1 = block_x1 - search_w / 2;
1309                 search_y1 = block_y1 - search_h / 2;
1310                 search_x2 = block_x2 + search_w / 2;
1311                 search_y2 = block_y2 + search_h / 2;
1313 // printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1314 // global_x1,
1315 // global_y1,
1316 // block_w,
1317 // block_h,
1318 // block_x1,
1319 // block_y1,
1320 // block_x2,
1321 // block_y2,
1322 // search_x1,
1323 // search_y1,
1324 // search_x2,
1325 // search_y2);
1327                 clamp_scan(w, 
1328                         h, 
1329                         &block_x1,
1330                         &block_y1,
1331                         &block_x2,
1332                         &block_y2,
1333                         &search_x1,
1334                         &search_y1,
1335                         &search_x2,
1336                         &search_y2,
1337                         1);
1339 // Vector
1340                 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1342 // Macroblock
1343                 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1344                 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1345                 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1346                 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1349 // Search area
1350                 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1351                 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1352                 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1353                 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1355 // Block should be endpoint of motion
1356                 if(config.rotate)
1357                 {
1358                         block_x = global_x2;
1359                         block_y = global_y2;
1360                 }
1361         }
1362         else
1363         {
1364                 block_x = (int64_t)(config.block_x * w / 100);
1365                 block_y = (int64_t)(config.block_y * h / 100);
1366         }
1368         block_w = config.rotation_block_w * w / 100;
1369         block_h = config.rotation_block_h * h / 100;
1370         if(config.rotate)
1371         {
1372                 float angle = total_angle * 2 * M_PI / 360;
1373                 double base_angle1 = atan((float)block_h / block_w);
1374                 double base_angle2 = atan((float)block_w / block_h);
1375                 double target_angle1 = base_angle1 + angle;
1376                 double target_angle2 = base_angle2 + angle;
1377                 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1378                 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1379                 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1380                 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1381                 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1382                 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1383                 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1384                 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1385                 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1387                 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1388                 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1389                 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1390                 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1393 // Center
1394                 if(!config.global)
1395                 {
1396                         draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1397                         draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1398                 }
1399         }
1404 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
1406         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1408 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1409 { \
1410         type **rows = (type**)frame->get_rows(); \
1411         rows[y][x * components] = max - rows[y][x * components]; \
1412         if(!do_yuv) \
1413         { \
1414                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1415                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1416         } \
1417         else \
1418         { \
1419                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1420                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1421         } \
1422         if(components == 4) \
1423                 rows[y][x * components + 3] = max; \
1427         switch(frame->get_color_model())
1428         {
1429                 case BC_RGB888:
1430                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1431                         break;
1432                 case BC_RGBA8888:
1433                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1434                         break;
1435                 case BC_RGB_FLOAT:
1436                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1437                         break;
1438                 case BC_RGBA_FLOAT:
1439                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1440                         break;
1441                 case BC_YUV888:
1442                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1443                         break;
1444                 case BC_YUVA8888:
1445                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1446                         break;
1447                 case BC_RGB161616:
1448                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1449                         break;
1450                 case BC_YUV161616:
1451                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1452                         break;
1453                 case BC_RGBA16161616:
1454                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1455                         break;
1456                 case BC_YUVA16161616:
1457                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1458                         break;
1459         }
1463 void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1465         int w = labs(x2 - x1);
1466         int h = labs(y2 - y1);
1467 //printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1469         if(!w && !h)
1470         {
1471                 draw_pixel(frame, x1, y1);
1472         }
1473         else
1474         if(w > h)
1475         {
1476 // Flip coordinates so x1 < x2
1477                 if(x2 < x1)
1478                 {
1479                         y2 ^= y1;
1480                         y1 ^= y2;
1481                         y2 ^= y1;
1482                         x1 ^= x2;
1483                         x2 ^= x1;
1484                         x1 ^= x2;
1485                 }
1486                 int numerator = y2 - y1;
1487                 int denominator = x2 - x1;
1488                 for(int i = x1; i < x2; i++)
1489                 {
1490                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1491                         draw_pixel(frame, i, y);
1492                 }
1493         }
1494         else
1495         {
1496 // Flip coordinates so y1 < y2
1497                 if(y2 < y1)
1498                 {
1499                         y2 ^= y1;
1500                         y1 ^= y2;
1501                         y2 ^= y1;
1502                         x1 ^= x2;
1503                         x2 ^= x1;
1504                         x1 ^= x2;
1505                 }
1506                 int numerator = x2 - x1;
1507                 int denominator = y2 - y1;
1508                 for(int i = y1; i < y2; i++)
1509                 {
1510                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1511                         draw_pixel(frame, x, i);
1512                 }
1513         }
1514 //printf("MotionMain::draw_line 2\n");
1517 #define ARROW_SIZE 10
1518 void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1520         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1521         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1522         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1523         int x3;
1524         int y3;
1525         int x4;
1526         int y4;
1527         if(x2 < x1)
1528         {
1529                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1530                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1531                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1532                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1533         }
1534         else
1535         {
1536                 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1537                 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1538                 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1539                 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1540         }
1542 // Main vector
1543         draw_line(frame, x1, y1, x2, y2);
1544 //      draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1546 // Arrow line
1547         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1548 //      draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1549 // Arrow line
1550         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1551 //      draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1557 #define ABS_DIFF(type, temp_type, multiplier, components) \
1558 { \
1559         temp_type result_temp = 0; \
1560         for(int i = 0; i < h; i++) \
1561         { \
1562                 type *prev_row = (type*)prev_ptr; \
1563                 type *current_row = (type*)current_ptr; \
1564                 for(int j = 0; j < w; j++) \
1565                 { \
1566                         for(int k = 0; k < 3; k++) \
1567                         { \
1568                                 temp_type difference; \
1569                                 difference = *prev_row++ - *current_row++; \
1570                                 if(difference < 0) \
1571                                         result_temp -= difference; \
1572                                 else \
1573                                         result_temp += difference; \
1574                         } \
1575                         if(components == 4) \
1576                         { \
1577                                 prev_row++; \
1578                                 current_row++; \
1579                         } \
1580                 } \
1581                 prev_ptr += row_bytes; \
1582                 current_ptr += row_bytes; \
1583         } \
1584         result = (int64_t)(result_temp * multiplier); \
1587 int64_t MotionMain::abs_diff(unsigned char *prev_ptr,
1588         unsigned char *current_ptr,
1589         int row_bytes,
1590         int w,
1591         int h,
1592         int color_model)
1594         int64_t result = 0;
1595         switch(color_model)
1596         {
1597                 case BC_RGB888:
1598                         ABS_DIFF(unsigned char, int64_t, 1, 3)
1599                         break;
1600                 case BC_RGBA8888:
1601                         ABS_DIFF(unsigned char, int64_t, 1, 4)
1602                         break;
1603                 case BC_RGB_FLOAT:
1604                         ABS_DIFF(float, double, 0x10000, 3)
1605                         break;
1606                 case BC_RGBA_FLOAT:
1607                         ABS_DIFF(float, double, 0x10000, 4)
1608                         break;
1609                 case BC_YUV888:
1610                         ABS_DIFF(unsigned char, int64_t, 1, 3)
1611                         break;
1612                 case BC_YUVA8888:
1613                         ABS_DIFF(unsigned char, int64_t, 1, 4)
1614                         break;
1615                 case BC_YUV161616:
1616                         ABS_DIFF(uint16_t, int64_t, 1, 3)
1617                         break;
1618                 case BC_YUVA16161616:
1619                         ABS_DIFF(uint16_t, int64_t, 1, 4)
1620                         break;
1621         }
1622         return result;
1627 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1628 { \
1629         temp_type result_temp = 0; \
1630         temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1631         temp_type y1_fraction = 0x100 - y2_fraction; \
1632         temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1633         temp_type x1_fraction = 0x100 - x2_fraction; \
1634         for(int i = 0; i < h_sub; i++) \
1635         { \
1636                 type *prev_row1 = (type*)prev_ptr; \
1637                 type *prev_row2 = (type*)prev_ptr + components; \
1638                 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1639                 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1640                 type *current_row = (type*)current_ptr; \
1641                 for(int j = 0; j < w_sub; j++) \
1642                 { \
1643                         for(int k = 0; k < 3; k++) \
1644                         { \
1645                                 temp_type difference; \
1646                                 temp_type prev_value = \
1647                                         (*prev_row1++ * x1_fraction * y1_fraction + \
1648                                         *prev_row2++ * x2_fraction * y1_fraction + \
1649                                         *prev_row3++ * x1_fraction * y2_fraction + \
1650                                         *prev_row4++ * x2_fraction * y2_fraction) / \
1651                                         0x100 / 0x100; \
1652                                 temp_type current_value = *current_row++; \
1653                                 difference = prev_value - current_value; \
1654                                 if(difference < 0) \
1655                                         result_temp -= difference; \
1656                                 else \
1657                                         result_temp += difference; \
1658                         } \
1660                         if(components == 4) \
1661                         { \
1662                                 prev_row1++; \
1663                                 prev_row2++; \
1664                                 prev_row3++; \
1665                                 prev_row4++; \
1666                                 current_row++; \
1667                         } \
1668                 } \
1669                 prev_ptr += row_bytes; \
1670                 current_ptr += row_bytes; \
1671         } \
1672         result = (int64_t)(result_temp * multiplier); \
1678 int64_t MotionMain::abs_diff_sub(unsigned char *prev_ptr,
1679         unsigned char *current_ptr,
1680         int row_bytes,
1681         int w,
1682         int h,
1683         int color_model,
1684         int sub_x,
1685         int sub_y)
1687         int h_sub = h - 1;
1688         int w_sub = w - 1;
1689         int64_t result = 0;
1691         switch(color_model)
1692         {
1693                 case BC_RGB888:
1694                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1695                         break;
1696                 case BC_RGBA8888:
1697                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1698                         break;
1699                 case BC_RGB_FLOAT:
1700                         ABS_DIFF_SUB(float, double, 0x10000, 3)
1701                         break;
1702                 case BC_RGBA_FLOAT:
1703                         ABS_DIFF_SUB(float, double, 0x10000, 4)
1704                         break;
1705                 case BC_YUV888:
1706                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1707                         break;
1708                 case BC_YUVA8888:
1709                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1710                         break;
1711                 case BC_YUV161616:
1712                         ABS_DIFF_SUB(uint16_t, int64_t, 1, 3)
1713                         break;
1714                 case BC_YUVA16161616:
1715                         ABS_DIFF_SUB(uint16_t, int64_t, 1, 4)
1716                         break;
1717         }
1718         return result;
1725 MotionScanPackage::MotionScanPackage()
1726  : LoadPackage()
1728         valid = 1;
1736 MotionScanUnit::MotionScanUnit(MotionScan *server, 
1737         MotionMain *plugin)
1738  : LoadClient(server)
1740         this->plugin = plugin;
1741         this->server = server;
1742         cache_lock = new Mutex("MotionScanUnit::cache_lock");
1745 MotionScanUnit::~MotionScanUnit()
1747         delete cache_lock;
1752 void MotionScanUnit::process_package(LoadPackage *package)
1754         MotionScanPackage *pkg = (MotionScanPackage*)package;
1755         int w = server->current_frame->get_w();
1756         int h = server->current_frame->get_h();
1757         int color_model = server->current_frame->get_color_model();
1758         int pixel_size = cmodel_calculate_pixelsize(color_model);
1759         int row_bytes = server->current_frame->get_bytes_per_line();
1772 // Single pixel
1773         if(!server->subpixel)
1774         {
1775                 int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1));
1776                 int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1));
1778 // Try cache
1779                 pkg->difference1 = server->get_cache(search_x, search_y);
1780                 if(pkg->difference1 < 0)
1781                 {
1782 //printf("MotionScanUnit::process_package 1 %d %d\n", 
1783 //search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1);
1784 // Pointers to first pixel in each block
1785                         unsigned char *prev_ptr = server->previous_frame->get_rows()[
1786                                 search_y] +     
1787                                 search_x * pixel_size;
1788                         unsigned char *current_ptr = server->current_frame->get_rows()[
1789                                 pkg->block_y1] +
1790                                 pkg->block_x1 * pixel_size;
1791 // Scan block
1792                         pkg->difference1 = plugin->abs_diff(prev_ptr,
1793                                 current_ptr,
1794                                 row_bytes,
1795                                 pkg->block_x2 - pkg->block_x1,
1796                                 pkg->block_y2 - pkg->block_y1,
1797                                 color_model);
1798 //printf("MotionScanUnit::process_package 2\n");
1799                         server->put_cache(search_x, search_y, pkg->difference1);
1800                 }
1801         }
1809         else
1818 // Sub pixel
1819         {
1820                 int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1;
1821                 int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1;
1823                 if(plugin->config.horizontal_only)
1824                 {
1825                         sub_y = 0;
1826                 }
1828                 if(plugin->config.vertical_only)
1829                 {
1830                         sub_x = 0;
1831                 }
1833                 int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE;
1834                 int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE;
1835                 sub_x %= OVERSAMPLE;
1836                 sub_y %= OVERSAMPLE;
1839                 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1840                         search_y] +
1841                         search_x * pixel_size;
1842                 unsigned char *current_ptr = server->current_frame->get_rows()[
1843                         pkg->block_y1] +
1844                         pkg->block_x1 * pixel_size;
1846 // With subpixel, there are two ways to compare each position, one by shifting
1847 // the previous frame and two by shifting the current frame.
1848                 pkg->difference1 = plugin->abs_diff_sub(prev_ptr,
1849                         current_ptr,
1850                         row_bytes,
1851                         pkg->block_x2 - pkg->block_x1,
1852                         pkg->block_y2 - pkg->block_y1,
1853                         color_model,
1854                         sub_x,
1855                         sub_y);
1856                 pkg->difference2 = plugin->abs_diff_sub(current_ptr,
1857                         prev_ptr,
1858                         row_bytes,
1859                         pkg->block_x2 - pkg->block_x1,
1860                         pkg->block_y2 - pkg->block_y1,
1861                         color_model,
1862                         sub_x,
1863                         sub_y);
1864 // printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
1865 // sub_x,
1866 // sub_y,
1867 // search_x,
1868 // search_y,
1869 // pkg->difference1,
1870 // pkg->difference2);
1871         }
1887 int64_t MotionScanUnit::get_cache(int x, int y)
1889         int64_t result = -1;
1890         cache_lock->lock("MotionScanUnit::get_cache");
1891         for(int i = 0; i < cache.total; i++)
1892         {
1893                 MotionScanCache *ptr = cache.values[i];
1894                 if(ptr->x == x && ptr->y == y)
1895                 {
1896                         result = ptr->difference;
1897                         break;
1898                 }
1899         }
1900         cache_lock->unlock();
1901         return result;
1904 void MotionScanUnit::put_cache(int x, int y, int64_t difference)
1906         MotionScanCache *ptr = new MotionScanCache(x, y, difference);
1907         cache_lock->lock("MotionScanUnit::put_cache");
1908         cache.append(ptr);
1909         cache_lock->unlock();
1922 MotionScan::MotionScan(MotionMain *plugin, 
1923         int total_clients,
1924         int total_packages)
1925  : LoadServer(
1926 //1, 1 
1927 total_clients, total_packages 
1930         this->plugin = plugin;
1931         cache_lock = new Mutex("MotionScan::cache_lock");
1934 MotionScan::~MotionScan()
1936         delete cache_lock;
1940 void MotionScan::init_packages()
1942 // Set package coords
1943         for(int i = 0; i < get_total_packages(); i++)
1944         {
1945                 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
1947                 pkg->block_x1 = block_x1;
1948                 pkg->block_x2 = block_x2;
1949                 pkg->block_y1 = block_y1;
1950                 pkg->block_y2 = block_y2;
1951                 pkg->scan_x1 = scan_x1;
1952                 pkg->scan_x2 = scan_x2;
1953                 pkg->scan_y1 = scan_y1;
1954                 pkg->scan_y2 = scan_y2;
1955                 pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps;
1956                 pkg->difference1 = 0;
1957                 pkg->difference2 = 0;
1958                 pkg->dx = 0;
1959                 pkg->dy = 0;
1960                 pkg->valid = 1;
1961         }
1964 LoadClient* MotionScan::new_client()
1966         return new MotionScanUnit(this, plugin);
1969 LoadPackage* MotionScan::new_package()
1971         return new MotionScanPackage;
1975 void MotionScan::scan_frame(VFrame *previous_frame,
1976         VFrame *current_frame)
1978         this->previous_frame = previous_frame;
1979         this->current_frame = current_frame;
1980         subpixel = 0;
1982         cache.remove_all_objects();
1985 // Single macroblock
1986         int w = current_frame->get_w();
1987         int h = current_frame->get_h();
1989 // Initial search parameters
1990         int scan_w = w * plugin->config.global_range_w / 100;
1991         int scan_h = h * plugin->config.global_range_h / 100;
1992         int block_w = w * plugin->config.global_block_w / 100;
1993         int block_h = h * plugin->config.global_block_h / 100;
1995 // Location of block in previous frame
1996         block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2);
1997         block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2);
1998         block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2);
1999         block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2);
2001 // Offset to location of previous block.  This offset needn't be very accurate
2002 // since it's the offset of the previous image and current image we want.
2003         if(plugin->config.mode3 == MotionConfig::TRACK_PREVIOUS)
2004         {
2005                 block_x1 += plugin->total_dx / OVERSAMPLE;
2006                 block_y1 += plugin->total_dy / OVERSAMPLE;
2007                 block_x2 += plugin->total_dx / OVERSAMPLE;
2008                 block_y2 += plugin->total_dy / OVERSAMPLE;
2009         }
2011         skip = 0;
2013         switch(plugin->config.mode2)
2014         {
2015 // Don't calculate
2016                 case MotionConfig::NO_CALCULATE:
2017                         dx_result = 0;
2018                         dy_result = 0;
2019                         skip = 1;
2020                         break;
2022                 case MotionConfig::LOAD:
2023                 {
2024 // Load result from disk
2025                         char string[BCTEXTLEN];
2026                         sprintf(string, "%s%06d", MOTION_FILE, plugin->get_source_position());
2027                         FILE *input = fopen(string, "r");
2028                         if(input)
2029                         {
2030                                 fscanf(input, 
2031                                         "%d %d", 
2032                                         &dx_result,
2033                                         &dy_result);
2034                                 fclose(input);
2035                                 skip = 1;
2036                         }
2037                         break;
2038                 }
2040 // Scan from scratch
2041                 default:
2042                         skip = 0;
2043                         break;
2044         }
2046 // Perform scan
2047         if(!skip)
2048         {
2049 // Location of block in current frame
2050                 int x_result = block_x1;
2051                 int y_result = block_y1;
2053 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2054 // block_x1 + block_w / 2,
2055 // block_y1 + block_h / 2,
2056 // block_w,
2057 // block_h,
2058 // block_x1,
2059 // block_y1,
2060 // block_x2,
2061 // block_y2);
2063                 while(1)
2064                 {
2065                         scan_x1 = x_result - scan_w / 2;
2066                         scan_y1 = y_result - scan_h / 2;
2067                         scan_x2 = x_result + scan_w / 2;
2068                         scan_y2 = y_result + scan_h / 2;
2072 // Zero out requested values
2073                         if(plugin->config.horizontal_only)
2074                         {
2075                                 scan_y1 = block_y1;
2076                                 scan_y2 = block_y1 + 1;
2077                         }
2078                         if(plugin->config.vertical_only)
2079                         {
2080                                 scan_x1 = block_x1;
2081                                 scan_x2 = block_x1 + 1;
2082                         }
2084 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2085 // block_x1,
2086 // block_y1,
2087 // block_x2,
2088 // block_y2,
2089 // scan_x1,
2090 // scan_y1,
2091 // scan_x2,
2092 // scan_y2);
2093 // Clamp the block coords before the scan so we get useful scan coords.
2094                         MotionMain::clamp_scan(w, 
2095                                 h, 
2096                                 &block_x1,
2097                                 &block_y1,
2098                                 &block_x2,
2099                                 &block_y2,
2100                                 &scan_x1,
2101                                 &scan_y1,
2102                                 &scan_x2,
2103                                 &scan_y2,
2104                                 0);
2105 // printf("MotionScan::scan_frame 1\n    block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n    scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n    x_result=%d y_result=%d\n", 
2106 // block_x1,
2107 // block_y1,
2108 // block_x2,
2109 // block_y2,
2110 // scan_x1, 
2111 // scan_y1, 
2112 // scan_x2, 
2113 // scan_y2, 
2114 // x_result, 
2115 // y_result);
2118 // Give up if invalid coords.
2119                         if(scan_y2 <= scan_y1 ||
2120                                 scan_x2 <= scan_x1 ||
2121                                 block_x2 <= block_x1 ||
2122                                 block_y2 <= block_y1)
2123                                 break;
2125 // For subpixel, the top row and left column are skipped
2126                         if(subpixel)
2127                         {
2128                                 if(plugin->config.horizontal_only ||
2129                                         plugin->config.vertical_only)
2130                                 {
2131                                         total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE;
2132                                 }
2133                                 else
2134                                 {
2135                                         total_pixels = 4 * OVERSAMPLE;
2136                                 }
2138                                 total_steps = total_pixels;
2140                                 set_package_count(total_steps);
2141                                 process_packages();
2143 // Get least difference
2144                                 int64_t min_difference = -1;
2145                                 for(int i = 0; i < get_total_packages(); i++)
2146                                 {
2147                                         MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
2148                                         if(pkg->difference1 < min_difference || min_difference == -1)
2149                                         {
2150                                                 min_difference = pkg->difference1;
2152                                                 if(plugin->config.vertical_only)
2153                                                         x_result = scan_x1 * OVERSAMPLE;
2154                                                 else
2155                                                         x_result = scan_x1 * OVERSAMPLE + 
2156                                                                 (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1;
2157                                                 
2158                                                 if(plugin->config.horizontal_only)
2159                                                         y_result = scan_y1 * OVERSAMPLE;
2160                                                 else
2161                                                         y_result = scan_y1 * OVERSAMPLE + 
2162                                                                 (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1;
2165 // Fill in results
2166                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2167                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2168                                         }
2170                                         if(pkg->difference2 < min_difference)
2171                                         {
2172                                                 min_difference = pkg->difference2;
2174                                                 if(plugin->config.vertical_only)
2175                                                         x_result = scan_x1 * OVERSAMPLE;
2176                                                 else
2177                                                         x_result = scan_x2 * OVERSAMPLE -
2178                                                                 ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1);
2180                                                 if(plugin->config.horizontal_only)
2181                                                         y_result = scan_y1 * OVERSAMPLE;
2182                                                 else
2183                                                         y_result = scan_y2 * OVERSAMPLE -
2184                                                                 ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1);
2186                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2187                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2188                                         }
2189                                 }
2191 //printf("MotionScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result);
2192                                 break;
2193                         }
2194                         else
2195                         {
2196                                 total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
2197                                 total_steps = MIN(plugin->config.global_positions, total_pixels);
2199                                 set_package_count(total_steps);
2200                                 process_packages();
2202 // Get least difference
2203                                 int64_t min_difference = -1;
2204                                 for(int i = 0; i < get_total_packages(); i++)
2205                                 {
2206                                         MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
2207                                         if(pkg->difference1 < min_difference || min_difference == -1)
2208                                         {
2209                                                 min_difference = pkg->difference1;
2210                                                 x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1));
2211                                                 y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1));
2212                                                 x_result *= OVERSAMPLE;
2213                                                 y_result *= OVERSAMPLE;
2214                                         }
2215                                 }
2217 // printf("MotionScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n",
2218 // total_steps, 
2219 // total_pixels,
2220 // subpixel);
2221 // 
2222 // printf("     scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n",
2223 // scan_w,
2224 // scan_h, 
2225 // scan_x1,
2226 // scan_y1,
2227 // scan_x2,
2228 // scan_y2);
2229 // 
2230 // printf("MotionScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n", 
2231 // block_x1, 
2232 // block_y1, 
2233 // block_x2,
2234 // block_y2,
2235 // (float)x_result / 4, 
2236 // (float)y_result / 4);
2239 // If a new search is required, rescale results back to pixels.
2240                                 if(total_steps >= total_pixels)
2241                                 {
2242 // Single pixel accuracy reached.  Now do exhaustive subpixel search.
2243                                         if(plugin->config.mode1 == MotionConfig::STABILIZE ||
2244                                                 plugin->config.mode1 == MotionConfig::TRACK ||
2245                                                 plugin->config.mode1 == MotionConfig::NOTHING)
2246                                         {
2247                                                 x_result /= OVERSAMPLE;
2248                                                 y_result /= OVERSAMPLE;
2249                                                 scan_w = 2;
2250                                                 scan_h = 2;
2251                                                 subpixel = 1;
2252                                         }
2253                                         else
2254                                         {
2255 // Fill in results and quit
2256                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2257                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2258                                                 break;
2259                                         }
2260                                 }
2261                                 else
2262 // Reduce scan area and try again
2263                                 {
2264                                         scan_w = (scan_x2 - scan_x1) / 2;
2265                                         scan_h = (scan_y2 - scan_y1) / 2;
2266                                         x_result /= OVERSAMPLE;
2267                                         y_result /= OVERSAMPLE;
2268                                 }
2269                         }
2270                 }
2272                 dx_result *= -1;
2273                 dy_result *= -1;
2274         }
2281 // Write results
2282         if(plugin->config.mode2 == MotionConfig::SAVE)
2283         {
2284                 char string[BCTEXTLEN];
2285                 sprintf(string, 
2286                         "%s%06d", 
2287                         MOTION_FILE, 
2288                         plugin->get_source_position());
2289                 FILE *output = fopen(string, "w");
2290                 if(output)
2291                 {
2292                         fprintf(output, 
2293                                 "%d %d\n",
2294                                 dx_result,
2295                                 dy_result);
2296                         fclose(output);
2297                 }
2298                 else
2299                 {
2300                         perror("MotionScan::scan_frame SAVE 1");
2301                 }
2302         }
2304 #ifdef DEBUG
2305 printf("MotionScan::scan_frame 10 dx=%.2f dy=%.2f\n", 
2306 (float)this->dx_result / OVERSAMPLE,
2307 (float)this->dy_result / OVERSAMPLE);
2308 #endif
2327 int64_t MotionScan::get_cache(int x, int y)
2329         int64_t result = -1;
2330         cache_lock->lock("MotionScan::get_cache");
2331         for(int i = 0; i < cache.total; i++)
2332         {
2333                 MotionScanCache *ptr = cache.values[i];
2334                 if(ptr->x == x && ptr->y == y)
2335                 {
2336                         result = ptr->difference;
2337                         break;
2338                 }
2339         }
2340         cache_lock->unlock();
2341         return result;
2344 void MotionScan::put_cache(int x, int y, int64_t difference)
2346         MotionScanCache *ptr = new MotionScanCache(x, y, difference);
2347         cache_lock->lock("MotionScan::put_cache");
2348         cache.append(ptr);
2349         cache_lock->unlock();
2356 MotionScanCache::MotionScanCache(int x, int y, int64_t difference)
2358         this->x = x;
2359         this->y = y;
2360         this->difference = difference;
2376 RotateScanPackage::RotateScanPackage()
2381 RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin)
2382  : LoadClient(server)
2384         this->server = server;
2385         this->plugin = plugin;
2386         rotater = 0;
2387         temp = 0;
2390 RotateScanUnit::~RotateScanUnit()
2392         delete rotater;
2393         delete temp;
2396 void RotateScanUnit::process_package(LoadPackage *package)
2398         if(server->skip) return;
2399         RotateScanPackage *pkg = (RotateScanPackage*)package;
2401         if((pkg->difference = server->get_cache(pkg->angle)) < 0)
2402         {
2403 //printf("RotateScanUnit::process_package 1\n");
2404                 int color_model = server->previous_frame->get_color_model();
2405                 int pixel_size = cmodel_calculate_pixelsize(color_model);
2406                 int row_bytes = server->previous_frame->get_bytes_per_line();
2408                 if(!rotater)
2409                         rotater = new AffineEngine(1, 1);
2410                 if(!temp) temp = new VFrame(0,
2411                         server->previous_frame->get_w(),
2412                         server->previous_frame->get_h(),
2413                         color_model);
2416 // Rotate original block size
2417                 rotater->set_viewport(server->block_x1, 
2418                         server->block_y1,
2419                         server->block_x2 - server->block_x1,
2420                         server->block_y2 - server->block_y1);
2421                 rotater->set_pivot(server->block_x, server->block_y);
2422 //pkg->angle = 2;
2423                 rotater->rotate(temp,
2424                         server->previous_frame,
2425                         pkg->angle);
2427 // Scan reduced block size
2428 //plugin->output_frame->copy_from(server->current_frame);
2429 //plugin->output_frame->copy_from(temp);
2430                 pkg->difference = plugin->abs_diff(
2431                         temp->get_rows()[server->scan_y] + server->scan_x * pixel_size,
2432                         server->current_frame->get_rows()[server->scan_y] + server->scan_x * pixel_size,
2433                         row_bytes,
2434                         server->scan_w,
2435                         server->scan_h,
2436                         color_model);
2437                 server->put_cache(pkg->angle, pkg->difference);
2439 // 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", 
2440 // server->block_x1, 
2441 // server->block_y1,
2442 // server->block_x2 - server->block_x1,
2443 // server->block_y2 - server->block_y1,
2444 // server->block_x,
2445 // server->block_y,
2446 // pkg->angle, 
2447 // server->scan_w,
2448 // server->scan_h,
2449 // pkg->difference);
2450         }
2474 RotateScan::RotateScan(MotionMain *plugin, 
2475         int total_clients, 
2476         int total_packages)
2477  : LoadServer(
2478 //1, 1 
2479 total_clients, total_packages 
2482         this->plugin = plugin;
2483         cache_lock = new Mutex("RotateScan::cache_lock");
2487 RotateScan::~RotateScan()
2489         delete cache_lock;
2492 void RotateScan::init_packages()
2494         for(int i = 0; i < get_total_packages(); i++)
2495         {
2496                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
2497                 pkg->angle = i * 
2498                         (scan_angle2 - scan_angle1) / 
2499                         (total_steps - 1) + 
2500                         scan_angle1;
2501         }
2504 LoadClient* RotateScan::new_client()
2506         return new RotateScanUnit(this, plugin);
2509 LoadPackage* RotateScan::new_package()
2511         return new RotateScanPackage;
2515 float RotateScan::scan_frame(VFrame *previous_frame,
2516         VFrame *current_frame,
2517         int block_x,
2518         int block_y)
2520         skip = 0;
2521         this->block_x = block_x;
2522         this->block_y = block_y;
2524         switch(plugin->config.mode2)
2525         {
2526                 case MotionConfig::NO_CALCULATE:
2527                         result = 0;
2528                         skip = 1;
2529                         break;
2531                 case MotionConfig::LOAD:
2532                 {
2533                         char string[BCTEXTLEN];
2534                         sprintf(string, "%s%06d", ROTATION_FILE, plugin->get_source_position());
2535                         FILE *input = fopen(string, "r");
2536                         if(input)
2537                         {
2538                                 fscanf(input, "%f", &result);
2539                                 fclose(input);
2540                                 skip = 1;
2541                         }
2542                         else
2543                         {
2544                                 perror("RotateScan::scan_frame LOAD");
2545                         }
2546                         break;
2547                 }
2548         }
2557         this->previous_frame = previous_frame;
2558         this->current_frame = current_frame;
2559         int w = current_frame->get_w();
2560         int h = current_frame->get_h();
2561         int block_w = w * plugin->config.rotation_block_w / 100;
2562         int block_h = h * plugin->config.rotation_block_h / 100;
2564         if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
2565         if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
2566         if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
2567         if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
2569         block_x1 = this->block_x - block_w / 2;
2570         block_x2 = this->block_x + block_w / 2;
2571         block_y1 = this->block_y - block_h / 2;
2572         block_y2 = this->block_y + block_h / 2;
2575 // Calculate the maximum area available to scan after rotation.
2576 // Must be calculated from the starting range because of cache.
2577 // Get coords of rectangle after rotation.
2578         double center_x = this->block_x;
2579         double center_y = this->block_y;
2580         double max_angle = plugin->config.rotation_range;
2581         double base_angle1 = atan((float)block_h / block_w);
2582         double base_angle2 = atan((float)block_w / block_h);
2583         double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
2584         double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
2585         double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
2586         double x1 = center_x - cos(target_angle1) * radius;
2587         double y1 = center_y - sin(target_angle1) * radius;
2588         double x2 = center_x + sin(target_angle2) * radius;
2589         double y2 = center_y - cos(target_angle2) * radius;
2590         double x3 = center_x - sin(target_angle2) * radius;
2591         double y3 = center_y + cos(target_angle2) * radius;
2593 // Track top edge to find greatest area.
2594         double max_area1 = 0;
2595         double max_x1 = 0;
2596         double max_y1 = 0;
2597         for(double x = x1; x < x2; x++)
2598         {
2599                 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
2600                 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
2601                 {
2602                         double area = fabs(x - center_x) * fabs(y - center_y);
2603                         if(area > max_area1)
2604                         {
2605                                 max_area1 = area;
2606                                 max_x1 = x;
2607                                 max_y1 = y;
2608                         }
2609                 }
2610         }
2612 // Track left edge to find greatest area.
2613         double max_area2 = 0;
2614         double max_x2 = 0;
2615         double max_y2 = 0;
2616         for(double y = y1; y < y3; y++)
2617         {
2618                 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
2619                 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
2620                 {
2621                         double area = fabs(x - center_x) * fabs(y - center_y);
2622                         if(area > max_area2)
2623                         {
2624                                 max_area2 = area;
2625                                 max_x2 = x;
2626                                 max_y2 = y;
2627                         }
2628                 }
2629         }
2631         double max_x, max_y;
2632         max_x = max_x2;
2633         max_y = max_y1;
2635 // Get reduced scan coords
2636         scan_w = (int)(fabs(max_x - center_x) * 2);
2637         scan_h = (int)(fabs(max_y - center_y) * 2);
2638         scan_x = (int)(center_x - scan_w / 2);
2639         scan_y = (int)(center_y - scan_h / 2);
2640 // printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", 
2641 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
2642 // printf("    angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
2644 // Determine min angle from size of block
2645         double angle1 = atan((double)block_h / block_w);
2646         double angle2 = atan((double)(block_h - 1) / (block_w + 1));
2647         double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
2648         min_angle = MAX(min_angle, MIN_ANGLE);
2650 #ifdef DEBUG
2651 printf("RotateScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI);
2652 #endif
2654         cache.remove_all_objects();
2655         if(!skip)
2656         {
2657 // Initial search range
2658                 float angle_range = (float)plugin->config.rotation_range;
2659                 result = 0;
2660                 total_steps = plugin->config.rotate_positions;
2663                 while(angle_range >= min_angle * total_steps)
2664                 {
2665                         scan_angle1 = result - angle_range;
2666                         scan_angle2 = result + angle_range;
2669                         set_package_count(total_steps);
2670 //set_package_count(1);
2671                         process_packages();
2673                         int64_t min_difference = -1;
2674                         for(int i = 0; i < get_total_packages(); i++)
2675                         {
2676                                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
2677                                 if(pkg->difference < min_difference || min_difference == -1)
2678                                 {
2679                                         min_difference = pkg->difference;
2680                                         result = pkg->angle;
2681                                 }
2682 //break;
2683                         }
2685                         angle_range /= 2;
2687 //break;
2688                 }
2689         }
2692         if(!skip && plugin->config.mode2 == MotionConfig::SAVE)
2693         {
2694                 char string[BCTEXTLEN];
2695                 sprintf(string, 
2696                         "%s%06d", 
2697                         ROTATION_FILE, 
2698                         plugin->get_source_position());
2699                 FILE *output = fopen(string, "w");
2700                 if(output)
2701                 {
2702                         fprintf(output, "%f\n", result);
2703                         fclose(output);
2704                 }
2705                 else
2706                 {
2707                         perror("RotateScan::scan_frame SAVE");
2708                 }
2709         }
2711 #ifdef DEBUG
2712 printf("RotateScan::scan_frame 10 angle=%f\n", result);
2713 #endif
2714         
2717         return result;
2720 int64_t RotateScan::get_cache(float angle)
2722         int64_t result = -1;
2723         cache_lock->lock("RotateScan::get_cache");
2724         for(int i = 0; i < cache.total; i++)
2725         {
2726                 RotateScanCache *ptr = cache.values[i];
2727                 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
2728                 {
2729                         result = ptr->difference;
2730                         break;
2731                 }
2732         }
2733         cache_lock->unlock();
2734         return result;
2737 void RotateScan::put_cache(float angle, int64_t difference)
2739         RotateScanCache *ptr = new RotateScanCache(angle, difference);
2740         cache_lock->lock("RotateScan::put_cache");
2741         cache.append(ptr);
2742         cache_lock->unlock();
2753 RotateScanCache::RotateScanCache(float angle, int64_t difference)
2755         this->angle = angle;
2756         this->difference = difference;