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