affine + perspective plugin rework, info popup tweaks
[goodguy/history.git] / cinelerra-5.1 / plugins / perspective / perspective.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "affine.h"
23 #include "cursors.h"
24 #include "language.h"
25 #include "perspective.h"
26
27
28
29
30
31
32
33 REGISTER_PLUGIN(PerspectiveMain)
34
35
36
37 PerspectiveConfig::PerspectiveConfig()
38 {
39         x1 = 0;    y1 = 0;
40         x2 = 100;  y2 = 0;
41         x3 = 100;  y3 = 100;
42         x4 = 0;    y4 = 100;
43         mode = AffineEngine::PERSPECTIVE;
44         smoothing = AffineEngine::AF_DEFAULT;
45         window_w = 400;
46         window_h = 450;
47         current_point = 0;
48         forward = 1;
49 }
50
51 int PerspectiveConfig::equivalent(PerspectiveConfig &that)
52 {
53         return
54                 EQUIV(x1, that.x1) &&
55                 EQUIV(y1, that.y1) &&
56                 EQUIV(x2, that.x2) &&
57                 EQUIV(y2, that.y2) &&
58                 EQUIV(x3, that.x3) &&
59                 EQUIV(y3, that.y3) &&
60                 EQUIV(x4, that.x4) &&
61                 EQUIV(y4, that.y4) &&
62                 mode == that.mode &&
63                 smoothing == that.smoothing &&
64                 forward == that.forward;
65 }
66
67 void PerspectiveConfig::copy_from(PerspectiveConfig &that)
68 {
69         x1 = that.x1;  y1 = that.y1;
70         x2 = that.x2;  y2 = that.y2;
71         x3 = that.x3;  y3 = that.y3;
72         x4 = that.x4;  y4 = that.y4;
73         mode = that.mode;
74         smoothing = that.smoothing;
75         window_w = that.window_w;
76         window_h = that.window_h;
77         current_point = that.current_point;
78         forward = that.forward;
79 }
80
81 void PerspectiveConfig::interpolate(PerspectiveConfig &prev, PerspectiveConfig &next,
82         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
83 {
84         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
85         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
86         this->x1 = prev.x1 * prev_scale + next.x1 * next_scale;
87         this->y1 = prev.y1 * prev_scale + next.y1 * next_scale;
88         this->x2 = prev.x2 * prev_scale + next.x2 * next_scale;
89         this->y2 = prev.y2 * prev_scale + next.y2 * next_scale;
90         this->x3 = prev.x3 * prev_scale + next.x3 * next_scale;
91         this->y3 = prev.y3 * prev_scale + next.y3 * next_scale;
92         this->x4 = prev.x4 * prev_scale + next.x4 * next_scale;
93         this->y4 = prev.y4 * prev_scale + next.y4 * next_scale;
94         mode = prev.mode;
95         smoothing = prev.smoothing;
96         forward = prev.forward;
97 }
98
99
100 PerspectiveWindow::PerspectiveWindow(PerspectiveMain *plugin)
101  : PluginClientWindow(plugin,
102         plugin->config.window_w, plugin->config.window_h,
103         plugin->config.window_w, plugin->config.window_h, 0)
104 {
105 //printf("PerspectiveWindow::PerspectiveWindow 1 %d %d\n", plugin->config.window_w, plugin->config.window_h);
106         this->plugin = plugin;
107 }
108
109 PerspectiveWindow::~PerspectiveWindow()
110 {
111 }
112
113 void PerspectiveWindow::create_objects()
114 {
115         int x = 10, y = 10;
116
117         add_subwindow(canvas = new PerspectiveCanvas(plugin,
118                 x, y, get_w() - 20, get_h() - 140));
119         canvas->set_cursor(CROSS_CURSOR, 0, 0);
120         y += canvas->get_h() + 10;
121         add_subwindow(new BC_Title(x, y, _("Current X:")));
122         x += 80;
123         this->x = new PerspectiveCoord(this,
124                 plugin, x, y, plugin->get_current_x(), 1);
125         this->x->create_objects();
126         x += 140;
127         add_subwindow(new BC_Title(x, y, _("Y:")));
128         x += 20;
129         this->y = new PerspectiveCoord(this,
130                 plugin, x, y, plugin->get_current_y(), 0);
131         this->y->create_objects();
132         x = 10;   y += 30;
133         add_subwindow(mode_perspective = new PerspectiveMode(plugin,
134                 x, y, AffineEngine::PERSPECTIVE, _("Perspective")));
135         x += 120;
136         add_subwindow(mode_sheer = new PerspectiveMode(plugin,
137                 x, y, AffineEngine::SHEER, _("Sheer")));
138         x += 100;
139         add_subwindow(affine = new PerspectiveAffine(this, x, y));
140         affine->create_objects();
141         x = 10;  y += 30;
142         add_subwindow(mode_stretch = new PerspectiveMode(plugin,
143                 x, y, AffineEngine::STRETCH, _("Stretch")));
144         x += 120;
145         add_subwindow(new PerspectiveReset(plugin, x, y));
146         update_canvas();
147         x = 10;   y += 30;
148         add_subwindow(new BC_Title(x, y, _("Perspective direction:")));
149         x += 170;
150         add_subwindow(forward = new PerspectiveDirection(plugin,
151                 x, y, 1, _("Forward")));
152         x += 100;
153         add_subwindow(reverse = new PerspectiveDirection(plugin,
154                 x, y, 0, _("Reverse")));
155
156         show_window();
157 }
158
159
160
161 int PerspectiveWindow::resize_event(int w, int h)
162 {
163         return 1;
164 }
165
166 void PerspectiveWindow::update_canvas()
167 {
168         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
169         int x1, y1, x2, y2, x3, y3, x4, y4;
170         calculate_canvas_coords(x1, y1, x2, y2, x3, y3, x4, y4);
171
172 //printf("PerspectiveWindow::update_canvas %d,%d %d,%d %d,%d %d,%d\n",
173 // x1, y1, x2, y2, x3, y3, x4, y4);
174 // Draw divisions
175         canvas->set_color(WHITE);
176
177 #define DIVISIONS 10
178         for( int i=0; i<=DIVISIONS; ++i ) {
179                 canvas->draw_line( // latitude
180                         x1 + (x4 - x1) * i / DIVISIONS,
181                         y1 + (y4 - y1) * i / DIVISIONS,
182                         x2 + (x3 - x2) * i / DIVISIONS,
183                         y2 + (y3 - y2) * i / DIVISIONS);
184                 canvas->draw_line( // longitude
185                         x1 + (x2 - x1) * i / DIVISIONS,
186                         y1 + (y2 - y1) * i / DIVISIONS,
187                         x4 + (x3 - x4) * i / DIVISIONS,
188                         y4 + (y3 - y4) * i / DIVISIONS);
189         }
190
191 // Corners
192 #define RADIUS 5
193         if(plugin->config.current_point == 0)
194                 canvas->draw_disc(x1 - RADIUS, y1 - RADIUS, RADIUS * 2, RADIUS * 2);
195         else
196                 canvas->draw_circle(x1 - RADIUS, y1 - RADIUS, RADIUS * 2, RADIUS * 2);
197
198         if(plugin->config.current_point == 1)
199                 canvas->draw_disc(x2 - RADIUS, y2 - RADIUS, RADIUS * 2, RADIUS * 2);
200         else
201                 canvas->draw_circle(x2 - RADIUS, y2 - RADIUS, RADIUS * 2, RADIUS * 2);
202
203         if(plugin->config.current_point == 2)
204                 canvas->draw_disc(x3 - RADIUS, y3 - RADIUS, RADIUS * 2, RADIUS * 2);
205         else
206                 canvas->draw_circle(x3 - RADIUS, y3 - RADIUS, RADIUS * 2, RADIUS * 2);
207
208         if(plugin->config.current_point == 3)
209                 canvas->draw_disc(x4 - RADIUS, y4 - RADIUS, RADIUS * 2, RADIUS * 2);
210         else
211                 canvas->draw_circle(x4 - RADIUS, y4 - RADIUS, RADIUS * 2, RADIUS * 2);
212
213         canvas->flash();
214 }
215
216 void PerspectiveWindow::update_mode()
217 {
218         mode_perspective->update(plugin->config.mode == AffineEngine::PERSPECTIVE);
219         mode_sheer->update(plugin->config.mode == AffineEngine::SHEER);
220         mode_stretch->update(plugin->config.mode == AffineEngine::STRETCH);
221         forward->update(plugin->config.forward);
222         reverse->update(!plugin->config.forward);
223 }
224
225 void PerspectiveWindow::update_coord()
226 {
227         x->update(plugin->get_current_x());
228         y->update(plugin->get_current_y());
229 }
230
231 void PerspectiveWindow::calculate_canvas_coords(
232         int &x1, int &y1, int &x2, int &y2,
233         int &x3, int &y3, int &x4, int &y4)
234 {
235         int w = canvas->get_w() - 1;
236         int h = canvas->get_h() - 1;
237         if( plugin->config.mode == AffineEngine::PERSPECTIVE ||
238             plugin->config.mode == AffineEngine::STRETCH ) {
239                 x1 = (int)(plugin->config.x1 * w / 100);
240                 y1 = (int)(plugin->config.y1 * h / 100);
241                 x2 = (int)(plugin->config.x2 * w / 100);
242                 y2 = (int)(plugin->config.y2 * h / 100);
243                 x3 = (int)(plugin->config.x3 * w / 100);
244                 y3 = (int)(plugin->config.y3 * h / 100);
245                 x4 = (int)(plugin->config.x4 * w / 100);
246                 y4 = (int)(plugin->config.y4 * h / 100);
247         }
248         else {
249                 x1 = (int)(plugin->config.x1 * w) / 100;
250                 y1 = 0;
251                 x2 = x1 + w;
252                 y2 = 0;
253                 x4 = (int)(plugin->config.x4 * w) / 100;
254                 y4 = h;
255                 x3 = x4 + w;
256                 y3 = h;
257         }
258 }
259
260
261 PerspectiveCanvas::PerspectiveCanvas(PerspectiveMain *plugin,
262         int x, int y, int w, int h)
263  : BC_SubWindow(x, y, w, h, BLACK)
264 {
265         this->plugin = plugin;
266         state = PerspectiveCanvas::NONE;
267 }
268
269
270 int PerspectiveCanvas::button_press_event()
271 {
272         if( is_event_win() && cursor_inside() ) {
273 // Set current point
274                 int x1, y1, x2, y2, x3, y3, x4, y4;
275                 int cursor_x = get_cursor_x();
276                 int cursor_y = get_cursor_y();
277                 ((PerspectiveWindow*)plugin->thread->window)->calculate_canvas_coords(x1, y1, x2, y2, x3, y3, x4, y4);
278
279                 float distance1 = DISTANCE(cursor_x, cursor_y, x1, y1);
280                 float distance2 = DISTANCE(cursor_x, cursor_y, x2, y2);
281                 float distance3 = DISTANCE(cursor_x, cursor_y, x3, y3);
282                 float distance4 = DISTANCE(cursor_x, cursor_y, x4, y4);
283 // printf("PerspectiveCanvas::button_press_event %f %d %d %d %d\n",
284 // distance3,
285 // cursor_x,
286 // cursor_y,
287 // x3,
288 // y3);
289                 float min = distance1;
290                 plugin->config.current_point = 0;
291                 if( distance2 < min ) {
292                         min = distance2;
293                         plugin->config.current_point = 1;
294                 }
295                 if( distance3 < min ) {
296                         min = distance3;
297                         plugin->config.current_point = 2;
298                 }
299                 if( distance4 < min ) {
300                         min = distance4;
301                         plugin->config.current_point = 3;
302                 }
303
304                 if( plugin->config.mode == AffineEngine::SHEER ) {
305                         if( plugin->config.current_point == 1 )
306                                 plugin->config.current_point = 0;
307                         else if( plugin->config.current_point == 2 )
308                                 plugin->config.current_point = 3;
309                 }
310                 start_cursor_x = cursor_x;
311                 start_cursor_y = cursor_y;
312
313                 if( alt_down() || shift_down() ) {
314                         state =  alt_down() ?
315                                 PerspectiveCanvas::DRAG_FULL :
316                                 PerspectiveCanvas::ZOOM;
317 // Get starting positions
318                         start_x1 = plugin->config.x1;
319                         start_y1 = plugin->config.y1;
320                         start_x2 = plugin->config.x2;
321                         start_y2 = plugin->config.y2;
322                         start_x3 = plugin->config.x3;
323                         start_y3 = plugin->config.y3;
324                         start_x4 = plugin->config.x4;
325                         start_y4 = plugin->config.y4;
326                 }
327                 else {
328                         state = PerspectiveCanvas::DRAG;
329 // Get starting positions
330                         start_x1 = plugin->get_current_x();
331                         start_y1 = plugin->get_current_y();
332                 }
333                 ((PerspectiveWindow*)plugin->thread->window)->update_coord();
334                 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
335                 return 1;
336         }
337
338         return 0;
339 }
340
341 int PerspectiveCanvas::button_release_event()
342 {
343         if( state != PerspectiveCanvas::NONE ) {
344                 state = PerspectiveCanvas::NONE;
345                 return 1;
346         }
347         return 0;
348 }
349
350 int PerspectiveCanvas::cursor_motion_event()
351 {
352         if( state != PerspectiveCanvas::NONE ) {
353                 int w = get_w() - 1;
354                 int h = get_h() - 1;
355                 if( state == PerspectiveCanvas::DRAG ) {
356                         plugin->set_current_x((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x1);
357                         plugin->set_current_y((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y1);
358                 }
359                 else if( state == PerspectiveCanvas::DRAG_FULL ) {
360                         plugin->config.x1 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x1);
361                         plugin->config.y1 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y1);
362                         plugin->config.x2 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x2);
363                         plugin->config.y2 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y2);
364                         plugin->config.x3 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x3);
365                         plugin->config.y3 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y3);
366                         plugin->config.x4 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x4);
367                         plugin->config.y4 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y4);
368                 }
369                 else if( state == PerspectiveCanvas::ZOOM ) {
370                         float center_x = (start_x1 + start_x2 + start_x3 + start_x4) / 4;
371                         float center_y = (start_y1 + start_y2 + start_y3 + start_y4) / 4;
372                         float zoom = (float)(get_cursor_y() - start_cursor_y + 640) / 640;
373                         plugin->config.x1 = center_x + (start_x1 - center_x) * zoom;
374                         plugin->config.y1 = center_y + (start_y1 - center_y) * zoom;
375                         plugin->config.x2 = center_x + (start_x2 - center_x) * zoom;
376                         plugin->config.y2 = center_y + (start_y2 - center_y) * zoom;
377                         plugin->config.x3 = center_x + (start_x3 - center_x) * zoom;
378                         plugin->config.y3 = center_y + (start_y3 - center_y) * zoom;
379                         plugin->config.x4 = center_x + (start_x4 - center_x) * zoom;
380                         plugin->config.y4 = center_y + (start_y4 - center_y) * zoom;
381                 }
382                 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
383                 ((PerspectiveWindow*)plugin->thread->window)->update_coord();
384                 plugin->send_configure_change();
385                 return 1;
386         }
387
388         return 0;
389 }
390
391
392 PerspectiveCoord::PerspectiveCoord(PerspectiveWindow *gui,
393         PerspectiveMain *plugin, int x, int y, float value, int is_x)
394  : BC_TumbleTextBox(gui, value, (float)-100, (float)200, x, y, 100)
395 {
396         this->plugin = plugin;
397         this->is_x = is_x;
398 }
399
400 int PerspectiveCoord::handle_event()
401 {
402         float v = atof(get_text());
403         if( is_x )
404                 plugin->set_current_x(v);
405         else
406                 plugin->set_current_y(v);
407         ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
408         plugin->send_configure_change();
409         return 1;
410 }
411
412
413 PerspectiveReset::PerspectiveReset(PerspectiveMain *plugin, int x, int y)
414  : BC_GenericButton(x, y, _("Reset"))
415 {
416         this->plugin = plugin;
417 }
418 int PerspectiveReset::handle_event()
419 {
420         plugin->config.x1 = 0;    plugin->config.y1 = 0;
421         plugin->config.x2 = 100;  plugin->config.y2 = 0;
422         plugin->config.x3 = 100;  plugin->config.y3 = 100;
423         plugin->config.x4 = 0;    plugin->config.y4 = 100;
424         ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
425         ((PerspectiveWindow*)plugin->thread->window)->update_coord();
426         plugin->send_configure_change();
427         return 1;
428 }
429
430
431 PerspectiveMode::PerspectiveMode(PerspectiveMain *plugin,
432         int x, int y, int value, char *text)
433  : BC_Radial(x, y, plugin->config.mode == value, text)
434 {
435         this->plugin = plugin;
436         this->value = value;
437 }
438 int PerspectiveMode::handle_event()
439 {
440         plugin->config.mode = value;
441         ((PerspectiveWindow*)plugin->thread->window)->update_mode();
442         ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
443         plugin->send_configure_change();
444         return 1;
445 }
446
447
448 PerspectiveDirection::PerspectiveDirection(PerspectiveMain *plugin,
449         int x,
450         int y,
451         int value,
452         char *text)
453  : BC_Radial(x, y, plugin->config.forward == value, text)
454 {
455         this->plugin = plugin;
456         this->value = value;
457 }
458 int PerspectiveDirection::handle_event()
459 {
460         plugin->config.forward = value;
461         ((PerspectiveWindow*)plugin->thread->window)->update_mode();
462         plugin->send_configure_change();
463         return 1;
464 }
465
466
467 int PerspectiveAffineItem::handle_event()
468 {
469         ((PerspectiveAffine *)get_popup_menu())->update(id);
470         return 1;
471 }
472
473 PerspectiveAffine::PerspectiveAffine(PerspectiveWindow *gui, int x, int y)
474  : BC_PopupMenu(x, y, 100, "", 1)
475 {
476         this->gui = gui;
477         affine_modes[AffineEngine::AF_DEFAULT]     = _("default");
478         affine_modes[AffineEngine::AF_NEAREST]     = _("Nearest");
479         affine_modes[AffineEngine::AF_LINEAR]      = _("Linear");
480         affine_modes[AffineEngine::AF_CUBIC]       = _("Cubic");
481         mode = -1;
482 }
483 PerspectiveAffine::~PerspectiveAffine()
484 {
485         int id = total_items();
486         while( --id >= 0 )
487                 remove_item(get_item(id));
488         for( int id=0; id<n_modes; ++id )
489                 delete affine_items[id];
490 }
491 void PerspectiveAffine::affine_item(int id)
492 {
493         affine_items[id] = new PerspectiveAffineItem(affine_modes[id], id);
494         add_item(affine_items[id]);
495 }
496
497 void PerspectiveAffine::create_objects()
498 {
499         affine_item(AffineEngine::AF_DEFAULT);
500         affine_item(AffineEngine::AF_NEAREST);
501         affine_item(AffineEngine::AF_LINEAR);
502         affine_item(AffineEngine::AF_CUBIC);
503         update(gui->plugin->config.smoothing, 0);
504 }
505
506 void PerspectiveAffine::update(int mode, int send)
507 {
508         if( this->mode == mode ) return;
509         this->mode = mode;
510         set_text(affine_modes[mode]);
511         gui->plugin->config.smoothing = mode;
512         if( send ) gui->plugin->send_configure_change();
513 }
514
515 PerspectiveMain::PerspectiveMain(PluginServer *server)
516  : PluginVClient(server)
517 {
518
519         engine = 0;
520         temp = 0;
521 }
522
523 PerspectiveMain::~PerspectiveMain()
524 {
525
526         if(engine) delete engine;
527         if(temp) delete temp;
528 }
529
530 const char* PerspectiveMain::plugin_title() { return _("Perspective"); }
531 int PerspectiveMain::is_realtime() { return 1; }
532
533
534 NEW_WINDOW_MACRO(PerspectiveMain, PerspectiveWindow)
535
536 LOAD_CONFIGURATION_MACRO(PerspectiveMain, PerspectiveConfig)
537
538 void PerspectiveMain::update_gui()
539 {
540         if( !thread ) return;
541 //printf("PerspectiveMain::update_gui 1\n");
542         thread->window->lock_window();
543         PerspectiveWindow *gui = (PerspectiveWindow*)thread->window;
544 //printf("PerspectiveMain::update_gui 2\n");
545         load_configuration();
546         gui->update_coord();
547         gui->update_mode();
548         gui->update_canvas();
549         thread->window->unlock_window();
550 //printf("PerspectiveMain::update_gui 3\n");
551 }
552
553
554
555
556
557 void PerspectiveMain::save_data(KeyFrame *keyframe)
558 {
559         FileXML output;
560
561 // cause data to be stored directly in text
562         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
563         output.tag.set_title("PERSPECTIVE");
564
565         output.tag.set_property("X1", config.x1);
566         output.tag.set_property("X2", config.x2);
567         output.tag.set_property("X3", config.x3);
568         output.tag.set_property("X4", config.x4);
569         output.tag.set_property("Y1", config.y1);
570         output.tag.set_property("Y2", config.y2);
571         output.tag.set_property("Y3", config.y3);
572         output.tag.set_property("Y4", config.y4);
573
574         output.tag.set_property("MODE", config.mode);
575         output.tag.set_property("SMOOTHING", config.smoothing);
576         output.tag.set_property("FORWARD", config.forward);
577         output.tag.set_property("WINDOW_W", config.window_w);
578         output.tag.set_property("WINDOW_H", config.window_h);
579         output.append_tag();
580         output.tag.set_title("/PERSPECTIVE");
581         output.append_tag();
582         output.append_newline();
583         output.terminate_string();
584 }
585
586 void PerspectiveMain::read_data(KeyFrame *keyframe)
587 {
588         FileXML input;
589         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
590         int result = 0;
591
592         while(!(result = input.read_tag()) ) {
593                 if(input.tag.title_is("PERSPECTIVE")) {
594                         config.x1 = input.tag.get_property("X1", config.x1);
595                         config.x2 = input.tag.get_property("X2", config.x2);
596                         config.x3 = input.tag.get_property("X3", config.x3);
597                         config.x4 = input.tag.get_property("X4", config.x4);
598                         config.y1 = input.tag.get_property("Y1", config.y1);
599                         config.y2 = input.tag.get_property("Y2", config.y2);
600                         config.y3 = input.tag.get_property("Y3", config.y3);
601                         config.y4 = input.tag.get_property("Y4", config.y4);
602
603                         config.mode = input.tag.get_property("MODE", config.mode);
604                         config.smoothing = input.tag.get_property("SMOOTHING", config.smoothing);
605                         config.forward = input.tag.get_property("FORWARD", config.forward);
606                         config.window_w = input.tag.get_property("WINDOW_W", config.window_w);
607                         config.window_h = input.tag.get_property("WINDOW_H", config.window_h);
608                 }
609         }
610 }
611
612 float PerspectiveMain::get_current_x()
613 {
614         switch( config.current_point ) {
615         case 0: return config.x1;
616         case 1: return config.x2;
617         case 2: return config.x3;
618         case 3: return config.x4;
619         }
620         return 0;
621 }
622
623 float PerspectiveMain::get_current_y()
624 {
625         switch( config.current_point ) {
626         case 0: return config.y1;
627         case 1: return config.y2;
628         case 2: return config.y3;
629         case 3: return config.y4;
630         }
631         return 0;
632 }
633
634 void PerspectiveMain::set_current_x(float value)
635 {
636         switch( config.current_point ) {
637         case 0: config.x1 = value; break;
638         case 1: config.x2 = value; break;
639         case 2: config.x3 = value; break;
640         case 3: config.x4 = value; break;
641         }
642 }
643
644 void PerspectiveMain::set_current_y(float value)
645 {
646         switch( config.current_point ) {
647         case 0: config.y1 = value; break;
648         case 1: config.y2 = value; break;
649         case 2: config.y3 = value; break;
650         case 3: config.y4 = value; break;
651         }
652 }
653
654 int PerspectiveMain::process_buffer(VFrame *frame,
655         int64_t start_position, double frame_rate)
656 {
657         /*int need_reconfigure =*/ load_configuration();
658         int smoothing = config.smoothing;
659 // default smoothing uses opengl if possible
660         int use_opengl = smoothing != AffineEngine::AF_DEFAULT ? 0 :
661 // Opengl does some funny business with stretching.
662                 config.mode == AffineEngine::PERSPECTIVE ||
663                 config.mode == AffineEngine::SHEER ? get_use_opengl() : 0;
664
665         read_frame(frame, 0, start_position, frame_rate, use_opengl);
666
667 // Do nothing
668
669         if( EQUIV(config.x1, 0)   && EQUIV(config.y1, 0) &&
670             EQUIV(config.x2, 100) && EQUIV(config.y2, 0) &&
671             EQUIV(config.x3, 100) && EQUIV(config.y3, 100) &&
672             EQUIV(config.x4, 0)   && EQUIV(config.y4, 100) )
673                 return 1;
674
675         if( !engine ) {
676                 int cpus = get_project_smp() + 1;
677                 engine = new AffineEngine(cpus, cpus);
678         }
679         engine->set_interpolation(smoothing);
680
681         if( use_opengl )
682                 return run_opengl();
683
684         this->input = frame;
685         this->output = frame;
686
687         int w = frame->get_w(), need_w = w;
688         int h = frame->get_h(), need_h = h;
689         int color_model = frame->get_color_model();
690         switch( config.mode ) {
691         case AffineEngine::STRETCH:
692                 need_w *= AFFINE_OVERSAMPLE;
693                 need_h *= AFFINE_OVERSAMPLE;
694         case AffineEngine::SHEER:
695         case AffineEngine::PERSPECTIVE:
696                 if( temp ) {
697                         if( temp->get_w() != need_w || temp->get_h() != need_h ||
698                             temp->get_color_model() != color_model ) {
699                                 delete temp;  temp = 0;
700                         }
701                 }
702                 if( !temp )
703                         temp = new VFrame(need_w, need_h, color_model);
704                 break;
705         }
706         switch( config.mode ) {
707         case AffineEngine::STRETCH:
708                 temp->clear_frame();
709                 break;
710         case AffineEngine::PERSPECTIVE:
711         case AffineEngine::SHEER:
712                 temp->copy_from(input);
713                 input = temp;
714                 output->clear_frame();
715                 break;
716         default:
717                 delete temp;  temp = 0;
718                 break;
719         }
720
721         engine->process(output, input, temp, config.mode,
722                 config.x1, config.y1, config.x2, config.y2,
723                 config.x3, config.y3, config.x4, config.y4,
724                 config.forward);
725
726 // Resample
727
728         if( config.mode == AffineEngine::STRETCH ) {
729
730 #define RESAMPLE(tag, type, components, chroma_offset) \
731 case tag: { \
732     int os = AFFINE_OVERSAMPLE, os2 = os*os; \
733     for( int i=0; i<h; ++i ) { \
734         type *out_row = (type*)output->get_rows()[i]; \
735         type *in_row1 = (type*)temp->get_rows()[i * os]; \
736         type *in_row2 = (type*)temp->get_rows()[i * os + 1]; \
737         for( int j=0; j<w; ++j ) { \
738             out_row[0] = \
739                 ( in_row1[0] + in_row1[components + 0] +  \
740                   in_row2[0] + in_row2[components + 0] ) / os2; \
741             out_row[1] = \
742                 ( in_row1[1] + in_row1[components + 1] + \
743                   in_row2[1] + in_row2[components + 1] ) / os2; \
744             out_row[2] = \
745                 ( in_row1[2] + in_row1[components + 2] + \
746                   in_row2[2] + in_row2[components + 2] ) / os2; \
747             if( components == 4 ) { \
748                 out_row[3] = \
749                     ( in_row1[3] + in_row1[components + 3] +  \
750                       in_row2[3] + in_row2[components + 3] ) / os2; \
751             } \
752             out_row += components; \
753             in_row1 += components * os; \
754             in_row2 += components * os; \
755         } \
756     } \
757 } break
758
759                 switch( frame->get_color_model() ) {
760                 RESAMPLE( BC_RGB_FLOAT, float, 3, 0 );
761                 RESAMPLE( BC_RGB888, unsigned char, 3, 0 );
762                 RESAMPLE( BC_RGBA_FLOAT, float, 4, 0 );
763                 RESAMPLE( BC_RGBA8888, unsigned char, 4, 0 );
764                 RESAMPLE( BC_YUV888, unsigned char, 3, 0x80 );
765                 RESAMPLE( BC_YUVA8888, unsigned char, 4, 0x80 );
766                 RESAMPLE( BC_RGB161616, uint16_t, 3, 0 );
767                 RESAMPLE( BC_RGBA16161616, uint16_t, 4, 0 );
768                 RESAMPLE( BC_YUV161616, uint16_t, 3, 0x8000 );
769                 RESAMPLE( BC_YUVA16161616, uint16_t, 4, 0x8000 );
770                 }
771         }
772
773         return 1;
774 }
775
776
777 int PerspectiveMain::handle_opengl()
778 {
779 #ifdef HAVE_GL
780         engine->set_opengl(1);
781         engine->process(get_output(), get_output(), get_output(), config.mode,
782                 config.x1, config.y1, config.x2, config.y2,
783                 config.x3, config.y3, config.x4, config.y4,
784                 config.forward);
785         engine->set_opengl(0);
786         return 0;
787 #endif
788 }
789