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
22
#include "../motion/affine.h"
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;
114
PLUGIN_THREAD_OBJECT(PerspectiveMain, PerspectiveThread, PerspectiveWindow)
118
PerspectiveWindow::PerspectiveWindow(PerspectiveMain *plugin, int x, int y)
119
: BC_Window(plugin->gui_string,
122
plugin->config.window_w,
123
plugin->config.window_h,
124
plugin->config.window_w,
125
plugin->config.window_h,
129
//printf("PerspectiveWindow::PerspectiveWindow 1 %d %d\n", plugin->config.window_w, plugin->config.window_h);
130
this->plugin = plugin;
133
PerspectiveWindow::~PerspectiveWindow()
137
int PerspectiveWindow::create_objects()
141
add_subwindow(canvas = new PerspectiveCanvas(plugin,
146
canvas->set_cursor(CROSS_CURSOR);
147
y += canvas->get_h() + 10;
148
add_subwindow(new BC_Title(x, y, _("Current X:")));
150
this->x = new PerspectiveCoord(this,
154
plugin->get_current_x(),
156
this->x->create_objects();
158
add_subwindow(new BC_Title(x, y, _("Y:")));
160
this->y = new PerspectiveCoord(this,
164
plugin->get_current_y(),
166
this->y->create_objects();
169
add_subwindow(new PerspectiveReset(plugin, x, y));
171
add_subwindow(mode_perspective = new PerspectiveMode(plugin,
174
AffineEngine::PERSPECTIVE,
177
add_subwindow(mode_sheer = new PerspectiveMode(plugin,
184
add_subwindow(mode_stretch = new PerspectiveMode(plugin,
187
AffineEngine::STRETCH,
192
add_subwindow(new BC_Title(x, y, _("Perspective direction:")));
194
add_subwindow(forward = new PerspectiveDirection(plugin,
200
add_subwindow(reverse = new PerspectiveDirection(plugin,
211
WINDOW_CLOSE_EVENT(PerspectiveWindow)
213
int PerspectiveWindow::resize_event(int w, int h)
218
void PerspectiveWindow::update_canvas()
220
canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
221
int x1, y1, x2, y2, x3, y3, x4, y4;
222
calculate_canvas_coords(x1, y1, x2, y2, x3, y3, x4, y4);
224
// printf("PerspectiveWindow::update_canvas %d,%d %d,%d %d,%d %d,%d\n",
233
canvas->set_color(BLACK);
236
for(int i = 0; i <= DIVISIONS; i++)
240
x1 + (x4 - x1) * i / DIVISIONS,
241
y1 + (y4 - y1) * i / DIVISIONS,
242
x2 + (x3 - x2) * i / DIVISIONS,
243
y2 + (y3 - y2) * i / DIVISIONS);
246
x1 + (x2 - x1) * i / DIVISIONS,
247
y1 + (y2 - y1) * i / DIVISIONS,
248
x4 + (x3 - x4) * i / DIVISIONS,
249
y4 + (y3 - y4) * i / DIVISIONS);
254
if(plugin->config.current_point == 0)
255
canvas->draw_disc(x1 - RADIUS, y1 - RADIUS, RADIUS * 2, RADIUS * 2);
257
canvas->draw_circle(x1 - RADIUS, y1 - RADIUS, RADIUS * 2, RADIUS * 2);
259
if(plugin->config.current_point == 1)
260
canvas->draw_disc(x2 - RADIUS, y2 - RADIUS, RADIUS * 2, RADIUS * 2);
262
canvas->draw_circle(x2 - RADIUS, y2 - RADIUS, RADIUS * 2, RADIUS * 2);
264
if(plugin->config.current_point == 2)
265
canvas->draw_disc(x3 - RADIUS, y3 - RADIUS, RADIUS * 2, RADIUS * 2);
267
canvas->draw_circle(x3 - RADIUS, y3 - RADIUS, RADIUS * 2, RADIUS * 2);
269
if(plugin->config.current_point == 3)
270
canvas->draw_disc(x4 - RADIUS, y4 - RADIUS, RADIUS * 2, RADIUS * 2);
272
canvas->draw_circle(x4 - RADIUS, y4 - RADIUS, RADIUS * 2, RADIUS * 2);
278
void PerspectiveWindow::update_mode()
280
mode_perspective->update(plugin->config.mode == AffineEngine::PERSPECTIVE);
281
mode_sheer->update(plugin->config.mode == AffineEngine::SHEER);
282
mode_stretch->update(plugin->config.mode == AffineEngine::STRETCH);
283
forward->update(plugin->config.forward);
284
reverse->update(!plugin->config.forward);
287
void PerspectiveWindow::update_coord()
289
x->update(plugin->get_current_x());
290
y->update(plugin->get_current_y());
293
void PerspectiveWindow::calculate_canvas_coords(int &x1,
302
int w = canvas->get_w() - 1;
303
int h = canvas->get_h() - 1;
304
if(plugin->config.mode == AffineEngine::PERSPECTIVE ||
305
plugin->config.mode == AffineEngine::STRETCH)
307
x1 = (int)(plugin->config.x1 * w / 100);
308
y1 = (int)(plugin->config.y1 * h / 100);
309
x2 = (int)(plugin->config.x2 * w / 100);
310
y2 = (int)(plugin->config.y2 * h / 100);
311
x3 = (int)(plugin->config.x3 * w / 100);
312
y3 = (int)(plugin->config.y3 * h / 100);
313
x4 = (int)(plugin->config.x4 * w / 100);
314
y4 = (int)(plugin->config.y4 * h / 100);
318
x1 = (int)(plugin->config.x1 * w) / 100;
322
x4 = (int)(plugin->config.x4 * w) / 100;
332
PerspectiveCanvas::PerspectiveCanvas(PerspectiveMain *plugin,
337
: BC_SubWindow(x, y, w, h, 0xffffff)
339
this->plugin = plugin;
340
state = PerspectiveCanvas::NONE;
346
int PerspectiveCanvas::button_press_event()
348
if(is_event_win() && cursor_inside())
351
int x1, y1, x2, y2, x3, y3, x4, y4;
352
int cursor_x = get_cursor_x();
353
int cursor_y = get_cursor_y();
354
plugin->thread->window->calculate_canvas_coords(x1, y1, x2, y2, x3, y3, x4, y4);
356
float distance1 = DISTANCE(cursor_x, cursor_y, x1, y1);
357
float distance2 = DISTANCE(cursor_x, cursor_y, x2, y2);
358
float distance3 = DISTANCE(cursor_x, cursor_y, x3, y3);
359
float distance4 = DISTANCE(cursor_x, cursor_y, x4, y4);
360
// printf("PerspectiveCanvas::button_press_event %f %d %d %d %d\n",
366
float min = distance1;
367
plugin->config.current_point = 0;
371
plugin->config.current_point = 1;
376
plugin->config.current_point = 2;
381
plugin->config.current_point = 3;
384
if(plugin->config.mode == AffineEngine::SHEER)
386
if(plugin->config.current_point == 1)
387
plugin->config.current_point = 0;
389
if(plugin->config.current_point == 2)
390
plugin->config.current_point = 3;
392
start_cursor_x = cursor_x;
393
start_cursor_y = cursor_y;
395
if(alt_down() || shift_down())
398
state = PerspectiveCanvas::DRAG_FULL;
400
state = PerspectiveCanvas::ZOOM;
402
// Get starting positions
403
start_x1 = plugin->config.x1;
404
start_y1 = plugin->config.y1;
405
start_x2 = plugin->config.x2;
406
start_y2 = plugin->config.y2;
407
start_x3 = plugin->config.x3;
408
start_y3 = plugin->config.y3;
409
start_x4 = plugin->config.x4;
410
start_y4 = plugin->config.y4;
414
state = PerspectiveCanvas::DRAG;
416
// Get starting positions
417
start_x1 = plugin->get_current_x();
418
start_y1 = plugin->get_current_y();
420
plugin->thread->window->update_coord();
421
plugin->thread->window->update_canvas();
428
int PerspectiveCanvas::button_release_event()
430
if(state != PerspectiveCanvas::NONE)
432
state = PerspectiveCanvas::NONE;
438
int PerspectiveCanvas::cursor_motion_event()
440
if(state != PerspectiveCanvas::NONE)
444
if(state == PerspectiveCanvas::DRAG)
446
plugin->set_current_x((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x1);
447
plugin->set_current_y((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y1);
450
if(state == PerspectiveCanvas::DRAG_FULL)
452
plugin->config.x1 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x1);
453
plugin->config.y1 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y1);
454
plugin->config.x2 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x2);
455
plugin->config.y2 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y2);
456
plugin->config.x3 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x3);
457
plugin->config.y3 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y3);
458
plugin->config.x4 = ((float)(get_cursor_x() - start_cursor_x) / w * 100 + start_x4);
459
plugin->config.y4 = ((float)(get_cursor_y() - start_cursor_y) / h * 100 + start_y4);
462
if(state == PerspectiveCanvas::ZOOM)
464
float center_x = (start_x1 +
468
float center_y = (start_y1 +
472
float zoom = (float)(get_cursor_y() - start_cursor_y + 640) / 640;
473
plugin->config.x1 = center_x + (start_x1 - center_x) * zoom;
474
plugin->config.y1 = center_y + (start_y1 - center_y) * zoom;
475
plugin->config.x2 = center_x + (start_x2 - center_x) * zoom;
476
plugin->config.y2 = center_y + (start_y2 - center_y) * zoom;
477
plugin->config.x3 = center_x + (start_x3 - center_x) * zoom;
478
plugin->config.y3 = center_y + (start_y3 - center_y) * zoom;
479
plugin->config.x4 = center_x + (start_x4 - center_x) * zoom;
480
plugin->config.y4 = center_y + (start_y4 - center_y) * zoom;
482
plugin->thread->window->update_canvas();
483
plugin->thread->window->update_coord();
484
plugin->send_configure_change();
496
PerspectiveCoord::PerspectiveCoord(PerspectiveWindow *gui,
497
PerspectiveMain *plugin,
502
: BC_TumbleTextBox(gui, value, (float)0, (float)100, x, y, 100)
504
this->plugin = plugin;
508
int PerspectiveCoord::handle_event()
511
plugin->set_current_x(atof(get_text()));
513
plugin->set_current_y(atof(get_text()));
514
plugin->thread->window->update_canvas();
515
plugin->send_configure_change();
526
PerspectiveReset::PerspectiveReset(PerspectiveMain *plugin,
529
: BC_GenericButton(x, y, _("Reset"))
531
this->plugin = plugin;
533
int PerspectiveReset::handle_event()
535
plugin->config.x1 = 0;
536
plugin->config.y1 = 0;
537
plugin->config.x2 = 100;
538
plugin->config.y2 = 0;
539
plugin->config.x3 = 100;
540
plugin->config.y3 = 100;
541
plugin->config.x4 = 0;
542
plugin->config.y4 = 100;
543
plugin->thread->window->update_canvas();
544
plugin->thread->window->update_coord();
545
plugin->send_configure_change();
559
PerspectiveMode::PerspectiveMode(PerspectiveMain *plugin,
564
: BC_Radial(x, y, plugin->config.mode == value, text)
566
this->plugin = plugin;
569
int PerspectiveMode::handle_event()
571
plugin->config.mode = value;
572
plugin->thread->window->update_mode();
573
plugin->thread->window->update_canvas();
574
plugin->send_configure_change();
581
PerspectiveDirection::PerspectiveDirection(PerspectiveMain *plugin,
586
: BC_Radial(x, y, plugin->config.forward == value, text)
588
this->plugin = plugin;
591
int PerspectiveDirection::handle_event()
593
plugin->config.forward = value;
594
plugin->thread->window->update_mode();
595
plugin->send_configure_change();
610
PerspectiveMain::PerspectiveMain(PluginServer *server)
611
: PluginVClient(server)
613
PLUGIN_CONSTRUCTOR_MACRO
618
PerspectiveMain::~PerspectiveMain()
620
PLUGIN_DESTRUCTOR_MACRO
621
if(engine) delete engine;
622
if(temp) delete temp;
625
char* PerspectiveMain::plugin_title() { return N_("Perspective"); }
626
int PerspectiveMain::is_realtime() { return 1; }
629
NEW_PICON_MACRO(PerspectiveMain)
631
SHOW_GUI_MACRO(PerspectiveMain, PerspectiveThread)
633
SET_STRING_MACRO(PerspectiveMain)
635
RAISE_WINDOW_MACRO(PerspectiveMain)
637
LOAD_CONFIGURATION_MACRO(PerspectiveMain, PerspectiveConfig)
641
void PerspectiveMain::update_gui()
645
//printf("PerspectiveMain::update_gui 1\n");
646
thread->window->lock_window();
647
//printf("PerspectiveMain::update_gui 2\n");
648
load_configuration();
649
thread->window->update_coord();
650
thread->window->update_mode();
651
thread->window->update_canvas();
652
thread->window->unlock_window();
653
//printf("PerspectiveMain::update_gui 3\n");
658
int PerspectiveMain::load_defaults()
660
char directory[1024], string[1024];
661
// set the default directory
662
sprintf(directory, "%sperspective.rc", BCASTDIR);
665
defaults = new BC_Hash(directory);
668
config.x1 = defaults->get("X1", config.x1);
669
config.x2 = defaults->get("X2", config.x2);
670
config.x3 = defaults->get("X3", config.x3);
671
config.x4 = defaults->get("X4", config.x4);
672
config.y1 = defaults->get("Y1", config.y1);
673
config.y2 = defaults->get("Y2", config.y2);
674
config.y3 = defaults->get("Y3", config.y3);
675
config.y4 = defaults->get("Y4", config.y4);
677
config.mode = defaults->get("MODE", config.mode);
678
config.forward = defaults->get("FORWARD", config.forward);
679
config.window_w = defaults->get("WINDOW_W", config.window_w);
680
config.window_h = defaults->get("WINDOW_H", config.window_h);
685
int PerspectiveMain::save_defaults()
687
defaults->update("X1", config.x1);
688
defaults->update("X2", config.x2);
689
defaults->update("X3", config.x3);
690
defaults->update("X4", config.x4);
691
defaults->update("Y1", config.y1);
692
defaults->update("Y2", config.y2);
693
defaults->update("Y3", config.y3);
694
defaults->update("Y4", config.y4);
696
defaults->update("MODE", config.mode);
697
defaults->update("FORWARD", config.forward);
698
defaults->update("WINDOW_W", config.window_w);
699
defaults->update("WINDOW_H", config.window_h);
706
void PerspectiveMain::save_data(KeyFrame *keyframe)
710
// cause data to be stored directly in text
711
output.set_shared_string(keyframe->data, MESSAGESIZE);
712
output.tag.set_title("PERSPECTIVE");
714
output.tag.set_property("X1", config.x1);
715
output.tag.set_property("X2", config.x2);
716
output.tag.set_property("X3", config.x3);
717
output.tag.set_property("X4", config.x4);
718
output.tag.set_property("Y1", config.y1);
719
output.tag.set_property("Y2", config.y2);
720
output.tag.set_property("Y3", config.y3);
721
output.tag.set_property("Y4", config.y4);
723
output.tag.set_property("MODE", config.mode);
724
output.tag.set_property("FORWARD", config.forward);
725
output.tag.set_property("WINDOW_W", config.window_w);
726
output.tag.set_property("WINDOW_H", config.window_h);
728
output.tag.set_title("/PERSPECTIVE");
730
output.terminate_string();
733
void PerspectiveMain::read_data(KeyFrame *keyframe)
737
input.set_shared_string(keyframe->data, strlen(keyframe->data));
743
result = input.read_tag();
747
if(input.tag.title_is("PERSPECTIVE"))
749
config.x1 = input.tag.get_property("X1", config.x1);
750
config.x2 = input.tag.get_property("X2", config.x2);
751
config.x3 = input.tag.get_property("X3", config.x3);
752
config.x4 = input.tag.get_property("X4", config.x4);
753
config.y1 = input.tag.get_property("Y1", config.y1);
754
config.y2 = input.tag.get_property("Y2", config.y2);
755
config.y3 = input.tag.get_property("Y3", config.y3);
756
config.y4 = input.tag.get_property("Y4", config.y4);
758
config.mode = input.tag.get_property("MODE", config.mode);
759
config.forward = input.tag.get_property("FORWARD", config.forward);
760
config.window_w = input.tag.get_property("WINDOW_W", config.window_w);
761
config.window_h = input.tag.get_property("WINDOW_H", config.window_h);
767
float PerspectiveMain::get_current_x()
769
switch(config.current_point)
786
float PerspectiveMain::get_current_y()
788
switch(config.current_point)
805
void PerspectiveMain::set_current_x(float value)
807
switch(config.current_point)
824
void PerspectiveMain::set_current_y(float value)
826
switch(config.current_point)
845
int PerspectiveMain::process_buffer(VFrame *frame,
846
int64_t start_position,
849
int need_reconfigure = load_configuration();
853
if( EQUIV(config.x1, 0) && EQUIV(config.y1, 0) &&
854
EQUIV(config.x2, 100) && EQUIV(config.y2, 0) &&
855
EQUIV(config.x3, 100) && EQUIV(config.y3, 100) &&
856
EQUIV(config.x4, 0) && EQUIV(config.y4, 100))
866
// Opengl does some funny business with stretching.
867
int use_opengl = get_use_opengl() &&
868
(config.mode == AffineEngine::PERSPECTIVE ||
869
config.mode == AffineEngine::SHEER);
876
if(!engine) engine = new AffineEngine(get_project_smp() + 1,
877
get_project_smp() + 1);
885
this->output = frame;
887
int w = frame->get_w();
888
int h = frame->get_h();
889
int color_model = frame->get_color_model();
892
config.mode == AffineEngine::STRETCH &&
893
(temp->get_w() != w * AFFINE_OVERSAMPLE ||
894
temp->get_h() != h * AFFINE_OVERSAMPLE))
901
(config.mode == AffineEngine::PERSPECTIVE ||
902
config.mode == AffineEngine::SHEER) &&
903
(temp->get_w() != w ||
910
if(config.mode == AffineEngine::STRETCH)
915
w * AFFINE_OVERSAMPLE,
916
h * AFFINE_OVERSAMPLE,
922
if(config.mode == AffineEngine::PERSPECTIVE ||
923
config.mode == AffineEngine::SHEER)
925
if(frame->get_rows()[0] == frame->get_rows()[0])
934
temp->copy_from(input);
937
output->clear_frame();
941
engine->process(output,
960
if(config.mode == AffineEngine::STRETCH)
962
#define RESAMPLE(type, components, chroma_offset) \
964
for(int i = 0; i < h; i++) \
966
type *out_row = (type*)output->get_rows()[i]; \
967
type *in_row1 = (type*)temp->get_rows()[i * AFFINE_OVERSAMPLE]; \
968
type *in_row2 = (type*)temp->get_rows()[i * AFFINE_OVERSAMPLE + 1]; \
969
for(int j = 0; j < w; j++) \
971
out_row[0] = (in_row1[0] + \
972
in_row1[components] + \
974
in_row2[components]) / \
975
AFFINE_OVERSAMPLE / \
977
out_row[1] = ((in_row1[1] + \
978
in_row1[components + 1] + \
980
in_row2[components + 1]) - \
982
AFFINE_OVERSAMPLE * \
983
AFFINE_OVERSAMPLE) / \
984
AFFINE_OVERSAMPLE / \
985
AFFINE_OVERSAMPLE + \
987
out_row[2] = ((in_row1[2] + \
988
in_row1[components + 2] + \
990
in_row2[components + 2]) - \
992
AFFINE_OVERSAMPLE * \
993
AFFINE_OVERSAMPLE) / \
994
AFFINE_OVERSAMPLE / \
995
AFFINE_OVERSAMPLE + \
997
if(components == 4) \
999
out_row[3] = (in_row1[3] + \
1000
in_row1[components + 3] + \
1002
in_row2[components + 3]) / \
1003
AFFINE_OVERSAMPLE / \
1004
AFFINE_OVERSAMPLE; \
1006
out_row += components; \
1007
in_row1 += components * AFFINE_OVERSAMPLE; \
1008
in_row2 += components * AFFINE_OVERSAMPLE; \
1013
switch(frame->get_color_model())
1016
RESAMPLE(float, 3, 0)
1019
RESAMPLE(unsigned char, 3, 0)
1022
RESAMPLE(float, 4, 0)
1025
RESAMPLE(unsigned char, 4, 0)
1028
RESAMPLE(unsigned char, 3, 0x80)
1031
RESAMPLE(unsigned char, 4, 0x80)
1034
RESAMPLE(uint16_t, 3, 0)
1036
case BC_RGBA16161616:
1037
RESAMPLE(uint16_t, 4, 0)
1040
RESAMPLE(uint16_t, 3, 0x8000)
1042
case BC_YUVA16161616:
1043
RESAMPLE(uint16_t, 4, 0x8000)
1052
int PerspectiveMain::handle_opengl()
1055
engine->set_opengl(1);
1056
engine->process(get_output(),
1069
engine->set_opengl(0);