r864: Merge 2.1:
[cinelerra_cv.git] / plugins / motion / motion.C
blobed8e5956906e0f4f93fbb0289e52773c366508e3
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.terminate_string();
420 void MotionMain::read_data(KeyFrame *keyframe)
422         FileXML input;
424         input.set_shared_string(keyframe->data, strlen(keyframe->data));
426         int result = 0;
428         while(!result)
429         {
430                 result = input.read_tag();
432                 if(!result)
433                 {
434                         if(input.tag.title_is("MOTION"))
435                         {
436                                 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
437                                 config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
438                                 config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
439                                 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
440                                 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
441                                 config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
442                                 config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
443                                 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
444                                 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
445                                 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
446                                 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
447                                 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
448                                 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
449                                 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
450                                 config.mode1 = input.tag.get_property("MODE1", config.mode1);
451                                 config.global = input.tag.get_property("GLOBAL", config.global);
452                                 config.rotate = input.tag.get_property("ROTATE", config.rotate);
453                                 config.mode2 = input.tag.get_property("MODE2", config.mode2);
454                                 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
455                                 config.mode3 = input.tag.get_property("MODE3", config.mode3);
456                                 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
457                                 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
458                                 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
459                                 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
460                         }
461                 }
462         }
463         config.boundaries();
474 void MotionMain::allocate_temp(int w, int h, int color_model)
476         if(temp_frame && 
477                 (temp_frame->get_w() != w ||
478                 temp_frame->get_h() != h))
479         {
480                 delete temp_frame;
481                 temp_frame = 0;
482         }
483         if(!temp_frame)
484                 temp_frame = new VFrame(0, w, h, color_model);
489 void MotionMain::process_global()
491         if(!engine) engine = new MotionScan(this,
492                 PluginClient::get_project_smp() + 1,
493                 PluginClient::get_project_smp() + 1);
495 // Get the current motion vector between the previous and current frame
496         engine->scan_frame(current_global_ref, prev_global_ref);
497         current_dx = engine->dx_result;
498         current_dy = engine->dy_result;
500 // Add current motion vector to accumulation vector.
501         if(config.mode3 != MotionConfig::TRACK_SINGLE)
502         {
503 // Retract over time
504                 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
505                 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
506                 total_dx += engine->dx_result;
507                 total_dy += engine->dy_result;
508         }
509         else
510 // Make accumulation vector current
511         {
512                 total_dx = engine->dx_result;
513                 total_dy = engine->dy_result;
514         }
516 // Clamp accumulation vector
517         if(config.magnitude < 100)
518         {
519                 int block_w = (int64_t)config.global_block_w * 
520                                 current_global_ref->get_w() / 100;
521                 int block_h = (int64_t)config.global_block_h * 
522                                 current_global_ref->get_h() / 100;
523                 int block_x_orig = (int64_t)(config.block_x * 
524                         current_global_ref->get_w() / 
525                         100);
526                 int block_y_orig = (int64_t)(config.block_y *
527                         current_global_ref->get_h() / 
528                         100);
530                 int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
531                         OVERSAMPLE * 
532                         config.magnitude / 
533                         100;
534                 int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
535                         OVERSAMPLE *
536                         config.magnitude / 
537                         100;
538                 int min_block_x = (int64_t)-block_x_orig * 
539                         OVERSAMPLE * 
540                         config.magnitude / 
541                         100;
542                 int min_block_y = (int64_t)-block_y_orig * 
543                         OVERSAMPLE * 
544                         config.magnitude / 
545                         100;
547                 CLAMP(total_dx, min_block_x, max_block_x);
548                 CLAMP(total_dy, min_block_y, max_block_y);
549         }
551 #ifdef DEBUG
552 printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", 
553 (float)total_dx / OVERSAMPLE,
554 (float)total_dy / OVERSAMPLE);
555 #endif
557         if(config.mode3 != MotionConfig::TRACK_SINGLE && !config.rotate)
558         {
559 // Transfer current reference frame to previous reference frame and update
560 // counter.  Must wait for rotate to compare.
561                 prev_global_ref->copy_from(current_global_ref);
562                 previous_frame_number = get_source_position();
563         }
565 // Decide what to do with target based on requested operation
566         int interpolation;
567         float dx;
568         float dy;
569         switch(config.mode1)
570         {
571                 case MotionConfig::NOTHING:
572                         global_target_dst->copy_from(global_target_src);
573                         break;
574                 case MotionConfig::TRACK_PIXEL:
575                         interpolation = NEAREST_NEIGHBOR;
576                         dx = (int)(total_dx / OVERSAMPLE);
577                         dy = (int)(total_dy / OVERSAMPLE);
578                         break;
579                 case MotionConfig::STABILIZE_PIXEL:
580                         interpolation = NEAREST_NEIGHBOR;
581                         dx = -(int)(total_dx / OVERSAMPLE);
582                         dy = -(int)(total_dy / OVERSAMPLE);
583                         break;
584                         break;
585                 case MotionConfig::TRACK:
586                         interpolation = CUBIC_LINEAR;
587                         dx = (float)total_dx / OVERSAMPLE;
588                         dy = (float)total_dy / OVERSAMPLE;
589                         break;
590                 case MotionConfig::STABILIZE:
591                         interpolation = CUBIC_LINEAR;
592                         dx = -(float)total_dx / OVERSAMPLE;
593                         dy = -(float)total_dy / OVERSAMPLE;
594                         break;
595         }
598         if(config.mode1 != MotionConfig::NOTHING)
599         {
600                 if(!overlayer) 
601                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
602                 global_target_dst->clear_frame();
603                 overlayer->overlay(global_target_dst,
604                         global_target_src,
605                         0,
606                         0,
607                         global_target_src->get_w(),
608                         global_target_src->get_h(),
609                         dx,
610                         dy,
611                         (float)global_target_src->get_w() + dx,
612                         (float)global_target_src->get_h() + dy,
613                         1,
614                         TRANSFER_REPLACE,
615                         interpolation);
616         }
621 void MotionMain::process_rotation()
623         int block_x;
624         int block_y;
626 // Convert the previous global reference into the previous rotation reference.
627 // Convert global target destination into rotation target source.
628         if(config.global)
629         {
630                 if(!overlayer) 
631                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
632                 float dx;
633                 float dy;
634                 if(config.mode3 == MotionConfig::TRACK_SINGLE)
635                 {
636                         dx = (float)total_dx / OVERSAMPLE;
637                         dy = (float)total_dy / OVERSAMPLE;
638                 }
639                 else
640                 {
641                         dx = (float)current_dx / OVERSAMPLE;
642                         dy = (float)current_dy / OVERSAMPLE;
643                 }
645                 prev_rotate_ref->clear_frame();
646                 overlayer->overlay(prev_rotate_ref,
647                         prev_global_ref,
648                         0,
649                         0,
650                         prev_global_ref->get_w(),
651                         prev_global_ref->get_h(),
652                         dx,
653                         dy,
654                         (float)prev_global_ref->get_w() + dx,
655                         (float)prev_global_ref->get_h() + dy,
656                         1,
657                         TRANSFER_REPLACE,
658                         CUBIC_LINEAR);
659 // Pivot is destination global position
660                 block_x = (int)(prev_rotate_ref->get_w() * 
661                         config.block_x / 
662                         100 +
663                         (float)total_dx / 
664                         OVERSAMPLE);
665                 block_y = (int)(prev_rotate_ref->get_h() * 
666                         config.block_y / 
667                         100 +
668                         (float)total_dy / 
669                         OVERSAMPLE);
670 // Use the global target output as the rotation target input
671                 rotate_target_src->copy_from(global_target_dst);
672 // Transfer current reference frame to previous reference frame for global.
673                 if(config.mode3 != MotionConfig::TRACK_SINGLE)
674                 {
675                         prev_global_ref->copy_from(current_global_ref);
676                         previous_frame_number = get_source_position();
677                 }
678         }
679         else
680         {
681 // Pivot is fixed
682                 block_x = (int)(prev_rotate_ref->get_w() * 
683                         config.block_x / 
684                         100);
685                 block_y = (int)(prev_rotate_ref->get_h() * 
686                         config.block_y / 
687                         100);
688         }
692 // Get rotation
693         if(!motion_rotate)
694                 motion_rotate = new RotateScan(this, 
695                         get_project_smp() + 1, 
696                         get_project_smp() + 1);
698         current_angle = motion_rotate->scan_frame(prev_rotate_ref, 
699                 current_rotate_ref,
700                 block_x,
701                 block_y);
705 // Add current rotation to accumulation
706         if(config.mode3 != MotionConfig::TRACK_SINGLE)
707         {
708 // Retract over time
709                 total_angle = total_angle * (100 - config.return_speed) / 100;
710                 total_angle += current_angle;
712                 if(!config.global)
713                 {
714 // Transfer current reference frame to previous reference frame and update
715 // counter.
716                         prev_rotate_ref->copy_from(current_rotate_ref);
717                         previous_frame_number = get_source_position();
718                 }
719         }
720         else
721         {
722                 total_angle = current_angle;
723         }
725 #ifdef DEBUG
726 printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
727 #endif
730 // Calculate rotation parameters based on requested operation
731         float angle;
732         switch(config.mode1)
733         {
734                 case MotionConfig::NOTHING:
735                         rotate_target_dst->copy_from(rotate_target_src);
736                         break;
737                 case MotionConfig::TRACK:
738                 case MotionConfig::TRACK_PIXEL:
739                         angle = total_angle;
740                         break;
741                 case MotionConfig::STABILIZE:
742                 case MotionConfig::STABILIZE_PIXEL:
743                         angle = -total_angle;
744                         break;
745         }
749         if(config.mode1 != MotionConfig::NOTHING)
750         {
751                 if(!rotate_engine)
752                         rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
753                                 PluginClient::get_project_smp() + 1);
755                 rotate_target_dst->clear_frame();
757 // Determine pivot based on a number of factors.
758                 switch(config.mode1)
759                 {
760                         case MotionConfig::TRACK:
761                         case MotionConfig::TRACK_PIXEL:
762 // Use destination of global tracking.
763                                 rotate_engine->set_pivot(block_x, block_y);
764                                 break;
766                         case MotionConfig::STABILIZE:
767                         case MotionConfig::STABILIZE_PIXEL:
768                                 if(config.global)
769                                 {
770 // Use origin of global stabilize operation
771                                         rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * 
772                                                         config.block_x / 
773                                                         100),
774                                                 (int)(rotate_target_dst->get_h() * 
775                                                         config.block_y / 
776                                                         100));
777                                 
778                                 }
779                                 else
780                                 {
781 // Use origin
782                                         rotate_engine->set_pivot(block_x, block_y);
783                                 }
784                                 break;
785                 }
788                 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
789 // overlayer->overlay(rotate_target_dst,
790 //      prev_rotate_ref,
791 //      0,
792 //      0,
793 //      prev_rotate_ref->get_w(),
794 //      prev_rotate_ref->get_h(),
795 //      0,
796 //      0,
797 //      prev_rotate_ref->get_w(),
798 //      prev_rotate_ref->get_h(),
799 //      1,
800 //      TRANSFER_NORMAL,
801 //      CUBIC_LINEAR);
802 // overlayer->overlay(rotate_target_dst,
803 //      current_rotate_ref,
804 //      0,
805 //      0,
806 //      prev_rotate_ref->get_w(),
807 //      prev_rotate_ref->get_h(),
808 //      0,
809 //      0,
810 //      prev_rotate_ref->get_w(),
811 //      prev_rotate_ref->get_h(),
812 //      1,
813 //      TRANSFER_NORMAL,
814 //      CUBIC_LINEAR);
817         }
830 int MotionMain::process_buffer(VFrame **frame,
831         int64_t start_position,
832         double frame_rate)
834         int need_reconfigure = load_configuration();
835         int color_model = frame[0]->get_color_model();
836         w = frame[0]->get_w();
837         h = frame[0]->get_h();
838         
840 #ifdef DEBUG
841 printf("MotionMain::process_buffer 1 start_position=%lld\n", start_position);
842 #endif
845 // Calculate the source and destination pointers for each of the operations.
846 // Get the layer to track motion in.
847         reference_layer = config.bottom_is_master ?
848                 PluginClient::total_in_buffers - 1 :
849                 0;
850 // Get the layer to apply motion in.
851         target_layer = config.bottom_is_master ?
852                 0 :
853                 PluginClient::total_in_buffers - 1;
856         output_frame = frame[target_layer];
859 // Get the position of previous reference frame.
860         int64_t actual_previous_number;
861 // Skip if match frame not available
862         int skip_current = 0;
865         if(config.mode3 == MotionConfig::TRACK_SINGLE)
866         {
867                 actual_previous_number = config.track_frame;
868                 if(get_direction() == PLAY_REVERSE)
869                         actual_previous_number++;
870                 if(actual_previous_number == start_position)
871                         skip_current = 1;
872         }
873         else
874         {
875                 actual_previous_number = start_position;
876                 if(get_direction() == PLAY_FORWARD)
877                 {
878                         actual_previous_number--;
879                         if(actual_previous_number < get_source_start())
880                                 skip_current = 1;
881                         else
882                         {
883                                 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
884                                 if(keyframe->position > 0 &&
885                                         actual_previous_number < keyframe->position)
886                                         skip_current = 1;
887                         }
888                 }
889                 else
890                 {
891                         actual_previous_number++;
892                         if(actual_previous_number >= get_source_start() + get_total_len())
893                                 skip_current = 1;
894                         else
895                         {
896                                 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
897                                 if(keyframe->position > 0 &&
898                                         actual_previous_number >= keyframe->position)
899                                         skip_current = 1;
900                         }
901                 }
903 // Only count motion since last keyframe
904                 
906         }
909         if(!config.global && !config.rotate) skip_current = 1;
914 // printf("process_realtime %d %lld %lld\n", 
915 // skip_current, 
916 // previous_frame_number, 
917 // actual_previous_number);
918 // Load match frame and reset vectors
919         int need_reload = !skip_current && 
920                 (previous_frame_number != actual_previous_number ||
921                 need_reconfigure);
922         if(need_reload)
923         {
924                 total_dx = 0;
925                 total_dy = 0;
926                 total_angle = 0;
927                 previous_frame_number = actual_previous_number;
928         }
931         if(skip_current)
932         {
933                 total_dx = 0;
934                 total_dy = 0;
935                 current_dx = 0;
936                 current_dy = 0;
937                 total_angle = 0;
938                 current_angle = 0;
939         }
944 // Get the global pointers.  Here we walk through the sequence of events.
945         if(config.global)
946         {
947 // Assume global only.  Global reads previous frame and compares
948 // with current frame to get the current translation.
949 // The center of the search area is fixed in compensate mode or
950 // the user value + the accumulation vector in track mode.
951                 if(!prev_global_ref)
952                         prev_global_ref = new VFrame(0, w, h, color_model);
953                 if(!current_global_ref)
954                         current_global_ref = new VFrame(0, w, h, color_model);
956 // Global loads the current target frame into the src and 
957 // writes it to the dst frame with desired translation.
958                 if(!global_target_src)
959                         global_target_src = new VFrame(0, w, h, color_model);
960                 if(!global_target_dst)
961                         global_target_dst = new VFrame(0, w, h, color_model);
964 // Load the global frames
965                 if(need_reload)
966                 {
967                         read_frame(prev_global_ref, 
968                                 reference_layer, 
969                                 previous_frame_number, 
970                                 frame_rate);
971                 }
973                 read_frame(current_global_ref, 
974                         reference_layer, 
975                         start_position, 
976                         frame_rate);
977                 read_frame(global_target_src,
978                         target_layer,
979                         start_position,
980                         frame_rate);
984 // Global followed by rotate
985                 if(config.rotate)
986                 {
987 // Must translate the previous global reference by the current global
988 // accumulation vector to match the current global reference.
989 // The center of the search area is always the user value + the accumulation
990 // vector.
991                         if(!prev_rotate_ref)
992                                 prev_rotate_ref = new VFrame(0, w, h, color_model);
993 // The current global reference is the current rotation reference.
994                         if(!current_rotate_ref)
995                                 current_rotate_ref = new VFrame(0, w, h, color_model);
996                         current_rotate_ref->copy_from(current_global_ref);
998 // The global target destination is copied to the rotation target source
999 // then written to the rotation output with rotation.
1000 // The pivot for the rotation is the center of the search area 
1001 // if we're tracking.
1002 // The pivot is fixed to the user position if we're compensating.
1003                         if(!rotate_target_src)
1004                                 rotate_target_src = new VFrame(0, w, h, color_model);
1005                         if(!rotate_target_dst)
1006                                 rotate_target_dst = new VFrame(0, w,h , color_model);
1007                 }
1008         }
1009         else
1010 // Rotation only
1011         if(config.rotate)
1012         {
1013 // Rotation reads the previous reference frame and compares it with current 
1014 // reference frame.
1015                 if(!prev_rotate_ref)
1016                         prev_rotate_ref = new VFrame(0, w, h, color_model);
1017                 if(!current_rotate_ref)
1018                         current_rotate_ref = new VFrame(0, w, h, color_model);
1020 // Rotation loads target frame to temporary, rotates it, and writes it to the
1021 // target frame.  The pivot is always fixed.
1022                 if(!rotate_target_src)
1023                         rotate_target_src = new VFrame(0, w, h, color_model);
1024                 if(!rotate_target_dst)
1025                         rotate_target_dst = new VFrame(0, w,h , color_model);
1028 // Load the rotate frames
1029                 if(need_reload)
1030                 {
1031                         read_frame(prev_rotate_ref, 
1032                                 reference_layer, 
1033                                 previous_frame_number, 
1034                                 frame_rate);
1035                 }
1036                 read_frame(current_rotate_ref, 
1037                         reference_layer, 
1038                         start_position, 
1039                         frame_rate);
1040                 read_frame(rotate_target_src,
1041                         target_layer,
1042                         start_position,
1043                         frame_rate);
1044         }
1055         if(!skip_current)
1056         {
1057 // Get position change from previous frame to current frame
1058                 if(config.global) process_global();
1059 // Get rotation change from previous frame to current frame
1060                 if(config.rotate) process_rotation();
1061 //frame[target_layer]->copy_from(prev_rotate_ref);
1062 //frame[target_layer]->copy_from(current_rotate_ref);
1063         }
1070 // Transfer the relevant target frame to the output
1071         if(!skip_current)
1072         {
1073                 if(config.rotate)
1074                 {
1075                         frame[target_layer]->copy_from(rotate_target_dst);
1076                 }
1077                 else
1078                 {
1079                         frame[target_layer]->copy_from(global_target_dst);
1080                 }
1081         }
1082         else
1083 // Read the target destination directly
1084         {
1085                 read_frame(frame[target_layer],
1086                         target_layer,
1087                         start_position,
1088                         frame_rate);
1089         }
1091         if(config.draw_vectors)
1092         {
1093                 draw_vectors(frame[target_layer]);
1094         }
1096 #ifdef DEBUG
1097 printf("MotionMain::process_buffer 100\n");
1098 #endif
1099         return 0;
1103 void MotionMain::clamp_scan(int w, 
1104         int h, 
1105         int *block_x1,
1106         int *block_y1,
1107         int *block_x2,
1108         int *block_y2,
1109         int *scan_x1,
1110         int *scan_y1,
1111         int *scan_x2,
1112         int *scan_y2,
1113         int use_absolute)
1115 // printf("MotionMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1116 // w,
1117 // h,
1118 // *block_x1,
1119 // *block_y1,
1120 // *block_x2,
1121 // *block_y2,
1122 // *scan_x1,
1123 // *scan_y1,
1124 // *scan_x2,
1125 // *scan_y2,
1126 // use_absolute);
1128         if(use_absolute)
1129         {
1130 // scan is always out of range before block.
1131                 if(*scan_x1 < 0)
1132                 {
1133                         int difference = -*scan_x1;
1134                         *block_x1 += difference;
1135                         *scan_x1 = 0;
1136                 }
1138                 if(*scan_y1 < 0)
1139                 {
1140                         int difference = -*scan_y1;
1141                         *block_y1 += difference;
1142                         *scan_y1 = 0;
1143                 }
1145                 if(*scan_x2 > w)
1146                 {
1147                         int difference = *scan_x2 - w;
1148                         *block_x2 -= difference;
1149                         *scan_x2 -= difference;
1150                 }
1152                 if(*scan_y2 > h)
1153                 {
1154                         int difference = *scan_y2 - h;
1155                         *block_y2 -= difference;
1156                         *scan_y2 -= difference;
1157                 }
1159                 CLAMP(*scan_x1, 0, w);
1160                 CLAMP(*scan_y1, 0, h);
1161                 CLAMP(*scan_x2, 0, w);
1162                 CLAMP(*scan_y2, 0, h);
1163         }
1164         else
1165         {
1166                 if(*scan_x1 < 0)
1167                 {
1168                         int difference = -*scan_x1;
1169                         *block_x1 += difference;
1170                         *scan_x2 += difference;
1171                         *scan_x1 = 0;
1172                 }
1174                 if(*scan_y1 < 0)
1175                 {
1176                         int difference = -*scan_y1;
1177                         *block_y1 += difference;
1178                         *scan_y2 += difference;
1179                         *scan_y1 = 0;
1180                 }
1182                 if(*scan_x2 - *block_x1 + *block_x2 > w)
1183                 {
1184                         int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1185                         *block_x2 -= difference;
1186                 }
1188                 if(*scan_y2 - *block_y1 + *block_y2 > h)
1189                 {
1190                         int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1191                         *block_y2 -= difference;
1192                 }
1194 //              CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1195 //              CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1196 //              CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1197 //              CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1198         }
1200 // Sanity checks which break the calculation but should never happen if the
1201 // center of the block is inside the frame.
1202         CLAMP(*block_x1, 0, w);
1203         CLAMP(*block_x2, 0, w);
1204         CLAMP(*block_y1, 0, h);
1205         CLAMP(*block_y2, 0, h);
1207 // printf("MotionMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1208 // w,
1209 // h,
1210 // *block_x1,
1211 // *block_y1,
1212 // *block_x2,
1213 // *block_y2,
1214 // *scan_x1,
1215 // *scan_y1,
1216 // *scan_x2,
1217 // *scan_y2,
1218 // use_absolute);
1223 void MotionMain::draw_vectors(VFrame *frame)
1225         int w = frame->get_w();
1226         int h = frame->get_h();
1227         int global_x1, global_y1;
1228         int global_x2, global_y2;
1229         int block_x, block_y;
1230         int block_w, block_h;
1231         int block_x1, block_y1;
1232         int block_x2, block_y2;
1233         int block_x3, block_y3;
1234         int block_x4, block_y4;
1235         int search_w, search_h;
1236         int search_x1, search_y1;
1237         int search_x2, search_y2;
1238         int search_x3, search_y3;
1239         int search_x4, search_y4;
1241         if(config.global)
1242         {
1243 // Get vector
1244 // Start of vector is center of previous block.
1245 // End of vector is total accumulation.
1246                 if(config.mode3 == MotionConfig::TRACK_SINGLE)
1247                 {
1248                         global_x1 = (int64_t)(config.block_x * 
1249                                 w / 
1250                                 100);
1251                         global_y1 = (int64_t)(config.block_y *
1252                                 h / 
1253                                 100);
1254                         global_x2 = global_x1 + total_dx / OVERSAMPLE;
1255                         global_y2 = global_y1 + total_dy / OVERSAMPLE;
1256 //printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1257                 }
1258                 else
1259 // Start of vector is center of previous block.
1260 // End of vector is current change.
1261                 if(config.mode3 == MotionConfig::PREVIOUS_SAME_BLOCK)
1262                 {
1263                         global_x1 = (int64_t)(config.block_x * 
1264                                 w / 
1265                                 100);
1266                         global_y1 = (int64_t)(config.block_y *
1267                                 h / 
1268                                 100);
1269                         global_x2 = global_x1 + current_dx / OVERSAMPLE;
1270                         global_y2 = global_y1 + current_dy / OVERSAMPLE;
1271                 }
1272                 else
1273                 {
1274                         global_x1 = (int64_t)(config.block_x * 
1275                                 w / 
1276                                 100 + 
1277                                 (total_dx - current_dx) / 
1278                                 OVERSAMPLE);
1279                         global_y1 = (int64_t)(config.block_y *
1280                                 h / 
1281                                 100 +
1282                                 (total_dy - current_dy) /
1283                                 OVERSAMPLE);
1284                         global_x2 = (int64_t)(config.block_x * 
1285                                 w / 
1286                                 100 + 
1287                                 total_dx / 
1288                                 OVERSAMPLE);
1289                         global_y2 = (int64_t)(config.block_y *
1290                                 h / 
1291                                 100 +
1292                                 total_dy /
1293                                 OVERSAMPLE);
1294                 }
1296                 block_x = global_x1;
1297                 block_y = global_y1;
1298                 block_w = config.global_block_w * w / 100;
1299                 block_h = config.global_block_h * h / 100;
1300                 block_x1 = block_x - block_w / 2;
1301                 block_y1 = block_y - block_h / 2;
1302                 block_x2 = block_x + block_w / 2;
1303                 block_y2 = block_y + block_h / 2;
1304                 search_w = config.global_range_w * w / 100;
1305                 search_h = config.global_range_h * h / 100;
1306                 search_x1 = block_x1 - search_w / 2;
1307                 search_y1 = block_y1 - search_h / 2;
1308                 search_x2 = block_x2 + search_w / 2;
1309                 search_y2 = block_y2 + search_h / 2;
1311 // printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1312 // global_x1,
1313 // global_y1,
1314 // block_w,
1315 // block_h,
1316 // block_x1,
1317 // block_y1,
1318 // block_x2,
1319 // block_y2,
1320 // search_x1,
1321 // search_y1,
1322 // search_x2,
1323 // search_y2);
1325                 clamp_scan(w, 
1326                         h, 
1327                         &block_x1,
1328                         &block_y1,
1329                         &block_x2,
1330                         &block_y2,
1331                         &search_x1,
1332                         &search_y1,
1333                         &search_x2,
1334                         &search_y2,
1335                         1);
1337 // Vector
1338                 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1340 // Macroblock
1341                 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1342                 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1343                 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1344                 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1347 // Search area
1348                 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1349                 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1350                 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1351                 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1353 // Block should be endpoint of motion
1354                 if(config.rotate)
1355                 {
1356                         block_x = global_x2;
1357                         block_y = global_y2;
1358                 }
1359         }
1360         else
1361         {
1362                 block_x = (int64_t)(config.block_x * w / 100);
1363                 block_y = (int64_t)(config.block_y * h / 100);
1364         }
1366         block_w = config.rotation_block_w * w / 100;
1367         block_h = config.rotation_block_h * h / 100;
1368         if(config.rotate)
1369         {
1370                 float angle = total_angle * 2 * M_PI / 360;
1371                 double base_angle1 = atan((float)block_h / block_w);
1372                 double base_angle2 = atan((float)block_w / block_h);
1373                 double target_angle1 = base_angle1 + angle;
1374                 double target_angle2 = base_angle2 + angle;
1375                 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1376                 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1377                 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1378                 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1379                 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1380                 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1381                 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1382                 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1383                 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1385                 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1386                 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1387                 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1388                 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1391 // Center
1392                 if(!config.global)
1393                 {
1394                         draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1395                         draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1396                 }
1397         }
1402 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
1404         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1406 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1407 { \
1408         type **rows = (type**)frame->get_rows(); \
1409         rows[y][x * components] = max - rows[y][x * components]; \
1410         if(!do_yuv) \
1411         { \
1412                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1413                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1414         } \
1415         else \
1416         { \
1417                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1418                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1419         } \
1420         if(components == 4) \
1421                 rows[y][x * components + 3] = max; \
1425         switch(frame->get_color_model())
1426         {
1427                 case BC_RGB888:
1428                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1429                         break;
1430                 case BC_RGBA8888:
1431                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1432                         break;
1433                 case BC_RGB_FLOAT:
1434                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1435                         break;
1436                 case BC_RGBA_FLOAT:
1437                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1438                         break;
1439                 case BC_YUV888:
1440                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1441                         break;
1442                 case BC_YUVA8888:
1443                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1444                         break;
1445                 case BC_RGB161616:
1446                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1447                         break;
1448                 case BC_YUV161616:
1449                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1450                         break;
1451                 case BC_RGBA16161616:
1452                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1453                         break;
1454                 case BC_YUVA16161616:
1455                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1456                         break;
1457         }
1461 void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1463         int w = labs(x2 - x1);
1464         int h = labs(y2 - y1);
1465 //printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1467         if(!w && !h)
1468         {
1469                 draw_pixel(frame, x1, y1);
1470         }
1471         else
1472         if(w > h)
1473         {
1474 // Flip coordinates so x1 < x2
1475                 if(x2 < x1)
1476                 {
1477                         y2 ^= y1;
1478                         y1 ^= y2;
1479                         y2 ^= y1;
1480                         x1 ^= x2;
1481                         x2 ^= x1;
1482                         x1 ^= x2;
1483                 }
1484                 int numerator = y2 - y1;
1485                 int denominator = x2 - x1;
1486                 for(int i = x1; i < x2; i++)
1487                 {
1488                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1489                         draw_pixel(frame, i, y);
1490                 }
1491         }
1492         else
1493         {
1494 // Flip coordinates so y1 < y2
1495                 if(y2 < y1)
1496                 {
1497                         y2 ^= y1;
1498                         y1 ^= y2;
1499                         y2 ^= y1;
1500                         x1 ^= x2;
1501                         x2 ^= x1;
1502                         x1 ^= x2;
1503                 }
1504                 int numerator = x2 - x1;
1505                 int denominator = y2 - y1;
1506                 for(int i = y1; i < y2; i++)
1507                 {
1508                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1509                         draw_pixel(frame, x, i);
1510                 }
1511         }
1512 //printf("MotionMain::draw_line 2\n");
1515 #define ARROW_SIZE 10
1516 void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1518         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1519         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1520         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1521         int x3;
1522         int y3;
1523         int x4;
1524         int y4;
1525         if(x2 < x1)
1526         {
1527                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1528                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1529                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1530                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1531         }
1532         else
1533         {
1534                 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1535                 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1536                 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1537                 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1538         }
1540 // Main vector
1541         draw_line(frame, x1, y1, x2, y2);
1542 //      draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1544 // Arrow line
1545         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1546 //      draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1547 // Arrow line
1548         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1549 //      draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1555 #define ABS_DIFF(type, temp_type, multiplier, components) \
1556 { \
1557         temp_type result_temp = 0; \
1558         for(int i = 0; i < h; i++) \
1559         { \
1560                 type *prev_row = (type*)prev_ptr; \
1561                 type *current_row = (type*)current_ptr; \
1562                 for(int j = 0; j < w; j++) \
1563                 { \
1564                         for(int k = 0; k < 3; k++) \
1565                         { \
1566                                 temp_type difference; \
1567                                 difference = *prev_row++ - *current_row++; \
1568                                 if(difference < 0) \
1569                                         result_temp -= difference; \
1570                                 else \
1571                                         result_temp += difference; \
1572                         } \
1573                         if(components == 4) \
1574                         { \
1575                                 prev_row++; \
1576                                 current_row++; \
1577                         } \
1578                 } \
1579                 prev_ptr += row_bytes; \
1580                 current_ptr += row_bytes; \
1581         } \
1582         result = (int64_t)(result_temp * multiplier); \
1585 int64_t MotionMain::abs_diff(unsigned char *prev_ptr,
1586         unsigned char *current_ptr,
1587         int row_bytes,
1588         int w,
1589         int h,
1590         int color_model)
1592         int64_t result = 0;
1593         switch(color_model)
1594         {
1595                 case BC_RGB888:
1596                         ABS_DIFF(unsigned char, int64_t, 1, 3)
1597                         break;
1598                 case BC_RGBA8888:
1599                         ABS_DIFF(unsigned char, int64_t, 1, 4)
1600                         break;
1601                 case BC_RGB_FLOAT:
1602                         ABS_DIFF(float, double, 0x10000, 3)
1603                         break;
1604                 case BC_RGBA_FLOAT:
1605                         ABS_DIFF(float, double, 0x10000, 4)
1606                         break;
1607                 case BC_YUV888:
1608                         ABS_DIFF(unsigned char, int64_t, 1, 3)
1609                         break;
1610                 case BC_YUVA8888:
1611                         ABS_DIFF(unsigned char, int64_t, 1, 4)
1612                         break;
1613                 case BC_YUV161616:
1614                         ABS_DIFF(uint16_t, int64_t, 1, 3)
1615                         break;
1616                 case BC_YUVA16161616:
1617                         ABS_DIFF(uint16_t, int64_t, 1, 4)
1618                         break;
1619         }
1620         return result;
1625 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1626 { \
1627         temp_type result_temp = 0; \
1628         temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1629         temp_type y1_fraction = 0x100 - y2_fraction; \
1630         temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1631         temp_type x1_fraction = 0x100 - x2_fraction; \
1632         for(int i = 0; i < h_sub; i++) \
1633         { \
1634                 type *prev_row1 = (type*)prev_ptr; \
1635                 type *prev_row2 = (type*)prev_ptr + components; \
1636                 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1637                 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1638                 type *current_row = (type*)current_ptr; \
1639                 for(int j = 0; j < w_sub; j++) \
1640                 { \
1641                         for(int k = 0; k < 3; k++) \
1642                         { \
1643                                 temp_type difference; \
1644                                 temp_type prev_value = \
1645                                         (*prev_row1++ * x1_fraction * y1_fraction + \
1646                                         *prev_row2++ * x2_fraction * y1_fraction + \
1647                                         *prev_row3++ * x1_fraction * y2_fraction + \
1648                                         *prev_row4++ * x2_fraction * y2_fraction) / \
1649                                         0x100 / 0x100; \
1650                                 temp_type current_value = *current_row++; \
1651                                 difference = prev_value - current_value; \
1652                                 if(difference < 0) \
1653                                         result_temp -= difference; \
1654                                 else \
1655                                         result_temp += difference; \
1656                         } \
1658                         if(components == 4) \
1659                         { \
1660                                 prev_row1++; \
1661                                 prev_row2++; \
1662                                 prev_row3++; \
1663                                 prev_row4++; \
1664                                 current_row++; \
1665                         } \
1666                 } \
1667                 prev_ptr += row_bytes; \
1668                 current_ptr += row_bytes; \
1669         } \
1670         result = (int64_t)(result_temp * multiplier); \
1676 int64_t MotionMain::abs_diff_sub(unsigned char *prev_ptr,
1677         unsigned char *current_ptr,
1678         int row_bytes,
1679         int w,
1680         int h,
1681         int color_model,
1682         int sub_x,
1683         int sub_y)
1685         int h_sub = h - 1;
1686         int w_sub = w - 1;
1687         int64_t result = 0;
1689         switch(color_model)
1690         {
1691                 case BC_RGB888:
1692                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1693                         break;
1694                 case BC_RGBA8888:
1695                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1696                         break;
1697                 case BC_RGB_FLOAT:
1698                         ABS_DIFF_SUB(float, double, 0x10000, 3)
1699                         break;
1700                 case BC_RGBA_FLOAT:
1701                         ABS_DIFF_SUB(float, double, 0x10000, 4)
1702                         break;
1703                 case BC_YUV888:
1704                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1705                         break;
1706                 case BC_YUVA8888:
1707                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1708                         break;
1709                 case BC_YUV161616:
1710                         ABS_DIFF_SUB(uint16_t, int64_t, 1, 3)
1711                         break;
1712                 case BC_YUVA16161616:
1713                         ABS_DIFF_SUB(uint16_t, int64_t, 1, 4)
1714                         break;
1715         }
1716         return result;
1723 MotionScanPackage::MotionScanPackage()
1724  : LoadPackage()
1726         valid = 1;
1734 MotionScanUnit::MotionScanUnit(MotionScan *server, 
1735         MotionMain *plugin)
1736  : LoadClient(server)
1738         this->plugin = plugin;
1739         this->server = server;
1740         cache_lock = new Mutex("MotionScanUnit::cache_lock");
1743 MotionScanUnit::~MotionScanUnit()
1745         delete cache_lock;
1750 void MotionScanUnit::process_package(LoadPackage *package)
1752         MotionScanPackage *pkg = (MotionScanPackage*)package;
1753         int w = server->current_frame->get_w();
1754         int h = server->current_frame->get_h();
1755         int color_model = server->current_frame->get_color_model();
1756         int pixel_size = cmodel_calculate_pixelsize(color_model);
1757         int row_bytes = server->current_frame->get_bytes_per_line();
1770 // Single pixel
1771         if(!server->subpixel)
1772         {
1773                 int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1));
1774                 int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1));
1776 // Try cache
1777                 pkg->difference1 = server->get_cache(search_x, search_y);
1778                 if(pkg->difference1 < 0)
1779                 {
1780 //printf("MotionScanUnit::process_package 1 %d %d\n", 
1781 //search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1);
1782 // Pointers to first pixel in each block
1783                         unsigned char *prev_ptr = server->previous_frame->get_rows()[
1784                                 search_y] +     
1785                                 search_x * pixel_size;
1786                         unsigned char *current_ptr = server->current_frame->get_rows()[
1787                                 pkg->block_y1] +
1788                                 pkg->block_x1 * pixel_size;
1789 // Scan block
1790                         pkg->difference1 = plugin->abs_diff(prev_ptr,
1791                                 current_ptr,
1792                                 row_bytes,
1793                                 pkg->block_x2 - pkg->block_x1,
1794                                 pkg->block_y2 - pkg->block_y1,
1795                                 color_model);
1796 //printf("MotionScanUnit::process_package 2\n");
1797                         server->put_cache(search_x, search_y, pkg->difference1);
1798                 }
1799         }
1807         else
1816 // Sub pixel
1817         {
1818                 int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1;
1819                 int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1;
1821                 if(plugin->config.horizontal_only)
1822                 {
1823                         sub_y = 0;
1824                 }
1826                 if(plugin->config.vertical_only)
1827                 {
1828                         sub_x = 0;
1829                 }
1831                 int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE;
1832                 int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE;
1833                 sub_x %= OVERSAMPLE;
1834                 sub_y %= OVERSAMPLE;
1837                 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1838                         search_y] +
1839                         search_x * pixel_size;
1840                 unsigned char *current_ptr = server->current_frame->get_rows()[
1841                         pkg->block_y1] +
1842                         pkg->block_x1 * pixel_size;
1844 // With subpixel, there are two ways to compare each position, one by shifting
1845 // the previous frame and two by shifting the current frame.
1846                 pkg->difference1 = plugin->abs_diff_sub(prev_ptr,
1847                         current_ptr,
1848                         row_bytes,
1849                         pkg->block_x2 - pkg->block_x1,
1850                         pkg->block_y2 - pkg->block_y1,
1851                         color_model,
1852                         sub_x,
1853                         sub_y);
1854                 pkg->difference2 = plugin->abs_diff_sub(current_ptr,
1855                         prev_ptr,
1856                         row_bytes,
1857                         pkg->block_x2 - pkg->block_x1,
1858                         pkg->block_y2 - pkg->block_y1,
1859                         color_model,
1860                         sub_x,
1861                         sub_y);
1862 // printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
1863 // sub_x,
1864 // sub_y,
1865 // search_x,
1866 // search_y,
1867 // pkg->difference1,
1868 // pkg->difference2);
1869         }
1885 int64_t MotionScanUnit::get_cache(int x, int y)
1887         int64_t result = -1;
1888         cache_lock->lock("MotionScanUnit::get_cache");
1889         for(int i = 0; i < cache.total; i++)
1890         {
1891                 MotionScanCache *ptr = cache.values[i];
1892                 if(ptr->x == x && ptr->y == y)
1893                 {
1894                         result = ptr->difference;
1895                         break;
1896                 }
1897         }
1898         cache_lock->unlock();
1899         return result;
1902 void MotionScanUnit::put_cache(int x, int y, int64_t difference)
1904         MotionScanCache *ptr = new MotionScanCache(x, y, difference);
1905         cache_lock->lock("MotionScanUnit::put_cache");
1906         cache.append(ptr);
1907         cache_lock->unlock();
1920 MotionScan::MotionScan(MotionMain *plugin, 
1921         int total_clients,
1922         int total_packages)
1923  : LoadServer(
1924 //1, 1 
1925 total_clients, total_packages 
1928         this->plugin = plugin;
1929         cache_lock = new Mutex("MotionScan::cache_lock");
1932 MotionScan::~MotionScan()
1934         delete cache_lock;
1938 void MotionScan::init_packages()
1940 // Set package coords
1941         for(int i = 0; i < get_total_packages(); i++)
1942         {
1943                 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
1945                 pkg->block_x1 = block_x1;
1946                 pkg->block_x2 = block_x2;
1947                 pkg->block_y1 = block_y1;
1948                 pkg->block_y2 = block_y2;
1949                 pkg->scan_x1 = scan_x1;
1950                 pkg->scan_x2 = scan_x2;
1951                 pkg->scan_y1 = scan_y1;
1952                 pkg->scan_y2 = scan_y2;
1953                 pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps;
1954                 pkg->difference1 = 0;
1955                 pkg->difference2 = 0;
1956                 pkg->dx = 0;
1957                 pkg->dy = 0;
1958                 pkg->valid = 1;
1959         }
1962 LoadClient* MotionScan::new_client()
1964         return new MotionScanUnit(this, plugin);
1967 LoadPackage* MotionScan::new_package()
1969         return new MotionScanPackage;
1973 void MotionScan::scan_frame(VFrame *previous_frame,
1974         VFrame *current_frame)
1976         this->previous_frame = previous_frame;
1977         this->current_frame = current_frame;
1978         subpixel = 0;
1980         cache.remove_all_objects();
1983 // Single macroblock
1984         int w = current_frame->get_w();
1985         int h = current_frame->get_h();
1987 // Initial search parameters
1988         int scan_w = w * plugin->config.global_range_w / 100;
1989         int scan_h = h * plugin->config.global_range_h / 100;
1990         int block_w = w * plugin->config.global_block_w / 100;
1991         int block_h = h * plugin->config.global_block_h / 100;
1993 // Location of block in previous frame
1994         block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2);
1995         block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2);
1996         block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2);
1997         block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2);
1999 // Offset to location of previous block.  This offset needn't be very accurate
2000 // since it's the offset of the previous image and current image we want.
2001         if(plugin->config.mode3 == MotionConfig::TRACK_PREVIOUS)
2002         {
2003                 block_x1 += plugin->total_dx / OVERSAMPLE;
2004                 block_y1 += plugin->total_dy / OVERSAMPLE;
2005                 block_x2 += plugin->total_dx / OVERSAMPLE;
2006                 block_y2 += plugin->total_dy / OVERSAMPLE;
2007         }
2009         skip = 0;
2011         switch(plugin->config.mode2)
2012         {
2013 // Don't calculate
2014                 case MotionConfig::NO_CALCULATE:
2015                         dx_result = 0;
2016                         dy_result = 0;
2017                         skip = 1;
2018                         break;
2020                 case MotionConfig::LOAD:
2021                 {
2022 // Load result from disk
2023                         char string[BCTEXTLEN];
2024                         sprintf(string, "%s%06d", MOTION_FILE, plugin->get_source_position());
2025                         FILE *input = fopen(string, "r");
2026                         if(input)
2027                         {
2028                                 fscanf(input, 
2029                                         "%d %d", 
2030                                         &dx_result,
2031                                         &dy_result);
2032                                 fclose(input);
2033                                 skip = 1;
2034                         }
2035                         break;
2036                 }
2038 // Scan from scratch
2039                 default:
2040                         skip = 0;
2041                         break;
2042         }
2044 // Perform scan
2045         if(!skip)
2046         {
2047 // Location of block in current frame
2048                 int x_result = block_x1;
2049                 int y_result = block_y1;
2051 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2052 // block_x1 + block_w / 2,
2053 // block_y1 + block_h / 2,
2054 // block_w,
2055 // block_h,
2056 // block_x1,
2057 // block_y1,
2058 // block_x2,
2059 // block_y2);
2061                 while(1)
2062                 {
2063                         scan_x1 = x_result - scan_w / 2;
2064                         scan_y1 = y_result - scan_h / 2;
2065                         scan_x2 = x_result + scan_w / 2;
2066                         scan_y2 = y_result + scan_h / 2;
2070 // Zero out requested values
2071                         if(plugin->config.horizontal_only)
2072                         {
2073                                 scan_y1 = block_y1;
2074                                 scan_y2 = block_y1 + 1;
2075                         }
2076                         if(plugin->config.vertical_only)
2077                         {
2078                                 scan_x1 = block_x1;
2079                                 scan_x2 = block_x1 + 1;
2080                         }
2082 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2083 // block_x1,
2084 // block_y1,
2085 // block_x2,
2086 // block_y2,
2087 // scan_x1,
2088 // scan_y1,
2089 // scan_x2,
2090 // scan_y2);
2091 // Clamp the block coords before the scan so we get useful scan coords.
2092                         MotionMain::clamp_scan(w, 
2093                                 h, 
2094                                 &block_x1,
2095                                 &block_y1,
2096                                 &block_x2,
2097                                 &block_y2,
2098                                 &scan_x1,
2099                                 &scan_y1,
2100                                 &scan_x2,
2101                                 &scan_y2,
2102                                 0);
2103 // 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", 
2104 // block_x1,
2105 // block_y1,
2106 // block_x2,
2107 // block_y2,
2108 // scan_x1, 
2109 // scan_y1, 
2110 // scan_x2, 
2111 // scan_y2, 
2112 // x_result, 
2113 // y_result);
2116 // Give up if invalid coords.
2117                         if(scan_y2 <= scan_y1 ||
2118                                 scan_x2 <= scan_x1 ||
2119                                 block_x2 <= block_x1 ||
2120                                 block_y2 <= block_y1)
2121                                 break;
2123 // For subpixel, the top row and left column are skipped
2124                         if(subpixel)
2125                         {
2126                                 if(plugin->config.horizontal_only ||
2127                                         plugin->config.vertical_only)
2128                                 {
2129                                         total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE;
2130                                 }
2131                                 else
2132                                 {
2133                                         total_pixels = 4 * OVERSAMPLE;
2134                                 }
2136                                 total_steps = total_pixels;
2138                                 set_package_count(total_steps);
2139                                 process_packages();
2141 // Get least difference
2142                                 int64_t min_difference = -1;
2143                                 for(int i = 0; i < get_total_packages(); i++)
2144                                 {
2145                                         MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
2146                                         if(pkg->difference1 < min_difference || min_difference == -1)
2147                                         {
2148                                                 min_difference = pkg->difference1;
2150                                                 if(plugin->config.vertical_only)
2151                                                         x_result = scan_x1 * OVERSAMPLE;
2152                                                 else
2153                                                         x_result = scan_x1 * OVERSAMPLE + 
2154                                                                 (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1;
2155                                                 
2156                                                 if(plugin->config.horizontal_only)
2157                                                         y_result = scan_y1 * OVERSAMPLE;
2158                                                 else
2159                                                         y_result = scan_y1 * OVERSAMPLE + 
2160                                                                 (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1;
2163 // Fill in results
2164                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2165                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2166                                         }
2168                                         if(pkg->difference2 < min_difference)
2169                                         {
2170                                                 min_difference = pkg->difference2;
2172                                                 if(plugin->config.vertical_only)
2173                                                         x_result = scan_x1 * OVERSAMPLE;
2174                                                 else
2175                                                         x_result = scan_x2 * OVERSAMPLE -
2176                                                                 ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1);
2178                                                 if(plugin->config.horizontal_only)
2179                                                         y_result = scan_y1 * OVERSAMPLE;
2180                                                 else
2181                                                         y_result = scan_y2 * OVERSAMPLE -
2182                                                                 ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1);
2184                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2185                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2186                                         }
2187                                 }
2189 //printf("MotionScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result);
2190                                 break;
2191                         }
2192                         else
2193                         {
2194                                 total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
2195                                 total_steps = MIN(plugin->config.global_positions, total_pixels);
2197                                 set_package_count(total_steps);
2198                                 process_packages();
2200 // Get least difference
2201                                 int64_t min_difference = -1;
2202                                 for(int i = 0; i < get_total_packages(); i++)
2203                                 {
2204                                         MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
2205                                         if(pkg->difference1 < min_difference || min_difference == -1)
2206                                         {
2207                                                 min_difference = pkg->difference1;
2208                                                 x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1));
2209                                                 y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1));
2210                                                 x_result *= OVERSAMPLE;
2211                                                 y_result *= OVERSAMPLE;
2212                                         }
2213                                 }
2215 // printf("MotionScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n",
2216 // total_steps, 
2217 // total_pixels,
2218 // subpixel);
2219 // 
2220 // printf("     scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n",
2221 // scan_w,
2222 // scan_h, 
2223 // scan_x1,
2224 // scan_y1,
2225 // scan_x2,
2226 // scan_y2);
2227 // 
2228 // printf("MotionScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n", 
2229 // block_x1, 
2230 // block_y1, 
2231 // block_x2,
2232 // block_y2,
2233 // (float)x_result / 4, 
2234 // (float)y_result / 4);
2237 // If a new search is required, rescale results back to pixels.
2238                                 if(total_steps >= total_pixels)
2239                                 {
2240 // Single pixel accuracy reached.  Now do exhaustive subpixel search.
2241                                         if(plugin->config.mode1 == MotionConfig::STABILIZE ||
2242                                                 plugin->config.mode1 == MotionConfig::TRACK ||
2243                                                 plugin->config.mode1 == MotionConfig::NOTHING)
2244                                         {
2245                                                 x_result /= OVERSAMPLE;
2246                                                 y_result /= OVERSAMPLE;
2247                                                 scan_w = 2;
2248                                                 scan_h = 2;
2249                                                 subpixel = 1;
2250                                         }
2251                                         else
2252                                         {
2253 // Fill in results and quit
2254                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2255                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2256                                                 break;
2257                                         }
2258                                 }
2259                                 else
2260 // Reduce scan area and try again
2261                                 {
2262                                         scan_w = (scan_x2 - scan_x1) / 2;
2263                                         scan_h = (scan_y2 - scan_y1) / 2;
2264                                         x_result /= OVERSAMPLE;
2265                                         y_result /= OVERSAMPLE;
2266                                 }
2267                         }
2268                 }
2270                 dx_result *= -1;
2271                 dy_result *= -1;
2272         }
2279 // Write results
2280         if(plugin->config.mode2 == MotionConfig::SAVE)
2281         {
2282                 char string[BCTEXTLEN];
2283                 sprintf(string, 
2284                         "%s%06d", 
2285                         MOTION_FILE, 
2286                         plugin->get_source_position());
2287                 FILE *output = fopen(string, "w");
2288                 if(output)
2289                 {
2290                         fprintf(output, 
2291                                 "%d %d\n",
2292                                 dx_result,
2293                                 dy_result);
2294                         fclose(output);
2295                 }
2296                 else
2297                 {
2298                         perror("MotionScan::scan_frame SAVE 1");
2299                 }
2300         }
2302 #ifdef DEBUG
2303 printf("MotionScan::scan_frame 10 dx=%.2f dy=%.2f\n", 
2304 (float)this->dx_result / OVERSAMPLE,
2305 (float)this->dy_result / OVERSAMPLE);
2306 #endif
2325 int64_t MotionScan::get_cache(int x, int y)
2327         int64_t result = -1;
2328         cache_lock->lock("MotionScan::get_cache");
2329         for(int i = 0; i < cache.total; i++)
2330         {
2331                 MotionScanCache *ptr = cache.values[i];
2332                 if(ptr->x == x && ptr->y == y)
2333                 {
2334                         result = ptr->difference;
2335                         break;
2336                 }
2337         }
2338         cache_lock->unlock();
2339         return result;
2342 void MotionScan::put_cache(int x, int y, int64_t difference)
2344         MotionScanCache *ptr = new MotionScanCache(x, y, difference);
2345         cache_lock->lock("MotionScan::put_cache");
2346         cache.append(ptr);
2347         cache_lock->unlock();
2354 MotionScanCache::MotionScanCache(int x, int y, int64_t difference)
2356         this->x = x;
2357         this->y = y;
2358         this->difference = difference;
2374 RotateScanPackage::RotateScanPackage()
2379 RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin)
2380  : LoadClient(server)
2382         this->server = server;
2383         this->plugin = plugin;
2384         rotater = 0;
2385         temp = 0;
2388 RotateScanUnit::~RotateScanUnit()
2390         delete rotater;
2391         delete temp;
2394 void RotateScanUnit::process_package(LoadPackage *package)
2396         if(server->skip) return;
2397         RotateScanPackage *pkg = (RotateScanPackage*)package;
2399         if((pkg->difference = server->get_cache(pkg->angle)) < 0)
2400         {
2401 //printf("RotateScanUnit::process_package 1\n");
2402                 int color_model = server->previous_frame->get_color_model();
2403                 int pixel_size = cmodel_calculate_pixelsize(color_model);
2404                 int row_bytes = server->previous_frame->get_bytes_per_line();
2406                 if(!rotater)
2407                         rotater = new AffineEngine(1, 1);
2408                 if(!temp) temp = new VFrame(0,
2409                         server->previous_frame->get_w(),
2410                         server->previous_frame->get_h(),
2411                         color_model);
2414 // Rotate original block size
2415                 rotater->set_viewport(server->block_x1, 
2416                         server->block_y1,
2417                         server->block_x2 - server->block_x1,
2418                         server->block_y2 - server->block_y1);
2419                 rotater->set_pivot(server->block_x, server->block_y);
2420 //pkg->angle = 2;
2421                 rotater->rotate(temp,
2422                         server->previous_frame,
2423                         pkg->angle);
2425 // Scan reduced block size
2426 //plugin->output_frame->copy_from(server->current_frame);
2427 //plugin->output_frame->copy_from(temp);
2428                 pkg->difference = plugin->abs_diff(
2429                         temp->get_rows()[server->scan_y] + server->scan_x * pixel_size,
2430                         server->current_frame->get_rows()[server->scan_y] + server->scan_x * pixel_size,
2431                         row_bytes,
2432                         server->scan_w,
2433                         server->scan_h,
2434                         color_model);
2435                 server->put_cache(pkg->angle, pkg->difference);
2437 // 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", 
2438 // server->block_x1, 
2439 // server->block_y1,
2440 // server->block_x2 - server->block_x1,
2441 // server->block_y2 - server->block_y1,
2442 // server->block_x,
2443 // server->block_y,
2444 // pkg->angle, 
2445 // server->scan_w,
2446 // server->scan_h,
2447 // pkg->difference);
2448         }
2472 RotateScan::RotateScan(MotionMain *plugin, 
2473         int total_clients, 
2474         int total_packages)
2475  : LoadServer(
2476 //1, 1 
2477 total_clients, total_packages 
2480         this->plugin = plugin;
2481         cache_lock = new Mutex("RotateScan::cache_lock");
2485 RotateScan::~RotateScan()
2487         delete cache_lock;
2490 void RotateScan::init_packages()
2492         for(int i = 0; i < get_total_packages(); i++)
2493         {
2494                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
2495                 pkg->angle = i * 
2496                         (scan_angle2 - scan_angle1) / 
2497                         (total_steps - 1) + 
2498                         scan_angle1;
2499         }
2502 LoadClient* RotateScan::new_client()
2504         return new RotateScanUnit(this, plugin);
2507 LoadPackage* RotateScan::new_package()
2509         return new RotateScanPackage;
2513 float RotateScan::scan_frame(VFrame *previous_frame,
2514         VFrame *current_frame,
2515         int block_x,
2516         int block_y)
2518         skip = 0;
2519         this->block_x = block_x;
2520         this->block_y = block_y;
2522         switch(plugin->config.mode2)
2523         {
2524                 case MotionConfig::NO_CALCULATE:
2525                         result = 0;
2526                         skip = 1;
2527                         break;
2529                 case MotionConfig::LOAD:
2530                 {
2531                         char string[BCTEXTLEN];
2532                         sprintf(string, "%s%06d", ROTATION_FILE, plugin->get_source_position());
2533                         FILE *input = fopen(string, "r");
2534                         if(input)
2535                         {
2536                                 fscanf(input, "%f", &result);
2537                                 fclose(input);
2538                                 skip = 1;
2539                         }
2540                         else
2541                         {
2542                                 perror("RotateScan::scan_frame LOAD");
2543                         }
2544                         break;
2545                 }
2546         }
2555         this->previous_frame = previous_frame;
2556         this->current_frame = current_frame;
2557         int w = current_frame->get_w();
2558         int h = current_frame->get_h();
2559         int block_w = w * plugin->config.rotation_block_w / 100;
2560         int block_h = h * plugin->config.rotation_block_h / 100;
2562         if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
2563         if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
2564         if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
2565         if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
2567         block_x1 = this->block_x - block_w / 2;
2568         block_x2 = this->block_x + block_w / 2;
2569         block_y1 = this->block_y - block_h / 2;
2570         block_y2 = this->block_y + block_h / 2;
2573 // Calculate the maximum area available to scan after rotation.
2574 // Must be calculated from the starting range because of cache.
2575 // Get coords of rectangle after rotation.
2576         double center_x = this->block_x;
2577         double center_y = this->block_y;
2578         double max_angle = plugin->config.rotation_range;
2579         double base_angle1 = atan((float)block_h / block_w);
2580         double base_angle2 = atan((float)block_w / block_h);
2581         double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
2582         double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
2583         double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
2584         double x1 = center_x - cos(target_angle1) * radius;
2585         double y1 = center_y - sin(target_angle1) * radius;
2586         double x2 = center_x + sin(target_angle2) * radius;
2587         double y2 = center_y - cos(target_angle2) * radius;
2588         double x3 = center_x - sin(target_angle2) * radius;
2589         double y3 = center_y + cos(target_angle2) * radius;
2591 // Track top edge to find greatest area.
2592         double max_area1 = 0;
2593         double max_x1 = 0;
2594         double max_y1 = 0;
2595         for(double x = x1; x < x2; x++)
2596         {
2597                 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
2598                 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
2599                 {
2600                         double area = fabs(x - center_x) * fabs(y - center_y);
2601                         if(area > max_area1)
2602                         {
2603                                 max_area1 = area;
2604                                 max_x1 = x;
2605                                 max_y1 = y;
2606                         }
2607                 }
2608         }
2610 // Track left edge to find greatest area.
2611         double max_area2 = 0;
2612         double max_x2 = 0;
2613         double max_y2 = 0;
2614         for(double y = y1; y < y3; y++)
2615         {
2616                 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
2617                 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
2618                 {
2619                         double area = fabs(x - center_x) * fabs(y - center_y);
2620                         if(area > max_area2)
2621                         {
2622                                 max_area2 = area;
2623                                 max_x2 = x;
2624                                 max_y2 = y;
2625                         }
2626                 }
2627         }
2629         double max_x, max_y;
2630         max_x = max_x2;
2631         max_y = max_y1;
2633 // Get reduced scan coords
2634         scan_w = (int)(fabs(max_x - center_x) * 2);
2635         scan_h = (int)(fabs(max_y - center_y) * 2);
2636         scan_x = (int)(center_x - scan_w / 2);
2637         scan_y = (int)(center_y - scan_h / 2);
2638 // printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", 
2639 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
2640 // printf("    angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
2642 // Determine min angle from size of block
2643         double angle1 = atan((double)block_h / block_w);
2644         double angle2 = atan((double)(block_h - 1) / (block_w + 1));
2645         double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
2646         min_angle = MAX(min_angle, MIN_ANGLE);
2648 #ifdef DEBUG
2649 printf("RotateScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI);
2650 #endif
2652         cache.remove_all_objects();
2653         if(!skip)
2654         {
2655 // Initial search range
2656                 float angle_range = (float)plugin->config.rotation_range;
2657                 result = 0;
2658                 total_steps = plugin->config.rotate_positions;
2661                 while(angle_range >= min_angle * total_steps)
2662                 {
2663                         scan_angle1 = result - angle_range;
2664                         scan_angle2 = result + angle_range;
2667                         set_package_count(total_steps);
2668 //set_package_count(1);
2669                         process_packages();
2671                         int64_t min_difference = -1;
2672                         for(int i = 0; i < get_total_packages(); i++)
2673                         {
2674                                 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
2675                                 if(pkg->difference < min_difference || min_difference == -1)
2676                                 {
2677                                         min_difference = pkg->difference;
2678                                         result = pkg->angle;
2679                                 }
2680 //break;
2681                         }
2683                         angle_range /= 2;
2685 //break;
2686                 }
2687         }
2690         if(!skip && plugin->config.mode2 == MotionConfig::SAVE)
2691         {
2692                 char string[BCTEXTLEN];
2693                 sprintf(string, 
2694                         "%s%06d", 
2695                         ROTATION_FILE, 
2696                         plugin->get_source_position());
2697                 FILE *output = fopen(string, "w");
2698                 if(output)
2699                 {
2700                         fprintf(output, "%f\n", result);
2701                         fclose(output);
2702                 }
2703                 else
2704                 {
2705                         perror("RotateScan::scan_frame SAVE");
2706                 }
2707         }
2709 #ifdef DEBUG
2710 printf("RotateScan::scan_frame 10 angle=%f\n", result);
2711 #endif
2712         
2715         return result;
2718 int64_t RotateScan::get_cache(float angle)
2720         int64_t result = -1;
2721         cache_lock->lock("RotateScan::get_cache");
2722         for(int i = 0; i < cache.total; i++)
2723         {
2724                 RotateScanCache *ptr = cache.values[i];
2725                 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
2726                 {
2727                         result = ptr->difference;
2728                         break;
2729                 }
2730         }
2731         cache_lock->unlock();
2732         return result;
2735 void RotateScan::put_cache(float angle, int64_t difference)
2737         RotateScanCache *ptr = new RotateScanCache(angle, difference);
2738         cache_lock->lock("RotateScan::put_cache");
2739         cache.append(ptr);
2740         cache_lock->unlock();
2751 RotateScanCache::RotateScanCache(float angle, int64_t difference)
2753         this->angle = angle;
2754         this->difference = difference;