ea339d9217ca08bb8a4af74b14fb75db2182d2dd
[goodguy/history.git] / cinelerra-5.1 / plugins / motion-hv / motion-hv.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2016 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "affine.h"
23 #include "bcdisplayinfo.h"
24 #include "clip.h"
25 #include "bchash.h"
26 #include "bcsignals.h"
27 #include "filexml.h"
28 #include "keyframe.h"
29 #include "language.h"
30 #include "motion-hv.h"
31 #include "motionscan-hv.h"
32 #include "motionwindow-hv.h"
33 #include "mutex.h"
34 #include "overlayframe.h"
35 #include "rotateframe.h"
36 #include "transportque.h"
37
38
39 #include <errno.h>
40 #include <unistd.h>
41
42 REGISTER_PLUGIN(MotionHVMain)
43
44 #undef DEBUG
45
46 // #ifndef DEBUG
47 // #define DEBUG
48 // #endif
49
50
51
52 MotionHVConfig::MotionHVConfig()
53 {
54         global_range_w = 5;
55         global_range_h = 5;
56         rotation_range = 5;
57         rotation_center = 0;
58         block_count = 1;
59         global_block_w = MIN_BLOCK;
60         global_block_h = MIN_BLOCK;
61 //      rotation_block_w = MIN_BLOCK;
62 //      rotation_block_h = MIN_BLOCK;
63         block_x = 50;
64         block_y = 50;
65 //      global_positions = 256;
66 //      rotate_positions = 4;
67         magnitude = 100;
68         rotate_magnitude = 90;
69         return_speed = 0;
70         rotate_return_speed = 0;
71         action_type = MotionHVScan::STABILIZE;
72 //      global = 1;
73         rotate = 1;
74         tracking_type = MotionHVScan::NO_CALCULATE;
75         draw_vectors = 1;
76         tracking_object = MotionHVScan::TRACK_SINGLE;
77         track_frame = 0;
78         bottom_is_master = 1;
79         horizontal_only = 0;
80         vertical_only = 0;
81 }
82
83 void MotionHVConfig::boundaries()
84 {
85         CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
86         CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS);
87         CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION);
88         CLAMP(rotation_center, -MAX_ROTATION, MAX_ROTATION);
89         CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
90         CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
91         CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
92 //      CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
93 //      CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
94 }
95
96 int MotionHVConfig::equivalent(MotionHVConfig &that)
97 {
98         return global_range_w == that.global_range_w &&
99                 global_range_h == that.global_range_h &&
100                 rotation_range == that.rotation_range &&
101                 rotation_center == that.rotation_center &&
102                 action_type == that.action_type &&
103 //              global == that.global &&
104                 rotate == that.rotate &&
105                 draw_vectors == that.draw_vectors &&
106                 block_count == that.block_count &&
107                 global_block_w == that.global_block_w &&
108                 global_block_h == that.global_block_h &&
109 //              rotation_block_w == that.rotation_block_w &&
110 //              rotation_block_h == that.rotation_block_h &&
111                 EQUIV(block_x, that.block_x) &&
112                 EQUIV(block_y, that.block_y) &&
113 //              global_positions == that.global_positions &&
114 //              rotate_positions == that.rotate_positions &&
115                 magnitude == that.magnitude &&
116                 return_speed == that.return_speed &&
117                 rotate_return_speed == that.rotate_return_speed &&
118                 rotate_magnitude == that.rotate_magnitude &&
119                 tracking_object == that.tracking_object &&
120                 track_frame == that.track_frame &&
121                 bottom_is_master == that.bottom_is_master &&
122                 horizontal_only == that.horizontal_only &&
123                 vertical_only == that.vertical_only;
124 }
125
126 void MotionHVConfig::copy_from(MotionHVConfig &that)
127 {
128         global_range_w = that.global_range_w;
129         global_range_h = that.global_range_h;
130         rotation_range = that.rotation_range;
131         rotation_center = that.rotation_center;
132         action_type = that.action_type;
133 //      global = that.global;
134         rotate = that.rotate;
135         tracking_type = that.tracking_type;
136         draw_vectors = that.draw_vectors;
137         block_count = that.block_count;
138         block_x = that.block_x;
139         block_y = that.block_y;
140 //      global_positions = that.global_positions;
141 //      rotate_positions = that.rotate_positions;
142         global_block_w = that.global_block_w;
143         global_block_h = that.global_block_h;
144 //      rotation_block_w = that.rotation_block_w;
145 //      rotation_block_h = that.rotation_block_h;
146         magnitude = that.magnitude;
147         return_speed = that.return_speed;
148         rotate_magnitude = that.rotate_magnitude;
149         rotate_return_speed = that.rotate_return_speed;
150         tracking_object = that.tracking_object;
151         track_frame = that.track_frame;
152         bottom_is_master = that.bottom_is_master;
153         horizontal_only = that.horizontal_only;
154         vertical_only = that.vertical_only;
155 }
156
157 void MotionHVConfig::interpolate(MotionHVConfig &prev,
158         MotionHVConfig &next,
159         int64_t prev_frame,
160         int64_t next_frame,
161         int64_t current_frame)
162 {
163         //double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
164         //double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
165         this->block_x = prev.block_x;
166         this->block_y = prev.block_y;
167         global_range_w = prev.global_range_w;
168         global_range_h = prev.global_range_h;
169         rotation_range = prev.rotation_range;
170         rotation_center = prev.rotation_center;
171         action_type = prev.action_type;
172 //      global = prev.global;
173         rotate = prev.rotate;
174         tracking_type = prev.tracking_type;
175         draw_vectors = prev.draw_vectors;
176         block_count = prev.block_count;
177 //      global_positions = prev.global_positions;
178 //      rotate_positions = prev.rotate_positions;
179         global_block_w = prev.global_block_w;
180         global_block_h = prev.global_block_h;
181 //      rotation_block_w = prev.rotation_block_w;
182 //      rotation_block_h = prev.rotation_block_h;
183         magnitude = prev.magnitude;
184         return_speed = prev.return_speed;
185         rotate_magnitude = prev.rotate_magnitude;
186         rotate_return_speed = prev.rotate_return_speed;
187         tracking_object = prev.tracking_object;
188         track_frame = prev.track_frame;
189         bottom_is_master = prev.bottom_is_master;
190         horizontal_only = prev.horizontal_only;
191         vertical_only = prev.vertical_only;
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 MotionHVMain::MotionHVMain(PluginServer *server)
213  : PluginVClient(server)
214 {
215         engine = 0;
216         rotate_engine = 0;
217 //      motion_rotate = 0;
218         total_dx = 0;
219         total_dy = 0;
220         total_angle = 0;
221         overlayer = 0;
222         search_area = 0;
223         search_size = 0;
224         temp_frame = 0;
225         previous_frame_number = -1;
226
227         prev_global_ref = 0;
228         current_global_ref = 0;
229         global_target_src = 0;
230         global_target_dst = 0;
231
232         prev_rotate_ref = 0;
233         current_rotate_ref = 0;
234         rotate_target_src = 0;
235         rotate_target_dst = 0;
236 }
237
238 MotionHVMain::~MotionHVMain()
239 {
240
241         delete engine;
242         delete overlayer;
243         delete [] search_area;
244         delete temp_frame;
245         delete rotate_engine;
246 //      delete motion_rotate;
247
248
249         delete prev_global_ref;
250         delete current_global_ref;
251         delete global_target_src;
252         delete global_target_dst;
253
254         delete prev_rotate_ref;
255         delete current_rotate_ref;
256         delete rotate_target_src;
257         delete rotate_target_dst;
258 }
259
260 const char* MotionHVMain::plugin_title() { return _("MotionHV"); }
261 int MotionHVMain::is_realtime() { return 1; }
262 int MotionHVMain::is_multichannel() { return 1; }
263
264
265 NEW_WINDOW_MACRO(MotionHVMain, MotionHVWindow)
266
267 LOAD_CONFIGURATION_MACRO(MotionHVMain, MotionHVConfig)
268
269
270
271 void MotionHVMain::update_gui()
272 {
273         if(thread)
274         {
275                 if(load_configuration())
276                 {
277                         thread->window->lock_window("MotionHVMain::update_gui");
278
279 //                      char string[BCTEXTLEN];
280 //                      sprintf(string, "%d", config.global_positions);
281 //                      ((MotionHVWindow*)thread->window)->global_search_positions->set_text(string);
282 //                      sprintf(string, "%d", config.rotate_positions);
283 //                      ((MotionHVWindow*)thread->window)->rotation_search_positions->set_text(string);
284
285                         ((MotionHVWindow*)thread->window)->global_block_w->update(config.global_block_w);
286                         ((MotionHVWindow*)thread->window)->global_block_h->update(config.global_block_h);
287 //                      ((MotionHVWindow*)thread->window)->rotation_block_w->update(config.rotation_block_w);
288 //                      ((MotionHVWindow*)thread->window)->rotation_block_h->update(config.rotation_block_h);
289                         ((MotionHVWindow*)thread->window)->block_x->update(config.block_x);
290                         ((MotionHVWindow*)thread->window)->block_y->update(config.block_y);
291                         ((MotionHVWindow*)thread->window)->block_x_text->update((float)config.block_x);
292                         ((MotionHVWindow*)thread->window)->block_y_text->update((float)config.block_y);
293                         ((MotionHVWindow*)thread->window)->magnitude->update(config.magnitude);
294                         ((MotionHVWindow*)thread->window)->return_speed->update(config.return_speed);
295                         ((MotionHVWindow*)thread->window)->rotate_magnitude->update(config.rotate_magnitude);
296                         ((MotionHVWindow*)thread->window)->rotate_return_speed->update(config.rotate_return_speed);
297                         ((MotionHVWindow*)thread->window)->rotation_range->update(config.rotation_range);
298                         ((MotionHVWindow*)thread->window)->rotation_center->update(config.rotation_center);
299
300
301                         ((MotionHVWindow*)thread->window)->track_single->update(config.tracking_object == MotionHVScan::TRACK_SINGLE);
302                         ((MotionHVWindow*)thread->window)->track_frame_number->update(config.track_frame);
303                         ((MotionHVWindow*)thread->window)->track_previous->update(config.tracking_object == MotionHVScan::TRACK_PREVIOUS);
304                         ((MotionHVWindow*)thread->window)->previous_same->update(config.tracking_object == MotionHVScan::PREVIOUS_SAME_BLOCK);
305                         if(config.tracking_object != MotionHVScan::TRACK_SINGLE)
306                                 ((MotionHVWindow*)thread->window)->track_frame_number->disable();
307                         else
308                                 ((MotionHVWindow*)thread->window)->track_frame_number->enable();
309
310                         ((MotionHVWindow*)thread->window)->action_type->set_text(
311                                 ActionType::to_text(config.action_type));
312                         ((MotionHVWindow*)thread->window)->tracking_type->set_text(
313                                 TrackingType::to_text(config.tracking_type));
314                         ((MotionHVWindow*)thread->window)->track_direction->set_text(
315                                 TrackDirection::to_text(config.horizontal_only, config.vertical_only));
316                         ((MotionHVWindow*)thread->window)->master_layer->set_text(
317                                 MasterLayer::to_text(config.bottom_is_master));
318
319
320                         ((MotionHVWindow*)thread->window)->update_mode();
321                         thread->window->unlock_window();
322                 }
323         }
324 }
325
326
327
328
329 void MotionHVMain::save_data(KeyFrame *keyframe)
330 {
331         FileXML output;
332
333 // cause data to be stored directly in text
334         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
335         output.tag.set_title("MOTIONHV");
336
337         output.tag.set_property("BLOCK_COUNT", config.block_count);
338 //      output.tag.set_property("GLOBAL_POSITIONS", config.global_positions);
339 //      output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
340         output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
341         output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
342 //      output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
343 //      output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
344         output.tag.set_property("BLOCK_X", config.block_x);
345         output.tag.set_property("BLOCK_Y", config.block_y);
346         output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
347         output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h);
348         output.tag.set_property("ROTATION_RANGE", config.rotation_range);
349         output.tag.set_property("ROTATION_CENTER", config.rotation_center);
350         output.tag.set_property("MAGNITUDE", config.magnitude);
351         output.tag.set_property("RETURN_SPEED", config.return_speed);
352         output.tag.set_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
353         output.tag.set_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
354         output.tag.set_property("ACTION_TYPE", config.action_type);
355 //      output.tag.set_property("GLOBAL", config.global);
356         output.tag.set_property("ROTATE", config.rotate);
357         output.tag.set_property("TRACKING_TYPE", config.tracking_type);
358         output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
359         output.tag.set_property("TRACKING_OBJECT", config.tracking_object);
360         output.tag.set_property("TRACK_FRAME", config.track_frame);
361         output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master);
362         output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only);
363         output.tag.set_property("VERTICAL_ONLY", config.vertical_only);
364         output.append_tag();
365         output.tag.set_title("/MOTIONHV");
366         output.append_tag();
367         output.terminate_string();
368 }
369
370 void MotionHVMain::read_data(KeyFrame *keyframe)
371 {
372         FileXML input;
373
374         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
375
376         int result = 0;
377
378         while(!result)
379         {
380                 result = input.read_tag();
381
382                 if(!result)
383                 {
384                         if(input.tag.title_is("MOTIONHV"))
385                         {
386                                 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
387 //                              config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
388 //                              config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
389                                 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
390                                 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
391 //                              config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
392 //                              config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
393                                 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
394                                 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
395                                 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
396                                 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
397                                 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
398                                 config.rotation_center = input.tag.get_property("ROTATION_CENTER", config.rotation_center);
399                                 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
400                                 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
401                                 config.rotate_magnitude = input.tag.get_property("ROTATE_MAGNITUDE", config.rotate_magnitude);
402                                 config.rotate_return_speed = input.tag.get_property("ROTATE_RETURN_SPEED", config.rotate_return_speed);
403                                 config.action_type = input.tag.get_property("ACTION_TYPE", config.action_type);
404 //                              config.global = input.tag.get_property("GLOBAL", config.global);
405                                 config.rotate = input.tag.get_property("ROTATE", config.rotate);
406                                 config.tracking_type = input.tag.get_property("TRACKING_TYPE", config.tracking_type);
407                                 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
408                                 config.tracking_object = input.tag.get_property("TRACKING_OBJECT", config.tracking_object);
409                                 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
410                                 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
411                                 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
412                                 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
413                         }
414                 }
415         }
416         config.boundaries();
417 }
418
419
420
421
422
423
424
425
426
427 void MotionHVMain::allocate_temp(int w, int h, int color_model)
428 {
429         if(temp_frame &&
430                 (temp_frame->get_w() != w ||
431                 temp_frame->get_h() != h))
432         {
433                 delete temp_frame;
434                 temp_frame = 0;
435         }
436         if(!temp_frame)
437                 temp_frame = new VFrame(w, h, color_model);
438 }
439
440
441 void MotionHVMain::process_global()
442 {
443         int w = current_global_ref->get_w();
444         int h = current_global_ref->get_h();
445
446
447         if(!engine) engine = new MotionHVScan(PluginClient::get_project_smp() + 1,
448                 PluginClient::get_project_smp() + 1);
449
450 // Determine if frames changed
451 // printf("MotionHVMain::process_global %d block_y=%f total_dy=%d\n",
452 //  __LINE__, config.block_y * h / 100, total_dy);
453         engine->scan_frame(current_global_ref,
454                 prev_global_ref,
455                 config.global_range_w * w / 100,
456                 config.global_range_h * h / 100,
457                 config.global_block_w * w / 100,
458                 config.global_block_h * h / 100,
459                 config.block_x * w / 100,
460                 config.block_y * h / 100,
461                 config.tracking_object,
462                 config.tracking_type,
463                 config.action_type,
464                 config.horizontal_only,
465                 config.vertical_only,
466                 get_source_position(),
467                 total_dx,
468                 total_dy,
469                 0,
470                 0,
471                 1, // do_motion
472                 config.rotate, // do_rotate
473                 config.rotation_center,
474                 config.rotation_range);
475
476         current_dx = engine->dx_result;
477         current_dy = engine->dy_result;
478
479 // Add current motion vector to accumulation vector.
480         if(config.tracking_object != MotionHVScan::TRACK_SINGLE)
481         {
482 // Retract over time
483                 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
484                 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
485                 total_dx += engine->dx_result;
486                 total_dy += engine->dy_result;
487 // printf("MotionHVMain::process_global %d total_dy=%d engine->dy_result=%d\n",
488 //  __LINE__, total_dy, engine->dy_result);
489         }
490         else
491 // Make accumulation vector current
492         {
493                 total_dx = engine->dx_result;
494                 total_dy = engine->dy_result;
495         }
496
497 // Clamp accumulation vector
498         if(config.magnitude < 100)
499         {
500                 //int block_w = (int64_t)config.global_block_w * w / 100;
501                 //int block_h = (int64_t)config.global_block_h * h / 100;
502                 int block_x_orig = (int64_t)(config.block_x * w / 100);
503                 int block_y_orig = (int64_t)(config.block_y *
504                         current_global_ref->get_h() / h / 100);
505
506                 int max_block_x = (int64_t)(w - block_x_orig) *
507                         OVERSAMPLE * config.magnitude / 100;
508                 int max_block_y = (int64_t)(h - block_y_orig) *
509                         OVERSAMPLE * config.magnitude / 100;
510                 int min_block_x = (int64_t)-block_x_orig *
511                         OVERSAMPLE * config.magnitude / 100;
512                 int min_block_y = (int64_t)-block_y_orig *
513                         OVERSAMPLE * config.magnitude / 100;
514
515                 CLAMP(total_dx, min_block_x, max_block_x);
516                 CLAMP(total_dy, min_block_y, max_block_y);
517         }
518
519 // printf("MotionHVMain::process_global %d total_dx=%d total_dy=%d\n",
520 //  __LINE__, total_dx, total_dy);
521
522         if(config.tracking_object != MotionHVScan::TRACK_SINGLE && !config.rotate)
523         {
524 // Transfer current reference frame to previous reference frame and update
525 // counter.  Must wait for rotate to compare.
526                 prev_global_ref->copy_from(current_global_ref);
527                 previous_frame_number = get_source_position();
528         }
529
530 // Decide what to do with target based on requested operation
531         int interpolation = NEAREST_NEIGHBOR;
532         float dx = 0.;
533         float dy = 0.;
534         switch(config.action_type)
535         {
536                 case MotionHVScan::NOTHING:
537                         global_target_dst->copy_from(global_target_src);
538                         break;
539                 case MotionHVScan::TRACK_PIXEL:
540                         interpolation = NEAREST_NEIGHBOR;
541                         dx = (int)(total_dx / OVERSAMPLE);
542                         dy = (int)(total_dy / OVERSAMPLE);
543                         break;
544                 case MotionHVScan::STABILIZE_PIXEL:
545                         interpolation = NEAREST_NEIGHBOR;
546                         dx = -(int)(total_dx / OVERSAMPLE);
547                         dy = -(int)(total_dy / OVERSAMPLE);
548                         break;
549                 case MotionHVScan::TRACK:
550                         interpolation = CUBIC_LINEAR;
551                         dx = (float)total_dx / OVERSAMPLE;
552                         dy = (float)total_dy / OVERSAMPLE;
553                         break;
554                 case MotionHVScan::STABILIZE:
555                         interpolation = CUBIC_LINEAR;
556                         dx = -(float)total_dx / OVERSAMPLE;
557                         dy = -(float)total_dy / OVERSAMPLE;
558                         break;
559         }
560
561
562         if(config.action_type != MotionHVScan::NOTHING)
563         {
564                 if(!overlayer)
565                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
566                 global_target_dst->clear_frame();
567                 overlayer->overlay(global_target_dst,
568                         global_target_src,
569                         0,
570                         0,
571                         global_target_src->get_w(),
572                         global_target_src->get_h(),
573                         dx,
574                         dy,
575                         (float)global_target_src->get_w() + dx,
576                         (float)global_target_src->get_h() + dy,
577                         1,
578                         TRANSFER_REPLACE,
579                         interpolation);
580         }
581 }
582
583
584
585 void MotionHVMain::process_rotation()
586 {
587         int block_x;
588         int block_y;
589
590 // Always require global
591 // Convert the previous global reference into the previous rotation reference.
592 // Convert global target destination into rotation target source.
593 //      if(config.global)
594         if(1)
595         {
596                 if(!overlayer)
597                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
598                 float dx;
599                 float dy;
600                 if(config.tracking_object == MotionHVScan::TRACK_SINGLE)
601                 {
602                         dx = (float)total_dx / OVERSAMPLE;
603                         dy = (float)total_dy / OVERSAMPLE;
604                 }
605                 else
606                 {
607                         dx = (float)current_dx / OVERSAMPLE;
608                         dy = (float)current_dy / OVERSAMPLE;
609                 }
610
611                 prev_rotate_ref->clear_frame();
612                 overlayer->overlay(prev_rotate_ref,
613                         prev_global_ref,
614                         0,
615                         0,
616                         prev_global_ref->get_w(),
617                         prev_global_ref->get_h(),
618                         dx,
619                         dy,
620                         (float)prev_global_ref->get_w() + dx,
621                         (float)prev_global_ref->get_h() + dy,
622                         1,
623                         TRANSFER_REPLACE,
624                         CUBIC_LINEAR);
625 // Pivot is destination global position
626                 block_x = (int)(prev_rotate_ref->get_w() *
627                         config.block_x /
628                         100 +
629                         (float)total_dx /
630                         OVERSAMPLE);
631                 block_y = (int)(prev_rotate_ref->get_h() *
632                         config.block_y /
633                         100 +
634                         (float)total_dy /
635                         OVERSAMPLE);
636 // Use the global target output as the rotation target input
637                 rotate_target_src->copy_from(global_target_dst);
638 // Transfer current reference frame to previous reference frame for global.
639                 if(config.tracking_object != MotionHVScan::TRACK_SINGLE)
640                 {
641                         prev_global_ref->copy_from(current_global_ref);
642                         previous_frame_number = get_source_position();
643                 }
644         }
645         else
646         {
647 // Pivot is fixed
648                 block_x = (int)(prev_rotate_ref->get_w() *
649                         config.block_x /
650                         100);
651                 block_y = (int)(prev_rotate_ref->get_h() *
652                         config.block_y /
653                         100);
654         }
655
656
657
658 // Get rotation
659 //      if(!motion_rotate)
660 //              motion_rotate = new RotateScan(this,
661 //                      get_project_smp() + 1,
662 //                      get_project_smp() + 1);
663 //
664 //      current_angle = motion_rotate->scan_frame(prev_rotate_ref,
665 //              current_rotate_ref,
666 //              block_x,
667 //              block_y);
668
669         current_angle = engine->dr_result;
670
671 // Add current rotation to accumulation
672         if(config.tracking_object != MotionHVScan::TRACK_SINGLE)
673         {
674 // Retract over time
675                 total_angle = total_angle * (100 - config.rotate_return_speed) / 100;
676 // Accumulate current rotation
677                 total_angle += current_angle;
678
679 // Clamp rotation accumulation
680                 if(config.rotate_magnitude < 90)
681                 {
682                         CLAMP(total_angle, -config.rotate_magnitude, config.rotate_magnitude);
683                 }
684
685 //              if(!config.global)
686 //              {
687 // Transfer current reference frame to previous reference frame and update
688 // counter.
689 //                      prev_rotate_ref->copy_from(current_rotate_ref);
690 //                      previous_frame_number = get_source_position();
691 //              }
692         }
693         else
694         {
695                 total_angle = current_angle;
696         }
697
698 #ifdef DEBUG
699 printf("MotionHVMain::process_rotation total_angle=%f\n", total_angle);
700 #endif
701
702
703 // Calculate rotation parameters based on requested operation
704         float angle = 0.;
705         switch(config.action_type)
706         {
707                 case MotionHVScan::NOTHING:
708                         rotate_target_dst->copy_from(rotate_target_src);
709                         break;
710                 case MotionHVScan::TRACK:
711                 case MotionHVScan::TRACK_PIXEL:
712                         angle = total_angle;
713                         break;
714                 case MotionHVScan::STABILIZE:
715                 case MotionHVScan::STABILIZE_PIXEL:
716                         angle = -total_angle;
717                         break;
718         }
719
720
721
722         if(config.action_type != MotionHVScan::NOTHING)
723         {
724                 if(!rotate_engine)
725                         rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
726                                 PluginClient::get_project_smp() + 1);
727
728                 rotate_target_dst->clear_frame();
729
730 // Determine pivot based on a number of factors.
731                 switch(config.action_type)
732                 {
733                         case MotionHVScan::TRACK:
734                         case MotionHVScan::TRACK_PIXEL:
735 // Use destination of global tracking.
736 //                              rotate_engine->set_pivot(block_x, block_y);
737                                 rotate_engine->set_in_pivot(block_x, block_y);
738                                 rotate_engine->set_out_pivot(block_x, block_y);
739                                 break;
740
741                         case MotionHVScan::STABILIZE:
742                         case MotionHVScan::STABILIZE_PIXEL:
743 //                              if(config.global)
744                                 if(1)
745                                 {
746 // Use origin of global stabilize operation
747 //                                      rotate_engine->set_pivot((int)(rotate_target_dst->get_w() *
748 //                                                      config.block_x /
749 //                                                      100),
750 //                                              (int)(rotate_target_dst->get_h() *
751 //                                                      config.block_y /
752 //                                                      100));
753                                         rotate_engine->set_in_pivot((int)(rotate_target_dst->get_w() *
754                                                         config.block_x /
755                                                         100),
756                                                 (int)(rotate_target_dst->get_h() *
757                                                         config.block_y /
758                                                         100));
759                                         rotate_engine->set_out_pivot((int)(rotate_target_dst->get_w() *
760                                                         config.block_x /
761                                                         100),
762                                                 (int)(rotate_target_dst->get_h() *
763                                                         config.block_y /
764                                                         100));
765
766                                 }
767                                 else
768                                 {
769 // Use origin
770 //                                      rotate_engine->set_pivot(block_x, block_y);
771                                         rotate_engine->set_in_pivot(block_x, block_y);
772                                         rotate_engine->set_out_pivot(block_x, block_y);
773                                 }
774                                 break;
775                 }
776
777
778 printf("MotionHVMain::process_rotation angle=%f\n", angle);
779                 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
780 // overlayer->overlay(rotate_target_dst,
781 //      prev_rotate_ref,
782 //      0,
783 //      0,
784 //      prev_rotate_ref->get_w(),
785 //      prev_rotate_ref->get_h(),
786 //      0,
787 //      0,
788 //      prev_rotate_ref->get_w(),
789 //      prev_rotate_ref->get_h(),
790 //      1,
791 //      TRANSFER_NORMAL,
792 //      CUBIC_LINEAR);
793 // overlayer->overlay(rotate_target_dst,
794 //      current_rotate_ref,
795 //      0,
796 //      0,
797 //      prev_rotate_ref->get_w(),
798 //      prev_rotate_ref->get_h(),
799 //      0,
800 //      0,
801 //      prev_rotate_ref->get_w(),
802 //      prev_rotate_ref->get_h(),
803 //      1,
804 //      TRANSFER_NORMAL,
805 //      CUBIC_LINEAR);
806
807
808         }
809
810
811 }
812
813
814
815
816
817
818
819
820
821 int MotionHVMain::process_buffer(VFrame **frame,
822         int64_t start_position,
823         double frame_rate)
824 {
825         int need_reconfigure = load_configuration();
826         int color_model = frame[0]->get_color_model();
827         w = frame[0]->get_w();
828         h = frame[0]->get_h();
829
830
831 #ifdef DEBUG
832 printf("MotionHVMain::process_buffer %d start_position=%lld\n", __LINE__, start_position);
833 #endif
834
835
836 // Calculate the source and destination pointers for each of the operations.
837 // Get the layer to track motion in.
838         reference_layer = config.bottom_is_master ?
839                 PluginClient::total_in_buffers - 1 :
840                 0;
841 // Get the layer to apply motion in.
842         target_layer = config.bottom_is_master ?
843                 0 :
844                 PluginClient::total_in_buffers - 1;
845
846
847         output_frame = frame[target_layer];
848
849
850 // Get the position of previous reference frame.
851         int64_t actual_previous_number;
852 // Skip if match frame not available
853         int skip_current = 0;
854
855
856         if(config.tracking_object == MotionHVScan::TRACK_SINGLE)
857         {
858                 actual_previous_number = config.track_frame;
859                 if(get_direction() == PLAY_REVERSE)
860                         actual_previous_number++;
861                 if(actual_previous_number == start_position)
862                         skip_current = 1;
863         }
864         else
865         {
866                 actual_previous_number = start_position;
867                 if(get_direction() == PLAY_FORWARD)
868                 {
869                         actual_previous_number--;
870                         if(actual_previous_number < get_source_start())
871                                 skip_current = 1;
872                         else
873                         {
874                                 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
875                                 if(keyframe->position > 0 &&
876                                         actual_previous_number < keyframe->position)
877                                         skip_current = 1;
878                         }
879                 }
880                 else
881                 {
882                         actual_previous_number++;
883                         if(actual_previous_number >= get_source_start() + get_total_len())
884                                 skip_current = 1;
885                         else
886                         {
887                                 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
888                                 if(keyframe->position > 0 &&
889                                         actual_previous_number >= keyframe->position)
890                                         skip_current = 1;
891                         }
892                 }
893
894 // Only count motion since last keyframe
895
896
897         }
898
899
900 //      if(!config.global && !config.rotate) skip_current = 1;
901
902
903
904
905 // printf("process_realtime %d %lld %lld\n",
906 // skip_current,
907 // previous_frame_number,
908 // actual_previous_number);
909 // Load match frame and reset vectors
910         int need_reload = !skip_current &&
911                 (previous_frame_number != actual_previous_number ||
912                 need_reconfigure);
913         if(need_reload)
914         {
915                 total_dx = 0;
916                 total_dy = 0;
917                 total_angle = 0;
918                 previous_frame_number = actual_previous_number;
919         }
920
921
922         if(skip_current)
923         {
924                 total_dx = 0;
925                 total_dy = 0;
926                 current_dx = 0;
927                 current_dy = 0;
928                 total_angle = 0;
929                 current_angle = 0;
930         }
931
932
933
934
935 // Get the global pointers.  Here we walk through the sequence of events.
936 //      if(config.global)
937         if(1)
938         {
939 // Assume global only.  Global reads previous frame and compares
940 // with current frame to get the current translation.
941 // The center of the search area is fixed in compensate mode or
942 // the user value + the accumulation vector in track mode.
943                 if(!prev_global_ref)
944                         prev_global_ref = new VFrame(w, h, color_model);
945                 if(!current_global_ref)
946                         current_global_ref = new VFrame(w, h, color_model);
947
948 // Global loads the current target frame into the src and
949 // writes it to the dst frame with desired translation.
950                 if(!global_target_src)
951                         global_target_src = new VFrame(w, h, color_model);
952                 if(!global_target_dst)
953                         global_target_dst = new VFrame(w, h, color_model);
954
955
956 // Load the global frames
957                 if(need_reload)
958                 {
959                         read_frame(prev_global_ref,
960                                 reference_layer,
961                                 previous_frame_number,
962                                 frame_rate,
963                                 0);
964                 }
965
966                 read_frame(current_global_ref,
967                         reference_layer,
968                         start_position,
969                         frame_rate,
970                         0);
971                 read_frame(global_target_src,
972                         target_layer,
973                         start_position,
974                         frame_rate,
975                         0);
976
977
978
979 // Global followed by rotate
980                 if(config.rotate)
981                 {
982 // Must translate the previous global reference by the current global
983 // accumulation vector to match the current global reference.
984 // The center of the search area is always the user value + the accumulation
985 // vector.
986                         if(!prev_rotate_ref)
987                                 prev_rotate_ref = new VFrame(w, h, color_model);
988 // The current global reference is the current rotation reference.
989                         if(!current_rotate_ref)
990                                 current_rotate_ref = new VFrame(w, h, color_model);
991                         current_rotate_ref->copy_from(current_global_ref);
992
993 // The global target destination is copied to the rotation target source
994 // then written to the rotation output with rotation.
995 // The pivot for the rotation is the center of the search area
996 // if we're tracking.
997 // The pivot is fixed to the user position if we're compensating.
998                         if(!rotate_target_src)
999                                 rotate_target_src = new VFrame(w, h, color_model);
1000                         if(!rotate_target_dst)
1001                                 rotate_target_dst = new VFrame(w, h, color_model);
1002                 }
1003         }
1004         else
1005 // Rotation only
1006         if(config.rotate)
1007         {
1008 // Rotation reads the previous reference frame and compares it with current
1009 // reference frame.
1010                 if(!prev_rotate_ref)
1011                         prev_rotate_ref = new VFrame(w, h, color_model);
1012                 if(!current_rotate_ref)
1013                         current_rotate_ref = new VFrame(w, h, color_model);
1014
1015 // Rotation loads target frame to temporary, rotates it, and writes it to the
1016 // target frame.  The pivot is always fixed.
1017                 if(!rotate_target_src)
1018                         rotate_target_src = new VFrame(w, h, color_model);
1019                 if(!rotate_target_dst)
1020                         rotate_target_dst = new VFrame(w, h, color_model);
1021
1022
1023 // Load the rotate frames
1024                 if(need_reload)
1025                 {
1026                         read_frame(prev_rotate_ref,
1027                                 reference_layer,
1028                                 previous_frame_number,
1029                                 frame_rate,
1030                                 0);
1031                 }
1032                 read_frame(current_rotate_ref,
1033                         reference_layer,
1034                         start_position,
1035                         frame_rate,
1036                         0);
1037                 read_frame(rotate_target_src,
1038                         target_layer,
1039                         start_position,
1040                         frame_rate,
1041                         0);
1042         }
1043
1044
1045
1046
1047
1048
1049
1050 //PRINT_TRACE
1051 //printf("skip_current=%d config.global=%d\n", skip_current, config.global);
1052
1053
1054         if(!skip_current)
1055         {
1056 // Get position change from previous frame to current frame
1057                 /* if(config.global) */ process_global();
1058 // Get rotation change from previous frame to current frame
1059                 if(config.rotate) process_rotation();
1060 //frame[target_layer]->copy_from(prev_rotate_ref);
1061 //frame[target_layer]->copy_from(current_rotate_ref);
1062         }
1063
1064
1065
1066
1067
1068
1069 // Transfer the relevant target frame to the output
1070         if(!skip_current)
1071         {
1072                 if(config.rotate)
1073                 {
1074                         frame[target_layer]->copy_from(rotate_target_dst);
1075                 }
1076                 else
1077                 {
1078                         frame[target_layer]->copy_from(global_target_dst);
1079                 }
1080         }
1081         else
1082 // Read the target destination directly
1083         {
1084                 read_frame(frame[target_layer],
1085                         target_layer,
1086                         start_position,
1087                         frame_rate,
1088                         0);
1089         }
1090
1091         if(config.draw_vectors)
1092         {
1093                 draw_vectors(frame[target_layer]);
1094         }
1095
1096 #ifdef DEBUG
1097 printf("MotionHVMain::process_buffer %d\n", __LINE__);
1098 #endif
1099         return 0;
1100 }
1101
1102
1103
1104 void MotionHVMain::draw_vectors(VFrame *frame)
1105 {
1106         int w = frame->get_w();
1107         int h = frame->get_h();
1108         int global_x1, global_y1;
1109         int global_x2, global_y2;
1110         int block_x, block_y;
1111         int block_w, block_h;
1112         int block_x1, block_y1;
1113         int block_x2, block_y2;
1114         int block_x3, block_y3;
1115         int block_x4, block_y4;
1116         int search_w, search_h;
1117         int search_x1, search_y1;
1118         int search_x2, search_y2;
1119
1120
1121 // always processing global
1122 //      if(config.global)
1123         if(1)
1124         {
1125 // Get vector
1126 // Start of vector is center of previous block.
1127 // End of vector is total accumulation.
1128                 if(config.tracking_object == MotionHVScan::TRACK_SINGLE)
1129                 {
1130                         global_x1 = (int64_t)(config.block_x *
1131                                 w /
1132                                 100);
1133                         global_y1 = (int64_t)(config.block_y *
1134                                 h /
1135                                 100);
1136                         global_x2 = global_x1 + total_dx / OVERSAMPLE;
1137                         global_y2 = global_y1 + total_dy / OVERSAMPLE;
1138 //printf("MotionHVMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1139                 }
1140                 else
1141 // Start of vector is center of previous block.
1142 // End of vector is current change.
1143                 if(config.tracking_object == MotionHVScan::PREVIOUS_SAME_BLOCK)
1144                 {
1145                         global_x1 = (int64_t)(config.block_x *
1146                                 w /
1147                                 100);
1148                         global_y1 = (int64_t)(config.block_y *
1149                                 h /
1150                                 100);
1151                         global_x2 = global_x1 + current_dx / OVERSAMPLE;
1152                         global_y2 = global_y1 + current_dy / OVERSAMPLE;
1153                 }
1154                 else
1155                 {
1156                         global_x1 = (int64_t)(config.block_x *
1157                                 w /
1158                                 100 +
1159                                 (total_dx - current_dx) /
1160                                 OVERSAMPLE);
1161                         global_y1 = (int64_t)(config.block_y *
1162                                 h /
1163                                 100 +
1164                                 (total_dy - current_dy) /
1165                                 OVERSAMPLE);
1166                         global_x2 = (int64_t)(config.block_x *
1167                                 w /
1168                                 100 +
1169                                 total_dx /
1170                                 OVERSAMPLE);
1171                         global_y2 = (int64_t)(config.block_y *
1172                                 h /
1173                                 100 +
1174                                 total_dy /
1175                                 OVERSAMPLE);
1176                 }
1177
1178                 block_x = global_x1;
1179                 block_y = global_y1;
1180                 block_w = config.global_block_w * w / 100;
1181                 block_h = config.global_block_h * h / 100;
1182                 block_x1 = block_x - block_w / 2;
1183                 block_y1 = block_y - block_h / 2;
1184                 block_x2 = block_x + block_w / 2;
1185                 block_y2 = block_y + block_h / 2;
1186                 search_w = config.global_range_w * w / 100;
1187                 search_h = config.global_range_h * h / 100;
1188                 search_x1 = block_x1 - search_w / 2;
1189                 search_y1 = block_y1 - search_h / 2;
1190                 search_x2 = block_x2 + search_w / 2;
1191                 search_y2 = block_y2 + search_h / 2;
1192
1193 // printf("MotionHVMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1194 // global_x1,
1195 // global_y1,
1196 // block_w,
1197 // block_h,
1198 // block_x1,
1199 // block_y1,
1200 // block_x2,
1201 // block_y2,
1202 // search_x1,
1203 // search_y1,
1204 // search_x2,
1205 // search_y2);
1206
1207                 MotionHVScan::clamp_scan(w,
1208                         h,
1209                         &block_x1,
1210                         &block_y1,
1211                         &block_x2,
1212                         &block_y2,
1213                         &search_x1,
1214                         &search_y1,
1215                         &search_x2,
1216                         &search_y2,
1217                         1);
1218
1219 // Vector
1220                 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1221
1222 // Macroblock
1223                 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1224                 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1225                 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1226                 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1227
1228
1229 // Search area
1230                 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1231                 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1232                 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1233                 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1234
1235 // Block should be endpoint of motion
1236                 if(config.rotate)
1237                 {
1238                         block_x = global_x2;
1239                         block_y = global_y2;
1240                 }
1241         }
1242         else
1243         {
1244                 block_x = (int64_t)(config.block_x * w / 100);
1245                 block_y = (int64_t)(config.block_y * h / 100);
1246         }
1247
1248         block_w = config.global_block_w * w / 100;
1249         block_h = config.global_block_h * h / 100;
1250         if(config.rotate)
1251         {
1252                 float angle = total_angle * 2 * M_PI / 360;
1253                 double base_angle1 = atan((float)block_h / block_w);
1254                 double base_angle2 = atan((float)block_w / block_h);
1255                 double target_angle1 = base_angle1 + angle;
1256                 double target_angle2 = base_angle2 + angle;
1257                 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1258                 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1259                 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1260                 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1261                 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1262                 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1263                 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1264                 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1265                 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1266
1267                 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1268                 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1269                 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1270                 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1271
1272
1273 // Center
1274                 if(!config.global)
1275                 {
1276                         draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1277                         draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1278                 }
1279         }
1280 }
1281
1282
1283
1284 void MotionHVMain::draw_pixel(VFrame *frame, int x, int y)
1285 {
1286         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1287
1288 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1289 { \
1290         type **rows = (type**)frame->get_rows(); \
1291         rows[y][x * components] = max - rows[y][x * components]; \
1292         if(!do_yuv) \
1293         { \
1294                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1295                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1296         } \
1297         else \
1298         { \
1299                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1300                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1301         } \
1302         if(components == 4) \
1303                 rows[y][x * components + 3] = max; \
1304 }
1305
1306
1307         switch(frame->get_color_model())
1308         {
1309                 case BC_RGB888:
1310                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1311                         break;
1312                 case BC_RGBA8888:
1313                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1314                         break;
1315                 case BC_RGB_FLOAT:
1316                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1317                         break;
1318                 case BC_RGBA_FLOAT:
1319                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1320                         break;
1321                 case BC_YUV888:
1322                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1323                         break;
1324                 case BC_YUVA8888:
1325                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1326                         break;
1327                 case BC_RGB161616:
1328                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1329                         break;
1330                 case BC_YUV161616:
1331                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1332                         break;
1333                 case BC_RGBA16161616:
1334                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1335                         break;
1336                 case BC_YUVA16161616:
1337                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1338                         break;
1339         }
1340 }
1341
1342
1343 void MotionHVMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1344 {
1345         int w = labs(x2 - x1);
1346         int h = labs(y2 - y1);
1347 //printf("MotionHVMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1348
1349         if(!w && !h)
1350         {
1351                 draw_pixel(frame, x1, y1);
1352         }
1353         else
1354         if(w > h)
1355         {
1356 // Flip coordinates so x1 < x2
1357                 if(x2 < x1)
1358                 {
1359                         y2 ^= y1;
1360                         y1 ^= y2;
1361                         y2 ^= y1;
1362                         x1 ^= x2;
1363                         x2 ^= x1;
1364                         x1 ^= x2;
1365                 }
1366                 int numerator = y2 - y1;
1367                 int denominator = x2 - x1;
1368                 for(int i = x1; i < x2; i++)
1369                 {
1370                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1371                         draw_pixel(frame, i, y);
1372                 }
1373         }
1374         else
1375         {
1376 // Flip coordinates so y1 < y2
1377                 if(y2 < y1)
1378                 {
1379                         y2 ^= y1;
1380                         y1 ^= y2;
1381                         y2 ^= y1;
1382                         x1 ^= x2;
1383                         x2 ^= x1;
1384                         x1 ^= x2;
1385                 }
1386                 int numerator = x2 - x1;
1387                 int denominator = y2 - y1;
1388                 for(int i = y1; i < y2; i++)
1389                 {
1390                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1391                         draw_pixel(frame, x, i);
1392                 }
1393         }
1394 //printf("MotionHVMain::draw_line 2\n");
1395 }
1396
1397 #define ARROW_SIZE 10
1398 void MotionHVMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1399 {
1400         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1401         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1402         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1403         int x3;
1404         int y3;
1405         int x4;
1406         int y4;
1407         if(x2 < x1)
1408         {
1409                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1410                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1411                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1412                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1413         }
1414         else
1415         {
1416                 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1417                 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1418                 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1419                 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1420         }
1421
1422 // Main vector
1423         draw_line(frame, x1, y1, x2, y2);
1424 //      draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1425
1426 // Arrow line
1427         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1428 //      draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1429 // Arrow line
1430         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1431 //      draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1432 }
1433
1434
1435
1436
1437
1438
1439