db30ad6ed76508c2ee2dd240da45c670c0bdf703
[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 #include "affine.h"
23 #include "bcdisplayinfo.h"
24 #include "clip.h"
25 #include "bchash.h"
26 #include "filexml.h"
27 #include "guicast.h"
28 #include "language.h"
29 #include "pluginvclient.h"
30 #include "rotateframe.h"
31 #include "vframe.h"
32
33
34 #include <string.h>
35
36
37
38 #define MAXANGLE 360
39
40
41 class RotateEffect;
42 class RotateWindow;
43
44
45 class RotateConfig
46 {
47 public:
48         RotateConfig();
49
50         int equivalent(RotateConfig &that);
51         void copy_from(RotateConfig &that);
52         void interpolate(RotateConfig &prev,
53                 RotateConfig &next,
54                 long prev_frame,
55                 long next_frame,
56                 long current_frame);
57
58         float angle;
59         float pivot_x;
60         float pivot_y;
61         int draw_pivot;
62 //      int bilinear;
63 };
64
65 class RotateToggle : public BC_Radial
66 {
67 public:
68         RotateToggle(RotateWindow *window,
69                 RotateEffect *plugin,
70                 int init_value,
71                 int x,
72                 int y,
73                 int value,
74                 const char *string);
75         int handle_event();
76
77         RotateEffect *plugin;
78     RotateWindow *window;
79     int value;
80 };
81
82 class RotateDrawPivot : public BC_CheckBox
83 {
84 public:
85         RotateDrawPivot(RotateWindow *window,
86                 RotateEffect *plugin,
87                 int x,
88                 int y);
89         int handle_event();
90         RotateEffect *plugin;
91     RotateWindow *window;
92     int value;
93 };
94
95 class RotateInterpolate : public BC_CheckBox
96 {
97 public:
98         RotateInterpolate(RotateEffect *plugin, int x, int y);
99         int handle_event();
100         RotateEffect *plugin;
101 };
102
103 class RotateFine : public BC_FPot
104 {
105 public:
106         RotateFine(RotateWindow *window,
107                 RotateEffect *plugin,
108                 int x,
109                 int y);
110         int handle_event();
111
112         RotateEffect *plugin;
113     RotateWindow *window;
114 };
115
116 class RotateX : public BC_FPot
117 {
118 public:
119         RotateX(RotateWindow *window,
120                 RotateEffect *plugin,
121                 int x,
122                 int y);
123         int handle_event();
124         RotateEffect *plugin;
125     RotateWindow *window;
126 };
127
128 class RotateY : public BC_FPot
129 {
130 public:
131         RotateY(RotateWindow *window,
132                 RotateEffect *plugin,
133                 int x,
134                 int y);
135         int handle_event();
136         RotateEffect *plugin;
137     RotateWindow *window;
138 };
139
140
141 class RotateText : public BC_TextBox
142 {
143 public:
144         RotateText(RotateWindow *window,
145                 RotateEffect *plugin,
146                 int x,
147                 int y);
148         int handle_event();
149
150         RotateEffect *plugin;
151     RotateWindow *window;
152 };
153
154 class RotateWindow : public PluginClientWindow
155 {
156 public:
157         RotateWindow(RotateEffect *plugin);
158
159         void create_objects();
160
161         int update();
162         int update_fine();
163         int update_text();
164         int update_toggles();
165
166         RotateEffect *plugin;
167         RotateToggle *toggle0;
168         RotateToggle *toggle90;
169         RotateToggle *toggle180;
170         RotateToggle *toggle270;
171         RotateDrawPivot *draw_pivot;
172         RotateFine *fine;
173         RotateText *text;
174         RotateX *x;
175         RotateY *y;
176 //      RotateInterpolate *bilinear;
177 };
178
179
180
181
182 class RotateEffect : public PluginVClient
183 {
184 public:
185         RotateEffect(PluginServer *server);
186         ~RotateEffect();
187
188         PLUGIN_CLASS_MEMBERS(RotateConfig)
189         int process_buffer(VFrame *frame,
190                 int64_t start_position,
191                 double frame_rate);
192         int is_realtime();
193         void update_gui();
194         void save_data(KeyFrame *keyframe);
195         void read_data(KeyFrame *keyframe);
196         int handle_opengl();
197
198         AffineEngine *engine;
199         int need_reconfigure;
200 };
201
202
203
204
205
206
207
208 REGISTER_PLUGIN(RotateEffect)
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 RotateConfig::RotateConfig()
228 {
229         angle = 0;
230         pivot_x = 50;
231         pivot_y = 50;
232         draw_pivot = 0;
233 }
234
235 int RotateConfig::equivalent(RotateConfig &that)
236 {
237         return EQUIV(angle, that.angle) &&
238                 EQUIV(pivot_x, that.pivot_y) &&
239                 EQUIV(pivot_y, that.pivot_y) &&
240                 draw_pivot == that.draw_pivot;
241 }
242
243 void RotateConfig::copy_from(RotateConfig &that)
244 {
245         angle = that.angle;
246         pivot_x = that.pivot_x;
247         pivot_y = that.pivot_y;
248         draw_pivot = that.draw_pivot;
249 //      bilinear = that.bilinear;
250 }
251
252 void RotateConfig::interpolate(RotateConfig &prev,
253                 RotateConfig &next,
254                 long prev_frame,
255                 long next_frame,
256                 long current_frame)
257 {
258         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
259         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
260
261         this->angle = prev.angle * prev_scale + next.angle * next_scale;
262         this->pivot_x = prev.pivot_x * prev_scale + next.pivot_x * next_scale;
263         this->pivot_y = prev.pivot_y * prev_scale + next.pivot_y * next_scale;
264         draw_pivot = prev.draw_pivot;
265 //      bilinear = prev.bilinear;
266 }
267
268
269
270
271
272
273
274
275
276
277
278 RotateToggle::RotateToggle(RotateWindow *window,
279         RotateEffect *plugin,
280         int init_value,
281         int x,
282         int y,
283         int value,
284         const char *string)
285  : BC_Radial(x, y, init_value, string)
286 {
287         this->value = value;
288         this->plugin = plugin;
289     this->window = window;
290 }
291
292 int RotateToggle::handle_event()
293 {
294         plugin->config.angle = (float)value;
295     window->update();
296         plugin->send_configure_change();
297         return 1;
298 }
299
300
301
302
303
304
305
306 RotateDrawPivot::RotateDrawPivot(RotateWindow *window,
307         RotateEffect *plugin,
308         int x,
309         int y)
310  : BC_CheckBox(x, y, plugin->config.draw_pivot, _("Draw pivot"))
311 {
312         this->plugin = plugin;
313     this->window = window;
314 }
315
316 int RotateDrawPivot::handle_event()
317 {
318         plugin->config.draw_pivot = get_value();
319         plugin->send_configure_change();
320         return 1;
321 }
322
323
324
325
326
327 // RotateInterpolate::RotateInterpolate(RotateEffect *plugin, int x, int y)
328 //  : BC_CheckBox(x, y, plugin->config.bilinear, _("Interpolate"))
329 // {
330 //      this->plugin = plugin;
331 // }
332 // int RotateInterpolate::handle_event()
333 // {
334 //      plugin->config.bilinear = get_value();
335 //      plugin->send_configure_change();
336 //      return 1;
337 // }
338 //
339
340
341
342 RotateFine::RotateFine(RotateWindow *window, RotateEffect *plugin, int x, int y)
343  : BC_FPot(x,
344         y,
345         (float)plugin->config.angle,
346         (float)-360,
347         (float)360)
348 {
349         this->window = window;
350         this->plugin = plugin;
351         set_precision(0.01);
352         set_use_caption(0);
353 }
354
355 int RotateFine::handle_event()
356 {
357         plugin->config.angle = get_value();
358         window->update_toggles();
359         window->update_text();
360         plugin->send_configure_change();
361         return 1;
362 }
363
364
365
366 RotateText::RotateText(RotateWindow *window,
367         RotateEffect *plugin,
368         int x,
369         int y)
370  : BC_TextBox(x,
371         y,
372         100,
373         1,
374         (float)plugin->config.angle)
375 {
376         this->window = window;
377         this->plugin = plugin;
378         set_precision(4);
379 }
380
381 int RotateText::handle_event()
382 {
383         plugin->config.angle = atof(get_text());
384         window->update_toggles();
385         window->update_fine();
386         plugin->send_configure_change();
387         return 1;
388 }
389
390
391
392 RotateX::RotateX(RotateWindow *window, RotateEffect *plugin, int x, int y)
393  : BC_FPot(x,
394         y,
395         (float)plugin->config.pivot_x,
396         (float)0,
397         (float)100)
398 {
399         this->window = window;
400         this->plugin = plugin;
401         set_precision(0.01);
402         set_use_caption(1);
403 }
404
405 int RotateX::handle_event()
406 {
407         plugin->config.pivot_x = get_value();
408         plugin->send_configure_change();
409         return 1;
410 }
411
412 RotateY::RotateY(RotateWindow *window, RotateEffect *plugin, int x, int y)
413  : BC_FPot(x,
414         y,
415         (float)plugin->config.pivot_y,
416         (float)0,
417         (float)100)
418 {
419         this->window = window;
420         this->plugin = plugin;
421         set_precision(0.01);
422         set_use_caption(1);
423 }
424
425 int RotateY::handle_event()
426 {
427         plugin->config.pivot_y = get_value();
428         plugin->send_configure_change();
429         return 1;
430 }
431
432
433
434
435
436
437
438
439 RotateWindow::RotateWindow(RotateEffect *plugin)
440  : PluginClientWindow(plugin,
441         250,
442         230,
443         250,
444         230,
445         0)
446 {
447         this->plugin = plugin;
448 }
449
450 #define RADIUS 30
451
452 void RotateWindow::create_objects()
453 {
454         int x = 10, y = 10;
455         BC_Title *title;
456
457
458
459         add_tool(new BC_Title(x, y, _("Rotate")));
460         x += 50;
461         y += 20;
462         add_tool(toggle0 = new RotateToggle(this,
463                 plugin,
464                 plugin->config.angle == 0,
465                 x,
466                 y,
467                 0,
468                 "0"));
469     x += RADIUS;
470     y += RADIUS;
471         add_tool(toggle90 = new RotateToggle(this,
472                 plugin,
473                 plugin->config.angle == 90,
474                 x,
475                 y,
476                 90,
477                 "90"));
478     x -= RADIUS;
479     y += RADIUS;
480         add_tool(toggle180 = new RotateToggle(this,
481                 plugin,
482                 plugin->config.angle == 180,
483                 x,
484                 y,
485                 180,
486                 "180"));
487     x -= RADIUS;
488     y -= RADIUS;
489         add_tool(toggle270 = new RotateToggle(this,
490                 plugin,
491                 plugin->config.angle == 270,
492                 x,
493                 y,
494                 270,
495                 "270"));
496 //      add_subwindow(bilinear = new RotateInterpolate(plugin, 10, y + 60));
497         x += 120;
498         y -= 50;
499         add_tool(fine = new RotateFine(this, plugin, x, y));
500         y += fine->get_h() + 10;
501         add_tool(text = new RotateText(this, plugin, x, y));
502         y += 30;
503         add_tool(new BC_Title(x, y, _("Degrees")));
504
505
506
507
508
509         y += text->get_h() + 10;
510         add_subwindow(title = new BC_Title(x, y, _("Pivot (x,y):")));
511         y += title->get_h() + 10;
512         add_subwindow(this->x = new RotateX(this, plugin, x, y));
513         x += this->x->get_w() + 10;
514         add_subwindow(this->y = new RotateY(this, plugin, x, y));
515
516         x = 10;
517         y += this->y->get_h() + 10;
518         add_subwindow(draw_pivot = new RotateDrawPivot(this, plugin, x, y));
519
520         show_window();
521
522
523
524 }
525
526
527
528 int RotateWindow::update()
529 {
530         update_fine();
531         update_toggles();
532         update_text();
533 //      bilinear->update(plugin->config.bilinear);
534         return 0;
535 }
536
537 int RotateWindow::update_fine()
538 {
539         fine->update(plugin->config.angle);
540         x->update(plugin->config.pivot_x);
541         y->update(plugin->config.pivot_y);
542         return 0;
543 }
544
545 int RotateWindow::update_text()
546 {
547         text->update(plugin->config.angle);
548         return 0;
549 }
550
551 int RotateWindow::update_toggles()
552 {
553         toggle0->update(EQUIV(plugin->config.angle, 0));
554         toggle90->update(EQUIV(plugin->config.angle, 90));
555         toggle180->update(EQUIV(plugin->config.angle, 180));
556         toggle270->update(EQUIV(plugin->config.angle, 270));
557         draw_pivot->update(plugin->config.draw_pivot);
558         return 0;
559 }
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594 RotateEffect::RotateEffect(PluginServer *server)
595  : PluginVClient(server)
596 {
597         engine = 0;
598         need_reconfigure = 1;
599
600 }
601
602 RotateEffect::~RotateEffect()
603 {
604
605         if(engine) delete engine;
606 }
607
608
609
610 const char* RotateEffect::plugin_title() { return N_("Rotate"); }
611 int RotateEffect::is_realtime() { return 1; }
612
613
614 NEW_WINDOW_MACRO(RotateEffect, RotateWindow)
615
616
617 void RotateEffect::update_gui()
618 {
619         if(thread)
620         {
621                 load_configuration();
622                 thread->window->lock_window();
623                 ((RotateWindow*)thread->window)->update();
624                 thread->window->unlock_window();
625         }
626 }
627
628 LOAD_CONFIGURATION_MACRO(RotateEffect, RotateConfig)
629
630
631
632
633 void RotateEffect::save_data(KeyFrame *keyframe)
634 {
635         FileXML output;
636
637 // cause data to be stored directly in text
638         output.set_shared_output(keyframe->xbuf);
639         output.tag.set_title("ROTATE");
640         output.tag.set_property("ANGLE", (float)config.angle);
641         output.tag.set_property("PIVOT_X", (float)config.pivot_x);
642         output.tag.set_property("PIVOT_Y", (float)config.pivot_y);
643         output.tag.set_property("DRAW_PIVOT", (int)config.draw_pivot);
644 //      output.tag.set_property("INTERPOLATE", (int)config.bilinear);
645         output.append_tag();
646         output.tag.set_title("/ROTATE");
647         output.append_tag();
648         output.append_newline();
649         output.terminate_string();
650 // data is now in *text
651 }
652
653 void RotateEffect::read_data(KeyFrame *keyframe)
654 {
655         FileXML input;
656
657         input.set_shared_input(keyframe->xbuf);
658
659         int result = 0;
660
661         while(!result)
662         {
663                 result = input.read_tag();
664
665                 if(!result)
666                 {
667                         if(input.tag.title_is("ROTATE"))
668                         {
669                                 config.angle = input.tag.get_property("ANGLE", (float)config.angle);
670                                 config.pivot_x = input.tag.get_property("PIVOT_X", (float)config.pivot_x);
671                                 config.pivot_y = input.tag.get_property("PIVOT_Y", (float)config.pivot_y);
672                                 config.draw_pivot = input.tag.get_property("DRAW_PIVOT", (int)config.draw_pivot);
673 //                              config.bilinear = input.tag.get_property("INTERPOLATE", (int)config.bilinear);
674                         }
675                 }
676         }
677 }
678
679 int RotateEffect::process_buffer(VFrame *frame,
680         int64_t start_position,
681         double frame_rate)
682 {
683         load_configuration();
684         int w = frame->get_w();
685         int h = frame->get_h();
686 //printf("RotateEffect::process_buffer %d\n", __LINE__);
687
688
689         if(config.angle == 0)
690         {
691                 read_frame(frame,
692                         0,
693                         start_position,
694                         frame_rate,
695                         get_use_opengl());
696                 return 1;
697         }
698 //printf("RotateEffect::process_buffer %d\n", __LINE__);
699
700         if(!engine) engine = new AffineEngine(PluginClient::smp + 1,
701                 PluginClient::smp + 1);
702         int pivot_x = (int)(config.pivot_x * get_input()->get_w() / 100);
703         int pivot_y = (int)(config.pivot_y * get_input()->get_h() / 100);
704         engine->set_in_pivot(pivot_x, pivot_y);
705         engine->set_out_pivot(pivot_x, pivot_y);
706
707
708 // Test
709 // engine->set_out_viewport(0, 0, 320, 240);
710 // engine->set_out_pivot(160, 120);
711
712         if(get_use_opengl())
713         {
714                 read_frame(frame,
715                         0,
716                         start_position,
717                         frame_rate,
718                         get_use_opengl());
719                 return run_opengl();
720         }
721 //printf("RotateEffect::process_buffer %d\n", __LINE__);
722
723
724 // engine->set_viewport(50,
725 // 50,
726 // 100,
727 // 100);
728 // engine->set_pivot(100, 100);
729
730
731         VFrame *temp_frame = PluginVClient::new_temp(get_input()->get_w(),
732                 get_input()->get_h(),
733                 get_input()->get_color_model());
734         read_frame(temp_frame,
735                 0,
736                 start_position,
737                 frame_rate,
738                 get_use_opengl());
739         frame->clear_frame();
740         engine->rotate(frame,
741                 temp_frame,
742                 config.angle);
743
744 //printf("RotateEffect::process_buffer %d draw_pivot=%d\n", __LINE__, config.draw_pivot);
745
746 // Draw center
747 #define CENTER_H 20
748 #define CENTER_W 20
749 #define DRAW_CENTER(components, type, max) \
750 { \
751         type **rows = (type**)get_output()->get_rows(); \
752         if( (center_x >= 0 && center_x < w) || (center_y >= 0 && center_y < h) ) \
753         { \
754                 type *hrow = rows[center_y] + components * (center_x - CENTER_W / 2); \
755                 for(int i = center_x - CENTER_W / 2; i <= center_x + CENTER_W / 2; i++) \
756                 { \
757                         if(i >= 0 && i < w) \
758                         { \
759                                 hrow[0] = max - hrow[0]; \
760                                 hrow[1] = max - hrow[1]; \
761                                 hrow[2] = max - hrow[2]; \
762                                 hrow += components; \
763                         } \
764                 } \
765  \
766                 for(int i = center_y - CENTER_W / 2; i <= center_y + CENTER_W / 2; i++) \
767                 { \
768                         if(i >= 0 && i < h) \
769                         { \
770                                 type *vrow = rows[i] + center_x * components; \
771                                 vrow[0] = max - vrow[0]; \
772                                 vrow[1] = max - vrow[1]; \
773                                 vrow[2] = max - vrow[2]; \
774                         } \
775                 } \
776         } \
777 }
778
779         if(config.draw_pivot)
780         {
781                 int center_x = (int)(config.pivot_x * w / 100); \
782                 int center_y = (int)(config.pivot_y * h / 100); \
783
784 //printf("RotateEffect::process_buffer %d %d %d\n", __LINE__, center_x, center_y);
785                 switch(get_output()->get_color_model())
786                 {
787                         case BC_RGB_FLOAT:
788                                 DRAW_CENTER(3, float, 1.0)
789                                 break;
790                         case BC_RGBA_FLOAT:
791                                 DRAW_CENTER(4, float, 1.0)
792                                 break;
793                         case BC_RGB888:
794                                 DRAW_CENTER(3, unsigned char, 0xff)
795                                 break;
796                         case BC_RGBA8888:
797                                 DRAW_CENTER(4, unsigned char, 0xff)
798                                 break;
799                         case BC_YUV888:
800                                 DRAW_CENTER(3, unsigned char, 0xff)
801                                 break;
802                         case BC_YUVA8888:
803                                 DRAW_CENTER(4, unsigned char, 0xff)
804                                 break;
805                 }
806         }
807
808 // Conserve memory by deleting large frames
809         if(get_input()->get_w() > PLUGIN_MAX_W &&
810                 get_input()->get_h() > PLUGIN_MAX_H)
811         {
812                 delete engine;
813                 engine = 0;
814         }
815         return 0;
816 }
817
818
819
820 int RotateEffect::handle_opengl()
821 {
822 #ifdef HAVE_GL
823         engine->set_opengl(1);
824         engine->rotate(get_output(),
825                 get_output(),
826                 config.angle);
827         engine->set_opengl(0);
828
829         if(config.draw_pivot)
830         {
831                 int w = get_output()->get_w();
832                 int h = get_output()->get_h();
833                 int center_x = (int)(config.pivot_x * w / 100);
834                 int center_y = (int)(config.pivot_y * h / 100);
835
836                 glDisable(GL_TEXTURE_2D);
837                 glColor4f(0.0, 0.0, 0.0, 1.0);
838                 glBegin(GL_LINES);
839                 glVertex3f(center_x, -h + center_y - CENTER_H / 2, 0.0);
840                 glVertex3f(center_x, -h + center_y + CENTER_H / 2, 0.0);
841                 glEnd();
842                 glBegin(GL_LINES);
843                 glVertex3f(center_x - CENTER_W / 2, -h + center_y, 0.0);
844                 glVertex3f(center_x + CENTER_W / 2, -h + center_y, 0.0);
845                 glEnd();
846                 glColor4f(1.0, 1.0, 1.0, 1.0);
847                 glBegin(GL_LINES);
848                 glVertex3f(center_x - 1, -h + center_y - CENTER_H / 2 - 1, 0.0);
849                 glVertex3f(center_x - 1, -h + center_y + CENTER_H / 2 - 1, 0.0);
850                 glEnd();
851                 glBegin(GL_LINES);
852                 glVertex3f(center_x - CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
853                 glVertex3f(center_x + CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
854                 glEnd();
855         }
856 #endif
857         return 0;
858 }
859
860