035ab0af196781b2a49658055c1756436d3cec86
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / radialblur / radialblur.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 "radialblur.h"
24
25
26
27 REGISTER_PLUGIN(RadialBlurMain)
28
29
30
31 RadialBlurConfig::RadialBlurConfig()
32 {
33         reset();
34 }
35
36
37 void RadialBlurConfig::reset()
38 {
39         x = 50;
40         y = 50;
41         steps = 10;
42         angle = 33;
43         r = 1;
44         g = 1;
45         b = 1;
46         a = 1;
47 }
48
49 int RadialBlurConfig::equivalent(RadialBlurConfig &that)
50 {
51         return
52                 angle == that.angle &&
53                 x == that.x &&
54                 y == that.y &&
55                 steps == that.steps &&
56                 r == that.r &&
57                 g == that.g &&
58                 b == that.b &&
59                 a == that.a;
60 }
61
62 void RadialBlurConfig::copy_from(RadialBlurConfig &that)
63 {
64         x = that.x;
65         y = that.y;
66         angle = that.angle;
67         steps = that.steps;
68         r = that.r;
69         g = that.g;
70         b = that.b;
71         a = that.a;
72 }
73
74 void RadialBlurConfig::interpolate(RadialBlurConfig &prev,
75         RadialBlurConfig &next,
76         long prev_frame,
77         long next_frame,
78         long current_frame)
79 {
80         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
81         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
82         this->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
83         this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
84         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
85         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale + 0.5);
86         r = prev.r;
87         g = prev.g;
88         b = prev.b;
89         a = prev.a;
90 }
91
92
93
94
95
96
97
98
99
100
101
102
103
104 RadialBlurWindow::RadialBlurWindow(RadialBlurMain *plugin)
105  : PluginClientWindow(plugin,
106         230,
107         370,
108         230,
109         370,
110         0)
111 {
112         this->plugin = plugin;
113 }
114
115 RadialBlurWindow::~RadialBlurWindow()
116 {
117 }
118
119 void RadialBlurWindow::create_objects()
120 {
121         int x = 10, y = 10;
122
123         add_subwindow(new BC_Title(x, y, _("X:")));
124         y += 20;
125         add_subwindow(this->x = new RadialBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
126         y += 30;
127         add_subwindow(new BC_Title(x, y, _("Y:")));
128         y += 20;
129         add_subwindow(this->y = new RadialBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
130         y += 30;
131         add_subwindow(new BC_Title(x, y, _("Angle:")));
132         y += 20;
133         add_subwindow(angle = new RadialBlurSize(plugin, x, y, &plugin->config.angle, 0, 360));
134         y += 30;
135         add_subwindow(new BC_Title(x, y, _("Steps:")));
136         y += 20;
137         add_subwindow(steps = new RadialBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
138         y += 30;
139         add_subwindow(r = new RadialBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
140         y += 30;
141         add_subwindow(g = new RadialBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
142         y += 30;
143         add_subwindow(b = new RadialBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
144         y += 30;
145         add_subwindow(a = new RadialBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
146         y += 40;
147         add_subwindow(reset = new RadialBlurReset(plugin, this, x, y));
148
149         show_window();
150         flush();
151 }
152
153 // for Reset button
154 void RadialBlurWindow::update()
155 {
156         this->x->update(plugin->config.x);
157         this->y->update(plugin->config.y);
158         angle->update(plugin->config.angle);
159         steps->update(plugin->config.steps);
160         r->update(plugin->config.r);
161         g->update(plugin->config.g);
162         b->update(plugin->config.b);
163         a->update(plugin->config.a);
164 }
165
166
167
168
169
170
171
172
173
174 RadialBlurToggle::RadialBlurToggle(RadialBlurMain *plugin,
175         int x,
176         int y,
177         int *output,
178         char *string)
179  : BC_CheckBox(x, y, *output, string)
180 {
181         this->plugin = plugin;
182         this->output = output;
183 }
184
185 int RadialBlurToggle::handle_event()
186 {
187         *output = get_value();
188         plugin->send_configure_change();
189         return 1;
190 }
191
192
193
194
195
196
197
198 RadialBlurSize::RadialBlurSize(RadialBlurMain *plugin,
199         int x,
200         int y,
201         int *output,
202         int min,
203         int max)
204  : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
205 {
206         this->plugin = plugin;
207         this->output = output;
208 }
209 int RadialBlurSize::handle_event()
210 {
211         *output = get_value();
212         plugin->send_configure_change();
213         return 1;
214 }
215
216
217
218
219
220
221
222 RadialBlurReset::RadialBlurReset(RadialBlurMain *plugin, RadialBlurWindow *gui, int x, int y)
223  : BC_GenericButton(x, y, _("Reset"))
224 {
225         this->plugin = plugin;
226         this->gui = gui;
227 }
228 RadialBlurReset::~RadialBlurReset()
229 {
230 }
231 int RadialBlurReset::handle_event()
232 {
233         plugin->config.reset();
234         gui->update();
235         plugin->send_configure_change();
236         return 1;
237 }
238
239
240
241
242
243
244
245 RadialBlurMain::RadialBlurMain(PluginServer *server)
246  : PluginVClient(server)
247 {
248
249         engine = 0;
250         temp = 0;
251         rotate = 0;
252 }
253
254 RadialBlurMain::~RadialBlurMain()
255 {
256
257         if(engine) delete engine;
258         if(temp) delete temp;
259         delete rotate;
260 }
261
262 const char* RadialBlurMain::plugin_title() { return N_("Radial Blur"); }
263 int RadialBlurMain::is_realtime() { return 1; }
264
265
266
267 NEW_WINDOW_MACRO(RadialBlurMain, RadialBlurWindow)
268
269 LOAD_CONFIGURATION_MACRO(RadialBlurMain, RadialBlurConfig)
270
271 int RadialBlurMain::process_buffer(VFrame *frame,
272                                                         int64_t start_position,
273                                                         double frame_rate)
274 {
275         load_configuration();
276
277
278         read_frame(frame,
279                 0,
280                 get_source_position(),
281                 get_framerate(),
282 //              0);
283                 get_use_opengl());
284
285         if(get_use_opengl()) return run_opengl();
286
287         if(!engine) engine = new RadialBlurEngine(this,
288                 get_project_smp() + 1,
289                 get_project_smp() + 1);
290
291         this->input = frame;
292         this->output = frame;
293
294
295         if(!temp)
296                 temp = new VFrame(frame->get_w(), frame->get_h(),
297                         frame->get_color_model(), 0);
298         temp->copy_from(frame);
299         this->input = temp;
300
301         engine->process_packages();
302         return 0;
303 }
304
305
306 void RadialBlurMain::update_gui()
307 {
308         if(thread)
309         {
310                 load_configuration();
311                 thread->window->lock_window();
312                 ((RadialBlurWindow*)thread->window)->x->update(config.x);
313                 ((RadialBlurWindow*)thread->window)->y->update(config.y);
314                 ((RadialBlurWindow*)thread->window)->angle->update(config.angle);
315                 ((RadialBlurWindow*)thread->window)->steps->update(config.steps);
316                 ((RadialBlurWindow*)thread->window)->r->update(config.r);
317                 ((RadialBlurWindow*)thread->window)->g->update(config.g);
318                 ((RadialBlurWindow*)thread->window)->b->update(config.b);
319                 ((RadialBlurWindow*)thread->window)->a->update(config.a);
320                 thread->window->unlock_window();
321         }
322 }
323
324
325
326
327 void RadialBlurMain::save_data(KeyFrame *keyframe)
328 {
329         FileXML output;
330
331 // cause data to be stored directly in text
332         output.set_shared_output(keyframe->xbuf);
333         output.tag.set_title("RADIALBLUR");
334
335         output.tag.set_property("X", config.x);
336         output.tag.set_property("Y", config.y);
337         output.tag.set_property("ANGLE", config.angle);
338         output.tag.set_property("STEPS", config.steps);
339         output.tag.set_property("R", config.r);
340         output.tag.set_property("G", config.g);
341         output.tag.set_property("B", config.b);
342         output.tag.set_property("A", config.a);
343         output.append_tag();
344         output.tag.set_title("/RADIALBLUR");
345         output.append_tag();
346         output.append_newline();
347         output.terminate_string();
348 }
349
350 void RadialBlurMain::read_data(KeyFrame *keyframe)
351 {
352         FileXML input;
353
354         input.set_shared_input(keyframe->xbuf);
355
356         int result = 0;
357
358         while(!result)
359         {
360                 result = input.read_tag();
361
362                 if(!result)
363                 {
364                         if(input.tag.title_is("RADIALBLUR"))
365                         {
366                                 config.x = input.tag.get_property("X", config.x);
367                                 config.y = input.tag.get_property("Y", config.y);
368                                 config.angle = input.tag.get_property("ANGLE", config.angle);
369                                 config.steps = input.tag.get_property("STEPS", config.steps);
370                                 config.r = input.tag.get_property("R", config.r);
371                                 config.g = input.tag.get_property("G", config.g);
372                                 config.b = input.tag.get_property("B", config.b);
373                                 config.a = input.tag.get_property("A", config.a);
374                         }
375                 }
376         }
377 }
378
379 int RadialBlurMain::handle_opengl()
380 {
381 #ifdef HAVE_GL
382         get_output()->to_texture();
383         get_output()->enable_opengl();
384         get_output()->init_screen();
385         get_output()->bind_texture(0);
386
387
388         //int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
389         glClearColor(0.0, 0.0, 0.0, 0.0);
390         glClear(GL_COLOR_BUFFER_BIT);
391
392 // Draw unselected channels
393         glEnable(GL_BLEND);
394         glBlendFunc(GL_ONE, GL_ONE);
395         glDrawBuffer(GL_BACK);
396         if(!config.r || !config.g || !config.b || !config.a)
397         {
398                 glColor4f(config.r ? 0 : 1,
399                         config.g ? 0 : 1,
400                         config.b ? 0 : 1,
401                         config.a ? 0 : 1);
402                 get_output()->draw_texture();
403         }
404         glAccum(GL_LOAD, 1.0);
405
406
407 // Blur selected channels
408         float fraction = 1.0 / config.steps;
409         for(int i = 0; i < config.steps; i++)
410         {
411                 get_output()->set_opengl_state(VFrame::TEXTURE);
412                 glClear(GL_COLOR_BUFFER_BIT);
413                 glColor4f(config.r ? 1 : 0,
414                         config.g ? 1 : 0,
415                         config.b ? 1 : 0,
416                         config.a ? 1 : 0);
417
418                 float w = get_output()->get_w();
419                 float h = get_output()->get_h();
420
421
422
423                 double current_angle = (double)config.angle *
424                         i /
425                         config.steps -
426                         (double)config.angle / 2;
427
428                 if(!rotate) rotate = new AffineEngine(PluginClient::smp + 1,
429                         PluginClient::smp + 1);
430                 rotate->set_in_pivot((int)(config.x * w / 100),
431                         (int)(config.y * h / 100));
432                 rotate->set_out_pivot((int)(config.x * w / 100),
433                         (int)(config.y * h / 100));
434                 rotate->set_opengl(1);
435                 rotate->rotate(get_output(),
436                         get_output(),
437                         current_angle);
438
439                 glAccum(GL_ACCUM, fraction);
440                 glEnable(GL_TEXTURE_2D);
441                 glColor4f(config.r ? 1 : 0,
442                         config.g ? 1 : 0,
443                         config.b ? 1 : 0,
444                         config.a ? 1 : 0);
445         }
446
447
448         glDisable(GL_BLEND);
449         glReadBuffer(GL_BACK);
450         glDisable(GL_TEXTURE_2D);
451         glAccum(GL_RETURN, 1.0);
452
453         glColor4f(1, 1, 1, 1);
454         get_output()->set_opengl_state(VFrame::SCREEN);
455 #endif
456         return 0;
457 }
458
459
460
461
462
463
464
465
466
467
468
469 RadialBlurPackage::RadialBlurPackage()
470  : LoadPackage()
471 {
472 }
473
474
475 RadialBlurUnit::RadialBlurUnit(RadialBlurEngine *server,
476         RadialBlurMain *plugin)
477  : LoadClient(server)
478 {
479         this->plugin = plugin;
480         this->server = server;
481 }
482
483
484 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
485 { \
486         int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
487         TYPE **in_rows = (TYPE**)plugin->input->get_rows(); \
488         TYPE **out_rows = (TYPE**)plugin->output->get_rows(); \
489         int steps = plugin->config.steps; \
490         double step = (double)plugin->config.angle / 360 * 2 * M_PI / steps; \
491  \
492         for(int i = pkg->y1, out_y = pkg->y1 - center_y; \
493                 i < pkg->y2; \
494                 i++, out_y++) \
495         { \
496                 TYPE *out_row = out_rows[i]; \
497                 TYPE *in_row = in_rows[i]; \
498                 int y_square = out_y * out_y; \
499  \
500                 for(int j = 0, out_x = -center_x; j < w; j++, out_x++) \
501                 { \
502                         TEMP_TYPE accum_r = 0; \
503                         TEMP_TYPE accum_g = 0; \
504                         TEMP_TYPE accum_b = 0; \
505                         TEMP_TYPE accum_a = 0; \
506  \
507 /* Output coordinate to polar */ \
508                         double magnitude = sqrt(y_square + out_x * out_x); \
509                         double angle; \
510                         if(out_y < 0) \
511                                 angle = atan((double)out_x / out_y) + M_PI; \
512                         else \
513                         if(out_y > 0) \
514                                 angle = atan((double)out_x / out_y); \
515                         else \
516                         if(out_x > 0) \
517                                 angle = M_PI / 2; \
518                         else \
519                                 angle = M_PI * 1.5; \
520  \
521 /* Overlay all steps on this pixel*/ \
522                         angle -= (double)plugin->config.angle / 360 * M_PI; \
523                         for(int k = 0; k < steps; k++, angle += step) \
524                         { \
525 /* Polar to input coordinate */ \
526                                 int in_x = (int)(magnitude * sin(angle)) + center_x; \
527                                 int in_y = (int)(magnitude * cos(angle)) + center_y; \
528  \
529 /* Accumulate input coordinate */ \
530                                 if(in_x >= 0 && in_x < w && in_y >= 0 && in_y < h) \
531                                 { \
532                                         accum_r += in_rows[in_y][in_x * COMPONENTS]; \
533                                         if(DO_YUV) \
534                                         { \
535                                                 accum_g += (int)in_rows[in_y][in_x * COMPONENTS + 1]; \
536                                                 accum_b += (int)in_rows[in_y][in_x * COMPONENTS + 2]; \
537                                         } \
538                                         else \
539                                         { \
540                                                 accum_g += in_rows[in_y][in_x * COMPONENTS + 1]; \
541                                                 accum_b += in_rows[in_y][in_x * COMPONENTS + 2]; \
542                                         } \
543                                         if(COMPONENTS == 4) \
544                                                 accum_a += in_rows[in_y][in_x * COMPONENTS + 3]; \
545                                 } \
546                                 else \
547                                 { \
548                                         accum_g += chroma_offset; \
549                                         accum_b += chroma_offset; \
550                                 } \
551                         } \
552  \
553 /* Accumulation to output */ \
554                         if(do_r) \
555                         { \
556                                 *out_row++ = (accum_r * fraction) / 0x10000; \
557                                 in_row++; \
558                         } \
559                         else \
560                         { \
561                                 *out_row++ = *in_row++; \
562                         } \
563  \
564                         if(do_g) \
565                         { \
566                                 if(DO_YUV) \
567                                         *out_row++ = ((accum_g * fraction) / 0x10000); \
568                                 else \
569                                         *out_row++ = (accum_g * fraction) / 0x10000; \
570                                 in_row++; \
571                         } \
572                         else \
573                         { \
574                                 *out_row++ = *in_row++; \
575                         } \
576  \
577                         if(do_b) \
578                         { \
579                                 if(DO_YUV) \
580                                         *out_row++ = (accum_b * fraction) / 0x10000; \
581                                 else \
582                                         *out_row++ = (accum_b * fraction) / 0x10000; \
583                                 in_row++; \
584                         } \
585                         else \
586                         { \
587                                 *out_row++ = *in_row++; \
588                         } \
589  \
590                         if(COMPONENTS == 4) \
591                         { \
592                                 if(do_a) \
593                                 { \
594                                         *out_row++ = (accum_a * fraction) / 0x10000; \
595                                         in_row++; \
596                                 } \
597                                 else \
598                                 { \
599                                         *out_row++ = *in_row++; \
600                                 } \
601                         } \
602                 } \
603         } \
604 }
605
606 void RadialBlurUnit::process_package(LoadPackage *package)
607 {
608         RadialBlurPackage *pkg = (RadialBlurPackage*)package;
609         int h = plugin->output->get_h();
610         int w = plugin->output->get_w();
611         int do_r = plugin->config.r;
612         int do_g = plugin->config.g;
613         int do_b = plugin->config.b;
614         int do_a = plugin->config.a;
615         int fraction = 0x10000 / plugin->config.steps;
616         int center_x = plugin->config.x * w / 100;
617         int center_y = plugin->config.y * h / 100;
618
619         switch(plugin->input->get_color_model())
620         {
621                 case BC_RGB888:
622                         BLEND_LAYER(3, uint8_t, int, 0xff, 0)
623                         break;
624                 case BC_RGBA8888:
625                         BLEND_LAYER(4, uint8_t, int, 0xff, 0)
626                         break;
627                 case BC_RGB_FLOAT:
628                         BLEND_LAYER(3, float, float, 1, 0)
629                         break;
630                 case BC_RGBA_FLOAT:
631                         BLEND_LAYER(4, float, float, 1, 0)
632                         break;
633                 case BC_RGB161616:
634                         BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
635                         break;
636                 case BC_RGBA16161616:
637                         BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
638                         break;
639                 case BC_YUV888:
640                         BLEND_LAYER(3, uint8_t, int, 0xff, 1)
641                         break;
642                 case BC_YUVA8888:
643                         BLEND_LAYER(4, uint8_t, int, 0xff, 1)
644                         break;
645                 case BC_YUV161616:
646                         BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
647                         break;
648                 case BC_YUVA16161616:
649                         BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
650                         break;
651         }
652 }
653
654
655
656
657
658
659 RadialBlurEngine::RadialBlurEngine(RadialBlurMain *plugin,
660         int total_clients,
661         int total_packages)
662  : LoadServer(total_clients, total_packages)
663 // : LoadServer(1, 1)
664 {
665         this->plugin = plugin;
666 }
667
668 void RadialBlurEngine::init_packages()
669 {
670         for(int i = 0; i < get_total_packages(); i++)
671         {
672                 RadialBlurPackage *package = (RadialBlurPackage*)get_package(i);
673                 package->y1 = plugin->output->get_h() * i / get_total_packages();
674                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
675         }
676 }
677
678 LoadClient* RadialBlurEngine::new_client()
679 {
680         return new RadialBlurUnit(this, plugin);
681 }
682
683 LoadPackage* RadialBlurEngine::new_package()
684 {
685         return new RadialBlurPackage;
686 }
687
688
689
690
691