4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
25 #include "perspective.h"
33 REGISTER_PLUGIN(PerspectiveMain)
37 PerspectiveConfig::PerspectiveConfig()
47 mode = AffineEngine::PERSPECTIVE;
54 int PerspectiveConfig::equivalent(PerspectiveConfig &that)
66 forward == that.forward;
69 void PerspectiveConfig::copy_from(PerspectiveConfig &that)
80 window_w = that.window_w;
81 window_h = that.window_h;
82 current_point = that.current_point;
83 forward = that.forward;
86 void PerspectiveConfig::interpolate(PerspectiveConfig &prev,
87 PerspectiveConfig &next,
90 int64_t current_frame)
92 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
93 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
94 this->x1 = prev.x1 * prev_scale + next.x1 * next_scale;
95 this->y1 = prev.y1 * prev_scale + next.y1 * next_scale;
96 this->x2 = prev.x2 * prev_scale + next.x2 * next_scale;
97 this->y2 = prev.y2 * prev_scale + next.y2 * next_scale;
98 this->x3 = prev.x3 * prev_scale + next.x3 * next_scale;
99 this->y3 = prev.y3 * prev_scale + next.y3 * next_scale;
100 this->x4 = prev.x4 * prev_scale + next.x4 * next_scale;
101 this->y4 = prev.y4 * prev_scale + next.y4 * next_scale;
103 forward = prev.forward;
118 PerspectiveWindow::PerspectiveWindow(PerspectiveMain *plugin)
119 : PluginClientWindow(plugin,
120 plugin->config.window_w,
121 plugin->config.window_h,
122 plugin->config.window_w,
123 plugin->config.window_h,
126 //printf("PerspectiveWindow::PerspectiveWindow 1 %d %d\n", plugin->config.window_w, plugin->config.window_h);
127 this->plugin = plugin;
130 PerspectiveWindow::~PerspectiveWindow()
134 void PerspectiveWindow::create_objects()
138 add_subwindow(canvas = new PerspectiveCanvas(plugin,
143 canvas->set_cursor(CROSS_CURSOR, 0, 0);
144 y += canvas->get_h() + 10;
145 add_subwindow(new BC_Title(x, y, _("Current X:")));
147 this->x = new PerspectiveCoord(this,
151 plugin->get_current_x(),
153 this->x->create_objects();
155 add_subwindow(new BC_Title(x, y, _("Y:")));
157 this->y = new PerspectiveCoord(this,
161 plugin->get_current_y(),
163 this->y->create_objects();
166 add_subwindow(new PerspectiveReset(plugin, x, y));
168 add_subwindow(mode_perspective = new PerspectiveMode(plugin,
171 AffineEngine::PERSPECTIVE,
174 add_subwindow(mode_sheer = new PerspectiveMode(plugin,
181 add_subwindow(mode_stretch = new PerspectiveMode(plugin,
184 AffineEngine::STRETCH,
189 add_subwindow(new BC_Title(x, y, _("Perspective direction:")));
191 add_subwindow(forward = new PerspectiveDirection(plugin,
197 add_subwindow(reverse = new PerspectiveDirection(plugin,
208 int PerspectiveWindow::resize_event(int w, int h)
213 void PerspectiveWindow::update_canvas()
215 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
216 int x1, y1, x2, y2, x3, y3, x4, y4;
217 calculate_canvas_coords(x1, y1, x2, y2, x3, y3, x4, y4);
219 // printf("PerspectiveWindow::update_canvas %d,%d %d,%d %d,%d %d,%d\n",
229 canvas->set_color(WHITE);
232 for(int i = 0; i <= DIVISIONS; i++)
236 x1 + (x4 - x1) * i / DIVISIONS,
237 y1 + (y4 - y1) * i / DIVISIONS,
238 x2 + (x3 - x2) * i / DIVISIONS,
239 y2 + (y3 - y2) * i / DIVISIONS);
242 x1 + (x2 - x1) * i / DIVISIONS,
243 y1 + (y2 - y1) * i / DIVISIONS,
244 x4 + (x3 - x4) * i / DIVISIONS,
245 y4 + (y3 - y4) * i / DIVISIONS);
250 if(plugin->config.current_point == 0)
251 canvas->draw_disc(x1 - RADIUS, y1 - RADIUS, RADIUS * 2, RADIUS * 2);
253 canvas->draw_circle(x1 - RADIUS, y1 - RADIUS, RADIUS * 2, RADIUS * 2);
255 if(plugin->config.current_point == 1)
256 canvas->draw_disc(x2 - RADIUS, y2 - RADIUS, RADIUS * 2, RADIUS * 2);
258 canvas->draw_circle(x2 - RADIUS, y2 - RADIUS, RADIUS * 2, RADIUS * 2);
260 if(plugin->config.current_point == 2)
261 canvas->draw_disc(x3 - RADIUS, y3 - RADIUS, RADIUS * 2, RADIUS * 2);
263 canvas->draw_circle(x3 - RADIUS, y3 - RADIUS, RADIUS * 2, RADIUS * 2);
265 if(plugin->config.current_point == 3)
266 canvas->draw_disc(x4 - RADIUS, y4 - RADIUS, RADIUS * 2, RADIUS * 2);
268 canvas->draw_circle(x4 - RADIUS, y4 - RADIUS, RADIUS * 2, RADIUS * 2);
273 void PerspectiveWindow::update_mode()
275 mode_perspective->update(plugin->config.mode == AffineEngine::PERSPECTIVE);
276 mode_sheer->update(plugin->config.mode == AffineEngine::SHEER);
277 mode_stretch->update(plugin->config.mode == AffineEngine::STRETCH);
278 forward->update(plugin->config.forward);
279 reverse->update(!plugin->config.forward);
282 void PerspectiveWindow::update_coord()
284 x->update(plugin->get_current_x());
285 y->update(plugin->get_current_y());
288 void PerspectiveWindow::calculate_canvas_coords(int &x1,
297 int w = canvas->get_w() - 1;
298 int h = canvas->get_h() - 1;
299 if(plugin->config.mode == AffineEngine::PERSPECTIVE ||
300 plugin->config.mode == AffineEngine::STRETCH)
302 x1 = (int)(plugin->config.x1 * w / 100);
303 y1 = (int)(plugin->config.y1 * h / 100);
304 x2 = (int)(plugin->config.x2 * w / 100);
305 y2 = (int)(plugin->config.y2 * h / 100);
306 x3 = (int)(plugin->config.x3 * w / 100);
307 y3 = (int)(plugin->config.y3 * h / 100);
308 x4 = (int)(plugin->config.x4 * w / 100);
309 y4 = (int)(plugin->config.y4 * h / 100);
313 x1 = (int)(plugin->config.x1 * w) / 100;
317 x4 = (int)(plugin->config.x4 * w) / 100;
327 PerspectiveCanvas::PerspectiveCanvas(PerspectiveMain *plugin,
332 : BC_SubWindow(x, y, w, h, BLACK)
334 this->plugin = plugin;
335 state = PerspectiveCanvas::NONE;
341 int PerspectiveCanvas::button_press_event()
343 if(is_event_win() && cursor_inside())
346 int x1, y1, x2, y2, x3, y3, x4, y4;
347 int cursor_x = get_cursor_x();
348 int cursor_y = get_cursor_y();
349 ((PerspectiveWindow*)plugin->thread->window)->calculate_canvas_coords(x1, y1, x2, y2, x3, y3, x4, y4);
351 float distance1 = DISTANCE(cursor_x, cursor_y, x1, y1);
352 float distance2 = DISTANCE(cursor_x, cursor_y, x2, y2);
353 float distance3 = DISTANCE(cursor_x, cursor_y, x3, y3);
354 float distance4 = DISTANCE(cursor_x, cursor_y, x4, y4);
355 // printf("PerspectiveCanvas::button_press_event %f %d %d %d %d\n",
361 float min = distance1;
362 plugin->config.current_point = 0;
366 plugin->config.current_point = 1;
371 plugin->config.current_point = 2;
376 plugin->config.current_point = 3;
379 if(plugin->config.mode == AffineEngine::SHEER)
381 if(plugin->config.current_point == 1)
382 plugin->config.current_point = 0;
384 if(plugin->config.current_point == 2)
385 plugin->config.current_point = 3;
387 start_cursor_x = cursor_x;
388 start_cursor_y = cursor_y;
390 if(alt_down() || shift_down())
393 state = PerspectiveCanvas::DRAG_FULL;
395 state = PerspectiveCanvas::ZOOM;
397 // Get starting positions
398 start_x1 = plugin->config.x1;
399 start_y1 = plugin->config.y1;
400 start_x2 = plugin->config.x2;
401 start_y2 = plugin->config.y2;
402 start_x3 = plugin->config.x3;
403 start_y3 = plugin->config.y3;
404 start_x4 = plugin->config.x4;
405 start_y4 = plugin->config.y4;
409 state = PerspectiveCanvas::DRAG;
411 // Get starting positions
412 start_x1 = plugin->get_current_x();
413 start_y1 = plugin->get_current_y();
415 ((PerspectiveWindow*)plugin->thread->window)->update_coord();
416 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
423 int PerspectiveCanvas::button_release_event()
425 if(state != PerspectiveCanvas::NONE)
427 state = PerspectiveCanvas::NONE;
433 int PerspectiveCanvas::cursor_motion_event()
435 if(state != PerspectiveCanvas::NONE)
439 if(state == PerspectiveCanvas::DRAG)
441 plugin->set_current_x((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x1);
442 plugin->set_current_y((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y1);
445 if(state == PerspectiveCanvas::DRAG_FULL)
447 plugin->config.x1 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x1);
448 plugin->config.y1 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y1);
449 plugin->config.x2 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x2);
450 plugin->config.y2 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y2);
451 plugin->config.x3 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x3);
452 plugin->config.y3 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y3);
453 plugin->config.x4 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x4);
454 plugin->config.y4 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y4);
457 if(state == PerspectiveCanvas::ZOOM)
459 float center_x = (start_x1 +
463 float center_y = (start_y1 +
467 float zoom = (float)(get_cursor_y() - start_cursor_y + 640) / 640;
468 plugin->config.x1 = center_x + (start_x1 - center_x) * zoom;
469 plugin->config.y1 = center_y + (start_y1 - center_y) * zoom;
470 plugin->config.x2 = center_x + (start_x2 - center_x) * zoom;
471 plugin->config.y2 = center_y + (start_y2 - center_y) * zoom;
472 plugin->config.x3 = center_x + (start_x3 - center_x) * zoom;
473 plugin->config.y3 = center_y + (start_y3 - center_y) * zoom;
474 plugin->config.x4 = center_x + (start_x4 - center_x) * zoom;
475 plugin->config.y4 = center_y + (start_y4 - center_y) * zoom;
477 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
478 ((PerspectiveWindow*)plugin->thread->window)->update_coord();
479 plugin->send_configure_change();
491 PerspectiveCoord::PerspectiveCoord(PerspectiveWindow *gui,
492 PerspectiveMain *plugin,
497 : BC_TumbleTextBox(gui, value, (float)-100, (float)200, x, y, 100)
499 this->plugin = plugin;
503 int PerspectiveCoord::handle_event()
506 plugin->set_current_x(atof(get_text()));
508 plugin->set_current_y(atof(get_text()));
509 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
510 plugin->send_configure_change();
521 PerspectiveReset::PerspectiveReset(PerspectiveMain *plugin,
524 : BC_GenericButton(x, y, _("Reset"))
526 this->plugin = plugin;
528 int PerspectiveReset::handle_event()
530 plugin->config.x1 = 0;
531 plugin->config.y1 = 0;
532 plugin->config.x2 = 100;
533 plugin->config.y2 = 0;
534 plugin->config.x3 = 100;
535 plugin->config.y3 = 100;
536 plugin->config.x4 = 0;
537 plugin->config.y4 = 100;
538 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
539 ((PerspectiveWindow*)plugin->thread->window)->update_coord();
540 plugin->send_configure_change();
554 PerspectiveMode::PerspectiveMode(PerspectiveMain *plugin,
559 : BC_Radial(x, y, plugin->config.mode == value, text)
561 this->plugin = plugin;
564 int PerspectiveMode::handle_event()
566 plugin->config.mode = value;
567 ((PerspectiveWindow*)plugin->thread->window)->update_mode();
568 ((PerspectiveWindow*)plugin->thread->window)->update_canvas();
569 plugin->send_configure_change();
576 PerspectiveDirection::PerspectiveDirection(PerspectiveMain *plugin,
581 : BC_Radial(x, y, plugin->config.forward == value, text)
583 this->plugin = plugin;
586 int PerspectiveDirection::handle_event()
588 plugin->config.forward = value;
589 ((PerspectiveWindow*)plugin->thread->window)->update_mode();
590 plugin->send_configure_change();
605 PerspectiveMain::PerspectiveMain(PluginServer *server)
606 : PluginVClient(server)
613 PerspectiveMain::~PerspectiveMain()
616 if(engine) delete engine;
617 if(temp) delete temp;
620 const char* PerspectiveMain::plugin_title() { return _("Perspective"); }
621 int PerspectiveMain::is_realtime() { return 1; }
625 NEW_WINDOW_MACRO(PerspectiveMain, PerspectiveWindow)
627 LOAD_CONFIGURATION_MACRO(PerspectiveMain, PerspectiveConfig)
631 void PerspectiveMain::update_gui()
635 //printf("PerspectiveMain::update_gui 1\n");
636 thread->window->lock_window();
637 //printf("PerspectiveMain::update_gui 2\n");
638 load_configuration();
639 ((PerspectiveWindow*)thread->window)->update_coord();
640 ((PerspectiveWindow*)thread->window)->update_mode();
641 ((PerspectiveWindow*)thread->window)->update_canvas();
642 thread->window->unlock_window();
643 //printf("PerspectiveMain::update_gui 3\n");
651 void PerspectiveMain::save_data(KeyFrame *keyframe)
655 // cause data to be stored directly in text
656 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
657 output.tag.set_title("PERSPECTIVE");
659 output.tag.set_property("X1", config.x1);
660 output.tag.set_property("X2", config.x2);
661 output.tag.set_property("X3", config.x3);
662 output.tag.set_property("X4", config.x4);
663 output.tag.set_property("Y1", config.y1);
664 output.tag.set_property("Y2", config.y2);
665 output.tag.set_property("Y3", config.y3);
666 output.tag.set_property("Y4", config.y4);
668 output.tag.set_property("MODE", config.mode);
669 output.tag.set_property("FORWARD", config.forward);
670 output.tag.set_property("WINDOW_W", config.window_w);
671 output.tag.set_property("WINDOW_H", config.window_h);
673 output.tag.set_title("/PERSPECTIVE");
675 output.append_newline();
676 output.terminate_string();
679 void PerspectiveMain::read_data(KeyFrame *keyframe)
683 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
689 result = input.read_tag();
693 if(input.tag.title_is("PERSPECTIVE"))
695 config.x1 = input.tag.get_property("X1", config.x1);
696 config.x2 = input.tag.get_property("X2", config.x2);
697 config.x3 = input.tag.get_property("X3", config.x3);
698 config.x4 = input.tag.get_property("X4", config.x4);
699 config.y1 = input.tag.get_property("Y1", config.y1);
700 config.y2 = input.tag.get_property("Y2", config.y2);
701 config.y3 = input.tag.get_property("Y3", config.y3);
702 config.y4 = input.tag.get_property("Y4", config.y4);
704 config.mode = input.tag.get_property("MODE", config.mode);
705 config.forward = input.tag.get_property("FORWARD", config.forward);
706 config.window_w = input.tag.get_property("WINDOW_W", config.window_w);
707 config.window_h = input.tag.get_property("WINDOW_H", config.window_h);
713 float PerspectiveMain::get_current_x()
715 switch(config.current_point)
733 float PerspectiveMain::get_current_y()
735 switch(config.current_point)
753 void PerspectiveMain::set_current_x(float value)
755 switch(config.current_point)
772 void PerspectiveMain::set_current_y(float value)
774 switch(config.current_point)
793 int PerspectiveMain::process_buffer(VFrame *frame,
794 int64_t start_position,
797 /*int need_reconfigure =*/ load_configuration();
800 if( EQUIV(config.x1, 0) && EQUIV(config.y1, 0) &&
801 EQUIV(config.x2, 100) && EQUIV(config.y2, 0) &&
802 EQUIV(config.x3, 100) && EQUIV(config.y3, 100) &&
803 EQUIV(config.x4, 0) && EQUIV(config.y4, 100))
813 // Opengl does some funny business with stretching.
814 int use_opengl = get_use_opengl() &&
815 (config.mode == AffineEngine::PERSPECTIVE ||
816 config.mode == AffineEngine::SHEER);
823 if(!engine) engine = new AffineEngine(get_project_smp() + 1,
824 get_project_smp() + 1);
832 this->output = frame;
834 int w = frame->get_w();
835 int h = frame->get_h();
836 int color_model = frame->get_color_model();
839 config.mode == AffineEngine::STRETCH &&
840 (temp->get_w() != w * AFFINE_OVERSAMPLE ||
841 temp->get_h() != h * AFFINE_OVERSAMPLE))
848 (config.mode == AffineEngine::PERSPECTIVE ||
849 config.mode == AffineEngine::SHEER) &&
850 (temp->get_w() != w ||
857 if(config.mode == AffineEngine::STRETCH)
863 w * AFFINE_OVERSAMPLE,
864 h * AFFINE_OVERSAMPLE,
871 if(config.mode == AffineEngine::PERSPECTIVE ||
872 config.mode == AffineEngine::SHEER)
874 if(frame->get_rows()[0] == frame->get_rows()[0])
885 temp->copy_from(input);
888 output->clear_frame();
892 engine->process(output,
911 if(config.mode == AffineEngine::STRETCH)
913 #define RESAMPLE(type, components, chroma_offset) \
915 for(int i = 0; i < h; i++) \
917 type *out_row = (type*)output->get_rows()[i]; \
918 type *in_row1 = (type*)temp->get_rows()[i * AFFINE_OVERSAMPLE]; \
919 type *in_row2 = (type*)temp->get_rows()[i * AFFINE_OVERSAMPLE + 1]; \
920 for(int j = 0; j < w; j++) \
922 out_row[0] = (in_row1[0] + \
923 in_row1[components] + \
925 in_row2[components]) / \
926 AFFINE_OVERSAMPLE / \
928 out_row[1] = ((in_row1[1] + \
929 in_row1[components + 1] + \
931 in_row2[components + 1]) - \
933 AFFINE_OVERSAMPLE * \
934 AFFINE_OVERSAMPLE) / \
935 AFFINE_OVERSAMPLE / \
936 AFFINE_OVERSAMPLE + \
938 out_row[2] = ((in_row1[2] + \
939 in_row1[components + 2] + \
941 in_row2[components + 2]) - \
943 AFFINE_OVERSAMPLE * \
944 AFFINE_OVERSAMPLE) / \
945 AFFINE_OVERSAMPLE / \
946 AFFINE_OVERSAMPLE + \
948 if(components == 4) \
950 out_row[3] = (in_row1[3] + \
951 in_row1[components + 3] + \
953 in_row2[components + 3]) / \
954 AFFINE_OVERSAMPLE / \
957 out_row += components; \
958 in_row1 += components * AFFINE_OVERSAMPLE; \
959 in_row2 += components * AFFINE_OVERSAMPLE; \
964 switch(frame->get_color_model())
967 RESAMPLE(float, 3, 0)
970 RESAMPLE(unsigned char, 3, 0)
973 RESAMPLE(float, 4, 0)
976 RESAMPLE(unsigned char, 4, 0)
979 RESAMPLE(unsigned char, 3, 0x80)
982 RESAMPLE(unsigned char, 4, 0x80)
985 RESAMPLE(uint16_t, 3, 0)
987 case BC_RGBA16161616:
988 RESAMPLE(uint16_t, 4, 0)
991 RESAMPLE(uint16_t, 3, 0x8000)
993 case BC_YUVA16161616:
994 RESAMPLE(uint16_t, 4, 0x8000)
1003 int PerspectiveMain::handle_opengl()
1006 engine->set_opengl(1);
1007 engine->process(get_output(),
1020 engine->set_opengl(0);