version update
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / rotate / rotate.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
23 #include "rotate.h"
24
25 #define MAXANGLE 360
26
27 REGISTER_PLUGIN(RotateEffect)
28
29
30
31
32 RotateConfig::RotateConfig()
33 {
34         reset();
35 }
36
37 void RotateConfig::reset()
38 {
39         angle = 0.0;
40         pivot_x = 50.0;
41         pivot_y = 50.0;
42         draw_pivot = 0;
43 }
44
45 int RotateConfig::equivalent(RotateConfig &that)
46 {
47         return EQUIV(angle, that.angle) &&
48                 EQUIV(pivot_x, that.pivot_y) &&
49                 EQUIV(pivot_y, that.pivot_y) &&
50                 draw_pivot == that.draw_pivot;
51 }
52
53 void RotateConfig::copy_from(RotateConfig &that)
54 {
55         angle = that.angle;
56         pivot_x = that.pivot_x;
57         pivot_y = that.pivot_y;
58         draw_pivot = that.draw_pivot;
59 //      bilinear = that.bilinear;
60 }
61
62 void RotateConfig::interpolate(RotateConfig &prev,
63                 RotateConfig &next,
64                 long prev_frame,
65                 long next_frame,
66                 long current_frame)
67 {
68         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
69         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
70
71         this->angle = prev.angle * prev_scale + next.angle * next_scale;
72         this->pivot_x = prev.pivot_x * prev_scale + next.pivot_x * next_scale;
73         this->pivot_y = prev.pivot_y * prev_scale + next.pivot_y * next_scale;
74         draw_pivot = prev.draw_pivot;
75 //      bilinear = prev.bilinear;
76 }
77
78
79
80
81
82
83
84
85
86
87
88 RotateToggle::RotateToggle(RotateWindow *window,
89         RotateEffect *plugin,
90         int init_value,
91         int x,
92         int y,
93         int value,
94         const char *string)
95  : BC_Radial(x, y, init_value, string)
96 {
97         this->value = value;
98         this->plugin = plugin;
99         this->window = window;
100 }
101
102 int RotateToggle::handle_event()
103 {
104         plugin->config.angle = (float)value;
105         window->update();
106         plugin->send_configure_change();
107         return 1;
108 }
109
110
111
112
113
114
115
116 RotateDrawPivot::RotateDrawPivot(RotateWindow *window,
117         RotateEffect *plugin,
118         int x,
119         int y)
120  : BC_CheckBox(x, y, plugin->config.draw_pivot, _("Draw pivot"))
121 {
122         this->plugin = plugin;
123         this->window = window;
124 }
125
126 int RotateDrawPivot::handle_event()
127 {
128         plugin->config.draw_pivot = get_value();
129         plugin->send_configure_change();
130         return 1;
131 }
132
133
134
135
136
137 // RotateInterpolate::RotateInterpolate(RotateEffect *plugin, int x, int y)
138 //  : BC_CheckBox(x, y, plugin->config.bilinear, _("Interpolate"))
139 // {
140 //      this->plugin = plugin;
141 // }
142 // int RotateInterpolate::handle_event()
143 // {
144 //      plugin->config.bilinear = get_value();
145 //      plugin->send_configure_change();
146 //      return 1;
147 // }
148 //
149
150
151
152 RotateFine::RotateFine(RotateWindow *window, RotateEffect *plugin, int x, int y)
153  : BC_FPot(x,
154         y,
155         (float)plugin->config.angle,
156         (float)-360,
157         (float)360)
158 {
159         this->window = window;
160         this->plugin = plugin;
161         set_precision(0.01);
162         set_use_caption(0);
163 }
164
165 int RotateFine::handle_event()
166 {
167         plugin->config.angle = get_value();
168         window->update_toggles();
169         window->update_text();
170         plugin->send_configure_change();
171         return 1;
172 }
173
174
175
176 RotateText::RotateText(RotateWindow *window,
177         RotateEffect *plugin,
178         int x,
179         int y)
180  : BC_TextBox(x,
181         y,
182         xS(90),
183         1,
184         (float)plugin->config.angle)
185 {
186         this->window = window;
187         this->plugin = plugin;
188         set_precision(4);
189 }
190
191 int RotateText::handle_event()
192 {
193         plugin->config.angle = atof(get_text());
194         window->update_toggles();
195         window->update_fine();
196         plugin->send_configure_change();
197         return 1;
198 }
199
200
201
202 RotateX::RotateX(RotateWindow *window, RotateEffect *plugin, int x, int y)
203  : BC_FPot(x,
204         y,
205         (float)plugin->config.pivot_x,
206         (float)0,
207         (float)100)
208 {
209         this->window = window;
210         this->plugin = plugin;
211         set_precision(0.01);
212         set_use_caption(1);
213 }
214
215 int RotateX::handle_event()
216 {
217         plugin->config.pivot_x = get_value();
218         plugin->send_configure_change();
219         return 1;
220 }
221
222 RotateY::RotateY(RotateWindow *window, RotateEffect *plugin, int x, int y)
223  : BC_FPot(x,
224         y,
225         (float)plugin->config.pivot_y,
226         (float)0,
227         (float)100)
228 {
229         this->window = window;
230         this->plugin = plugin;
231         set_precision(0.01);
232         set_use_caption(1);
233 }
234
235 int RotateY::handle_event()
236 {
237         plugin->config.pivot_y = get_value();
238         plugin->send_configure_change();
239         return 1;
240 }
241
242
243 RotateReset::RotateReset(RotateEffect *plugin, RotateWindow *window, int x, int y)
244  : BC_GenericButton(x, y, _("Reset"))
245 {
246         this->plugin = plugin;
247         this->window = window;
248 }
249 RotateReset::~RotateReset()
250 {
251 }
252 int RotateReset::handle_event()
253 {
254         plugin->config.reset();
255         window->update();
256         plugin->send_configure_change();
257         return 1;
258 }
259
260
261
262
263
264
265 RotateWindow::RotateWindow(RotateEffect *plugin)
266  : PluginClientWindow(plugin, xS(300), yS(230), xS(300), yS(230), 0)
267 {
268         this->plugin = plugin;
269 }
270
271 #define RADIUS xS(30)
272
273 void RotateWindow::create_objects()
274 {
275         int xs10 = xS(10), xs50 = xS(50), xs150 = xS(150);
276         int ys10 = yS(10), ys20 = yS(20), ys25 = yS(25), ys50 = yS(50), ys60 = yS(60);
277         int x = xs10, y = ys10;
278         BC_Title *title;
279         add_tool(new BC_Title(x, y, _("Rotate")));
280         x += xs50;  y += ys20;
281         add_tool(toggle0 = new RotateToggle(this, plugin,
282                 plugin->config.angle == 0, x, y, 0, "0"));
283     x += RADIUS;  y += RADIUS;
284         add_tool(toggle90 = new RotateToggle(this, plugin,
285                 plugin->config.angle == 90, x, y, 90, "90"));
286     x -= RADIUS;  y += RADIUS;
287         add_tool(toggle180 = new RotateToggle(this, plugin,
288                 plugin->config.angle == 180, x, y, 180, "180"));
289     x -= RADIUS;  y -= RADIUS;
290         add_tool(toggle270 = new RotateToggle(this, plugin,
291                 plugin->config.angle == 270, x, y, 270, "270"));
292 //      add_subwindow(bilinear = new RotateInterpolate(plugin, xs10, y + ys60));
293         x += xs150;  y -= ys50;
294         add_tool(fine = new RotateFine(this, plugin, x, y));
295         y += fine->get_h() + ys10;
296         add_tool(text = new RotateText(this, plugin, x, y));
297         y += ys25;
298         add_tool(new BC_Title(x, y, _("Degrees")));
299
300         y += text->get_h() + ys10;
301         add_subwindow(title = new BC_Title(x, y, _("Pivot (x,y):")));
302         y += title->get_h() + ys10;
303         add_subwindow(this->x = new RotateX(this, plugin, x, y));
304         x += this->x->get_w() + xs10;
305         add_subwindow(this->y = new RotateY(this, plugin, x, y));
306
307 //      y += this->y->get_h() + ys10;
308         x = xs10;
309         add_subwindow(draw_pivot = new RotateDrawPivot(this, plugin, x, y));
310         y += ys60;
311         add_subwindow(reset = new RotateReset(plugin, this, x, y));
312
313         show_window();
314 }
315
316
317
318 int RotateWindow::update()
319 {
320         update_fine();
321         update_toggles();
322         update_text();
323 //      bilinear->update(plugin->config.bilinear);
324         return 0;
325 }
326
327 int RotateWindow::update_fine()
328 {
329         fine->update(plugin->config.angle);
330         x->update(plugin->config.pivot_x);
331         y->update(plugin->config.pivot_y);
332         return 0;
333 }
334
335 int RotateWindow::update_text()
336 {
337         text->update(plugin->config.angle);
338         return 0;
339 }
340
341 int RotateWindow::update_toggles()
342 {
343         toggle0->update(EQUIV(plugin->config.angle, 0));
344         toggle90->update(EQUIV(plugin->config.angle, 90));
345         toggle180->update(EQUIV(plugin->config.angle, 180));
346         toggle270->update(EQUIV(plugin->config.angle, 270));
347         draw_pivot->update(plugin->config.draw_pivot);
348         return 0;
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384 RotateEffect::RotateEffect(PluginServer *server)
385  : PluginVClient(server)
386 {
387         engine = 0;
388         need_reconfigure = 1;
389
390 }
391
392 RotateEffect::~RotateEffect()
393 {
394
395         if(engine) delete engine;
396 }
397
398
399
400 const char* RotateEffect::plugin_title() { return N_("Rotate"); }
401 int RotateEffect::is_realtime() { return 1; }
402
403
404 NEW_WINDOW_MACRO(RotateEffect, RotateWindow)
405
406
407 void RotateEffect::update_gui()
408 {
409         if(thread)
410         {
411                 load_configuration();
412                 thread->window->lock_window();
413                 ((RotateWindow*)thread->window)->update();
414                 thread->window->unlock_window();
415         }
416 }
417
418 LOAD_CONFIGURATION_MACRO(RotateEffect, RotateConfig)
419
420
421
422
423 void RotateEffect::save_data(KeyFrame *keyframe)
424 {
425         FileXML output;
426
427 // cause data to be stored directly in text
428         output.set_shared_output(keyframe->xbuf);
429         output.tag.set_title("ROTATE");
430         output.tag.set_property("ANGLE", (float)config.angle);
431         output.tag.set_property("PIVOT_X", (float)config.pivot_x);
432         output.tag.set_property("PIVOT_Y", (float)config.pivot_y);
433         output.tag.set_property("DRAW_PIVOT", (int)config.draw_pivot);
434 //      output.tag.set_property("INTERPOLATE", (int)config.bilinear);
435         output.append_tag();
436         output.tag.set_title("/ROTATE");
437         output.append_tag();
438         output.append_newline();
439         output.terminate_string();
440 // data is now in *text
441 }
442
443 void RotateEffect::read_data(KeyFrame *keyframe)
444 {
445         FileXML input;
446
447         input.set_shared_input(keyframe->xbuf);
448
449         int result = 0;
450
451         while(!result)
452         {
453                 result = input.read_tag();
454
455                 if(!result)
456                 {
457                         if(input.tag.title_is("ROTATE"))
458                         {
459                                 config.angle = input.tag.get_property("ANGLE", (float)config.angle);
460                                 config.pivot_x = input.tag.get_property("PIVOT_X", (float)config.pivot_x);
461                                 config.pivot_y = input.tag.get_property("PIVOT_Y", (float)config.pivot_y);
462                                 config.draw_pivot = input.tag.get_property("DRAW_PIVOT", (int)config.draw_pivot);
463 //                              config.bilinear = input.tag.get_property("INTERPOLATE", (int)config.bilinear);
464                         }
465                 }
466         }
467 }
468
469 int RotateEffect::process_buffer(VFrame *frame,
470         int64_t start_position,
471         double frame_rate)
472 {
473         load_configuration();
474         int w = frame->get_w();
475         int h = frame->get_h();
476 //printf("RotateEffect::process_buffer %d\n", __LINE__);
477
478
479         if(config.angle == 0 && !config.draw_pivot)
480         {
481                 read_frame(frame,
482                         0,
483                         start_position,
484                         frame_rate,
485                         get_use_opengl());
486                 return 1;
487         }
488 //printf("RotateEffect::process_buffer %d\n", __LINE__);
489
490         if(!engine) engine = new AffineEngine(PluginClient::smp + 1,
491                 PluginClient::smp + 1);
492         int pivot_x = (int)(config.pivot_x * get_input()->get_w() / 100);
493         int pivot_y = (int)(config.pivot_y * get_input()->get_h() / 100);
494         engine->set_in_pivot(pivot_x, pivot_y);
495         engine->set_out_pivot(pivot_x, pivot_y);
496
497
498 // Test
499 // engine->set_out_viewport(0, 0, 320, 240);
500 // engine->set_out_pivot(160, 120);
501
502         if(get_use_opengl())
503         {
504                 read_frame(frame,
505                         0,
506                         start_position,
507                         frame_rate,
508                         get_use_opengl());
509                 return run_opengl();
510         }
511 //printf("RotateEffect::process_buffer %d\n", __LINE__);
512
513
514 // engine->set_viewport(50,
515 // 50,
516 // 100,
517 // 100);
518 // engine->set_pivot(100, 100);
519
520
521         VFrame *temp_frame = PluginVClient::new_temp(get_input()->get_w(),
522                 get_input()->get_h(),
523                 get_input()->get_color_model());
524         read_frame(temp_frame,
525                 0,
526                 start_position,
527                 frame_rate,
528                 get_use_opengl());
529         frame->clear_frame();
530         engine->rotate(frame,
531                 temp_frame,
532                 config.angle);
533
534 //printf("RotateEffect::process_buffer %d draw_pivot=%d\n", __LINE__, config.draw_pivot);
535
536 // Draw center
537 #define CENTER_H xS(20)
538 #define CENTER_W yS(20)
539 #define DRAW_CENTER(components, type, max) \
540 { \
541         type **rows = (type**)get_output()->get_rows(); \
542         if( (center_x >= 0 && center_x < w) && (center_y >= 0 && center_y < h) ) \
543         { \
544                 for(int i = center_x - CENTER_W / 2; i <= center_x + CENTER_W / 2; i++) \
545                 { \
546                         if(i >= 0 && i < w) \
547                         { \
548                                 type *hrow = rows[center_y] + components * i; \
549                                 hrow[0] = max - hrow[0]; \
550                                 hrow[1] = max - hrow[1]; \
551                                 hrow[2] = max - hrow[2]; \
552                                 hrow += components; \
553                         } \
554                 } \
555  \
556                 for(int i = center_y - CENTER_W / 2; i <= center_y + CENTER_W / 2; i++) \
557                 { \
558                         if(i >= 0 && i < h) \
559                         { \
560                                 type *vrow = rows[i] + center_x * components; \
561                                 vrow[0] = max - vrow[0]; \
562                                 vrow[1] = max - vrow[1]; \
563                                 vrow[2] = max - vrow[2]; \
564                         } \
565                 } \
566         } \
567 }
568
569         if(config.draw_pivot)
570         {
571                 int center_x = (int)(config.pivot_x * w / 100); \
572                 int center_y = (int)(config.pivot_y * h / 100); \
573
574 //printf("RotateEffect::process_buffer %d %d %d\n", __LINE__, center_x, center_y);
575                 switch(get_output()->get_color_model())
576                 {
577                         case BC_RGB_FLOAT:
578                                 DRAW_CENTER(3, float, 1.0)
579                                 break;
580                         case BC_RGBA_FLOAT:
581                                 DRAW_CENTER(4, float, 1.0)
582                                 break;
583                         case BC_RGB888:
584                                 DRAW_CENTER(3, unsigned char, 0xff)
585                                 break;
586                         case BC_RGBA8888:
587                                 DRAW_CENTER(4, unsigned char, 0xff)
588                                 break;
589                         case BC_YUV888:
590                                 DRAW_CENTER(3, unsigned char, 0xff)
591                                 break;
592                         case BC_YUVA8888:
593                                 DRAW_CENTER(4, unsigned char, 0xff)
594                                 break;
595                 }
596         }
597
598 // Conserve memory by deleting large frames
599         if(get_input()->get_w() > PLUGIN_MAX_W &&
600                 get_input()->get_h() > PLUGIN_MAX_H)
601         {
602                 delete engine;
603                 engine = 0;
604         }
605         return 0;
606 }
607
608
609
610 int RotateEffect::handle_opengl()
611 {
612 #ifdef HAVE_GL
613         engine->set_opengl(1);
614         engine->rotate(get_output(),
615                 get_output(),
616                 config.angle);
617         engine->set_opengl(0);
618
619         if(config.draw_pivot)
620         {
621                 int w = get_output()->get_w();
622                 int h = get_output()->get_h();
623                 int center_x = (int)(config.pivot_x * w / 100);
624                 int center_y = (int)(config.pivot_y * h / 100);
625
626                 glDisable(GL_TEXTURE_2D);
627                 glColor4f(0.0, 0.0, 0.0, 1.0);
628                 glBegin(GL_LINES);
629                 glVertex3f(center_x, -h + center_y - CENTER_H / 2, 0.0);
630                 glVertex3f(center_x, -h + center_y + CENTER_H / 2, 0.0);
631                 glEnd();
632                 glBegin(GL_LINES);
633                 glVertex3f(center_x - CENTER_W / 2, -h + center_y, 0.0);
634                 glVertex3f(center_x + CENTER_W / 2, -h + center_y, 0.0);
635                 glEnd();
636                 glColor4f(1.0, 1.0, 1.0, 1.0);
637                 glBegin(GL_LINES);
638                 glVertex3f(center_x - 1, -h + center_y - CENTER_H / 2 - 1, 0.0);
639                 glVertex3f(center_x - 1, -h + center_y + CENTER_H / 2 - 1, 0.0);
640                 glEnd();
641                 glBegin(GL_LINES);
642                 glVertex3f(center_x - CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
643                 glVertex3f(center_x + CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
644                 glEnd();
645         }
646 #endif
647         return 0;
648 }
649
650