e93c100c7b78fee19546e298f1f1ca834b9f2ee3
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / zoomblur / zoomblur.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 "zoomblur.h"
24
25
26
27 REGISTER_PLUGIN(ZoomBlurMain)
28
29
30
31 ZoomBlurConfig::ZoomBlurConfig()
32 {
33         reset(RESET_DEFAULT_SETTINGS);
34 }
35
36 void ZoomBlurConfig::reset(int clear)
37 {
38         switch(clear) {
39                 case RESET_ALL :
40                         x = xS(50);
41                         y = yS(50);
42                         radius = 0;
43                         steps = 1;
44                         r = 1;
45                         g = 1;
46                         b = 1;
47                         a = 1;
48                         break;
49                 case RESET_XSLIDER : x = xS(50);
50                         break;
51                 case RESET_YSLIDER : y = yS(50);
52                         break;
53                 case RESET_RADIUS : radius = 0;
54                         break;
55                 case RESET_STEPS : steps = 1;
56                         break;
57                 case RESET_DEFAULT_SETTINGS :
58                 default:
59                         x = xS(50);
60                         y = yS(50);
61                         radius = 10;
62                         steps = 10;
63                         r = 1;
64                         g = 1;
65                         b = 1;
66                         a = 1;
67                         break;
68         }
69 }
70
71 int ZoomBlurConfig::equivalent(ZoomBlurConfig &that)
72 {
73         return
74                 x == that.x &&
75                 y == that.y &&
76                 radius == that.radius &&
77                 steps == that.steps &&
78                 r == that.r &&
79                 g == that.g &&
80                 b == that.b &&
81                 a == that.a;
82 }
83
84 void ZoomBlurConfig::copy_from(ZoomBlurConfig &that)
85 {
86         x = that.x;
87         y = that.y;
88         radius = that.radius;
89         steps = that.steps;
90         r = that.r;
91         g = that.g;
92         b = that.b;
93         a = that.a;
94 }
95
96 void ZoomBlurConfig::interpolate(ZoomBlurConfig &prev,
97         ZoomBlurConfig &next,
98         long prev_frame,
99         long next_frame,
100         long current_frame)
101 {
102         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
103         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
104         this->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
105         this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
106         this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
107         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
108         r = prev.r;
109         g = prev.g;
110         b = prev.b;
111         a = prev.a;
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125 ZoomBlurWindow::ZoomBlurWindow(ZoomBlurMain *plugin)
126  : PluginClientWindow(plugin,
127         xS(280),
128         yS(370),
129         xS(280),
130         yS(370),
131         0)
132 {
133         this->plugin = plugin;
134 }
135
136 ZoomBlurWindow::~ZoomBlurWindow()
137 {
138 }
139
140 void ZoomBlurWindow::create_objects()
141 {
142         int xs10 = xS(10), xs50 = xS(50), xs100 = xS(100);
143         int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40);
144         int x = xs10, y = ys10;
145         int x1 = 0; int clrBtn_w = xs50;
146         int defaultBtn_w = xs100;
147
148         add_subwindow(new BC_Title(x, y, _("X:")));
149         y += ys20;
150         add_subwindow(this->x = new ZoomBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
151         x1 = x + this->x->get_w() + xs10;
152         add_subwindow(xClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_XSLIDER));
153
154         y += ys30;
155         add_subwindow(new BC_Title(x, y, _("Y:")));
156         y += ys20;
157         add_subwindow(this->y = new ZoomBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
158         add_subwindow(yClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_YSLIDER));
159
160         y += ys30;
161         add_subwindow(new BC_Title(x, y, _("Radius:")));
162         y += ys20;
163         add_subwindow(radius = new ZoomBlurSize(plugin, x, y, &plugin->config.radius, -100, 100));
164         add_subwindow(radiusClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_RADIUS));
165
166         y += ys30;
167         add_subwindow(new BC_Title(x, y, _("Steps:")));
168         y += ys20;
169         add_subwindow(steps = new ZoomBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
170         add_subwindow(stepsClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_STEPS));
171
172         y += ys30;
173         add_subwindow(r = new ZoomBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
174         y += ys30;
175         add_subwindow(g = new ZoomBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
176         y += ys30;
177         add_subwindow(b = new ZoomBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
178         y += ys30;
179         add_subwindow(a = new ZoomBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
180         y += ys40;
181         add_subwindow(reset = new ZoomBlurReset(plugin, this, x, y));
182         add_subwindow(default_settings = new ZoomBlurDefaultSettings(plugin, this,
183                 (xS(280) - xS(10) - defaultBtn_w), y, defaultBtn_w));
184
185         show_window();
186         flush();
187 }
188
189 // for Reset button
190 void ZoomBlurWindow::update_gui(int clear)
191 {
192         switch(clear) {
193                 case RESET_XSLIDER : this->x->update(plugin->config.x);
194                         break;
195                 case RESET_YSLIDER : this->y->update(plugin->config.y);
196                         break;
197                 case RESET_RADIUS : radius->update(plugin->config.radius);
198                         break;
199                 case RESET_STEPS : steps->update(plugin->config.steps);
200                         break;
201                 case RESET_ALL :
202                 case RESET_DEFAULT_SETTINGS :
203                 default:
204                         this->x->update(plugin->config.x);
205                         this->y->update(plugin->config.x);
206                         radius->update(plugin->config.radius);
207                         steps->update(plugin->config.steps);
208                         r->update(plugin->config.r);
209                         g->update(plugin->config.g);
210                         b->update(plugin->config.b);
211                         a->update(plugin->config.a);
212                         break;
213         }
214 }
215
216
217
218
219
220
221
222
223
224
225 ZoomBlurToggle::ZoomBlurToggle(ZoomBlurMain *plugin,
226         int x,
227         int y,
228         int *output,
229         char *string)
230  : BC_CheckBox(x, y, *output, string)
231 {
232         this->plugin = plugin;
233         this->output = output;
234 }
235
236 int ZoomBlurToggle::handle_event()
237 {
238         *output = get_value();
239         plugin->send_configure_change();
240         return 1;
241 }
242
243
244
245
246
247
248
249 ZoomBlurSize::ZoomBlurSize(ZoomBlurMain *plugin,
250         int x,
251         int y,
252         int *output,
253         int min,
254         int max)
255  : BC_ISlider(x, y, 0, xS(200), yS(200), min, max, *output)
256 {
257         this->plugin = plugin;
258         this->output = output;
259 }
260 int ZoomBlurSize::handle_event()
261 {
262         *output = get_value();
263         plugin->send_configure_change();
264         return 1;
265 }
266
267
268 ZoomBlurReset::ZoomBlurReset(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y)
269  : BC_GenericButton(x, y, _("Reset"))
270 {
271         this->plugin = plugin;
272         this->window = window;
273 }
274 ZoomBlurReset::~ZoomBlurReset()
275 {
276 }
277 int ZoomBlurReset::handle_event()
278 {
279         plugin->config.reset(RESET_ALL);
280         window->update_gui(RESET_ALL);
281         plugin->send_configure_change();
282         return 1;
283 }
284
285
286 ZoomBlurDefaultSettings::ZoomBlurDefaultSettings(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y, int w)
287  : BC_GenericButton(x, y, w, _("Default"))
288 {
289         this->plugin = plugin;
290         this->window = window;
291 }
292 ZoomBlurDefaultSettings::~ZoomBlurDefaultSettings()
293 {
294 }
295 int ZoomBlurDefaultSettings::handle_event()
296 {
297         plugin->config.reset(RESET_DEFAULT_SETTINGS);
298         window->update_gui(RESET_DEFAULT_SETTINGS);
299         plugin->send_configure_change();
300         return 1;
301 }
302
303
304 ZoomBlurSliderClr::ZoomBlurSliderClr(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y, int w, int clear)
305  : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
306 {
307         this->plugin = plugin;
308         this->window = window;
309         this->clear = clear;
310 }
311 ZoomBlurSliderClr::~ZoomBlurSliderClr()
312 {
313 }
314 int ZoomBlurSliderClr::handle_event()
315 {
316         // clear==1 ==> X slider
317         // clear==2 ==> Y slider
318         // clear==3 ==> Radius slider
319         // clear==4 ==> Steps slider
320         plugin->config.reset(clear);
321         window->update_gui(clear);
322         plugin->send_configure_change();
323         return 1;
324 }
325
326
327
328
329 ZoomBlurMain::ZoomBlurMain(PluginServer *server)
330  : PluginVClient(server)
331 {
332
333         engine = 0;
334         scale_x_table = 0;
335         scale_y_table = 0;
336         layer_table = 0;
337         table_entries = 0;
338         accum = 0;
339         need_reconfigure = 1;
340         temp = 0;
341 }
342
343 ZoomBlurMain::~ZoomBlurMain()
344 {
345
346         if(engine) delete engine;
347         delete_tables();
348         if(accum) delete [] accum;
349         if(temp) delete temp;
350 }
351
352 const char* ZoomBlurMain::plugin_title() { return N_("Zoom Blur"); }
353 int ZoomBlurMain::is_realtime() { return 1; }
354
355
356
357 NEW_WINDOW_MACRO(ZoomBlurMain, ZoomBlurWindow)
358
359 LOAD_CONFIGURATION_MACRO(ZoomBlurMain, ZoomBlurConfig)
360
361 void ZoomBlurMain::delete_tables()
362 {
363         if(scale_x_table)
364         {
365                 for(int i = 0; i < table_entries; i++)
366                         delete [] scale_x_table[i];
367                 delete [] scale_x_table;
368         }
369
370         if(scale_y_table)
371         {
372                 for(int i = 0; i < table_entries; i++)
373                         delete [] scale_y_table[i];
374                 delete [] scale_y_table;
375         }
376
377         delete [] layer_table;
378         scale_x_table = 0;
379         scale_y_table = 0;
380         layer_table = 0;
381         table_entries = 0;
382 }
383
384 int ZoomBlurMain::process_buffer(VFrame *frame,
385                 int64_t start_position,
386                 double frame_rate)
387 {
388         need_reconfigure |= load_configuration();
389
390
391 SET_TRACE
392         read_frame(frame,
393                 0,
394                 get_source_position(),
395                 get_framerate(),
396                 get_use_opengl());
397
398 SET_TRACE
399
400 // Generate tables here.  The same table is used by many packages to render
401 // each horizontal stripe.  Need to cover the entire output range in  each
402 // table to avoid green borders
403         if(need_reconfigure)
404         {
405 SET_TRACE
406                 float w = frame->get_w();
407                 float h = frame->get_h();
408                 float center_x = (float)config.x / 100 * w;
409                 float center_y = (float)config.y / 100 * h;
410                 float radius = (float)(100 + config.radius) / 100;
411                 float min_w, min_h;
412                 //float max_w, max_h;
413                 int steps = config.steps ? config.steps : 1;
414                 float min_x1;
415                 float min_y1;
416                 float min_x2;
417                 float min_y2;
418                 float max_x1;
419                 float max_y1;
420                 float max_x2;
421                 float max_y2;
422
423 SET_TRACE
424
425 // printf("ZoomBlurMain::process_realtime 1 %d %d\n",
426 // config.x,
427 // config.y);
428
429                 center_x = (center_x - w / 2) * (1.0 - radius) + w / 2;
430                 center_y = (center_y - h / 2) * (1.0 - radius) + h / 2;
431                 min_w = w * radius;
432                 min_h = h * radius;
433                 //max_w = w;
434                 //max_h = h;
435                 min_x1 = center_x - min_w / 2;
436                 min_y1 = center_y - min_h / 2;
437                 min_x2 = center_x + min_w / 2;
438                 min_y2 = center_y + min_h / 2;
439                 max_x1 = 0;
440                 max_y1 = 0;
441                 max_x2 = w;
442                 max_y2 = h;
443
444 SET_TRACE
445 // printf("ZoomBlurMain::process_realtime 2 w=%f radius=%f center_x=%f\n",
446 // w,
447 // radius,
448 // center_x);
449
450
451 // Dimensions of outermost rectangle
452
453                 delete_tables();
454                 table_entries = steps;
455                 scale_x_table = new int*[steps];
456                 scale_y_table = new int*[steps];
457                 layer_table = new ZoomBlurLayer[table_entries];
458
459 SET_TRACE
460                 for(int i = 0; i < steps; i++)
461                 {
462                         float fraction = (float)i / steps;
463                         float inv_fraction = 1.0 - fraction;
464                         float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
465                         float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
466                         float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
467                         float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
468                         float out_w = out_x2 - out_x1;
469                         float out_h = out_y2 - out_y1;
470                         if(out_w < 0) out_w = 0;
471                         if(out_h < 0) out_h = 0;
472                         float scale_x = (float)w / out_w;
473                         float scale_y = (float)h / out_h;
474
475                         int *x_table;
476                         int *y_table;
477                         scale_y_table[i] = y_table = new int[(int)(h + 1)];
478                         scale_x_table[i] = x_table = new int[(int)(w + 1)];
479 SET_TRACE
480                         layer_table[i].x1 = out_x1;
481                         layer_table[i].y1 = out_y1;
482                         layer_table[i].x2 = out_x2;
483                         layer_table[i].y2 = out_y2;
484 SET_TRACE
485
486                         for(int j = 0; j < h; j++)
487                         {
488                                 y_table[j] = (int)((j - out_y1) * scale_y);
489                         }
490                         for(int j = 0; j < w; j++)
491                         {
492                                 x_table[j] = (int)((j - out_x1) * scale_x);
493 //printf("ZoomBlurMain::process_realtime %d %d\n", j, x_table[j]);
494                         }
495                 }
496 SET_TRACE
497                 need_reconfigure = 0;
498         }
499
500 SET_TRACE
501         if(get_use_opengl()) return run_opengl();
502
503 SET_TRACE
504
505
506
507         if(!engine) engine = new ZoomBlurEngine(this,
508                 get_project_smp() + 1,
509                 get_project_smp() + 1);
510         if(!accum) accum = new unsigned char[frame->get_w() *
511                 frame->get_h() *
512                 BC_CModels::components(frame->get_color_model()) *
513                 MAX(sizeof(int), sizeof(float))];
514
515         this->input = frame;
516         this->output = frame;
517
518
519         if(!temp) temp = new VFrame(frame->get_w(), frame->get_h(),
520                 frame->get_color_model(), 0);
521         temp->copy_from(frame);
522         this->input = temp;
523
524         bzero(accum,
525                 frame->get_w() *
526                 frame->get_h() *
527                 BC_CModels::components(frame->get_color_model()) *
528                 MAX(sizeof(int), sizeof(float)));
529         engine->process_packages();
530         return 0;
531 }
532
533
534 void ZoomBlurMain::update_gui()
535 {
536         if(thread)
537         {
538                 load_configuration();
539                 thread->window->lock_window();
540                 ((ZoomBlurWindow*)thread->window)->x->update(config.x);
541                 ((ZoomBlurWindow*)thread->window)->y->update(config.y);
542                 ((ZoomBlurWindow*)thread->window)->radius->update(config.radius);
543                 ((ZoomBlurWindow*)thread->window)->steps->update(config.steps);
544                 ((ZoomBlurWindow*)thread->window)->r->update(config.r);
545                 ((ZoomBlurWindow*)thread->window)->g->update(config.g);
546                 ((ZoomBlurWindow*)thread->window)->b->update(config.b);
547                 ((ZoomBlurWindow*)thread->window)->a->update(config.a);
548                 thread->window->unlock_window();
549         }
550 }
551
552
553
554
555
556 void ZoomBlurMain::save_data(KeyFrame *keyframe)
557 {
558         FileXML output;
559
560 // cause data to be stored directly in text
561         output.set_shared_output(keyframe->xbuf);
562         output.tag.set_title("ZOOMBLUR");
563
564         output.tag.set_property("X", config.x);
565         output.tag.set_property("Y", config.y);
566         output.tag.set_property("RADIUS", config.radius);
567         output.tag.set_property("STEPS", config.steps);
568         output.tag.set_property("R", config.r);
569         output.tag.set_property("G", config.g);
570         output.tag.set_property("B", config.b);
571         output.tag.set_property("A", config.a);
572         output.append_tag();
573         output.tag.set_title("/ZOOMBLUR");
574         output.append_tag();
575         output.append_newline();
576         output.terminate_string();
577 }
578
579 void ZoomBlurMain::read_data(KeyFrame *keyframe)
580 {
581         FileXML input;
582
583         input.set_shared_input(keyframe->xbuf);
584
585         int result = 0;
586
587         while(!result)
588         {
589                 result = input.read_tag();
590
591                 if(!result)
592                 {
593                         if(input.tag.title_is("ZOOMBLUR"))
594                         {
595                                 config.x = input.tag.get_property("X", config.x);
596                                 config.y = input.tag.get_property("Y", config.y);
597                                 config.radius = input.tag.get_property("RADIUS", config.radius);
598                                 config.steps = input.tag.get_property("STEPS", config.steps);
599                                 config.r = input.tag.get_property("R", config.r);
600                                 config.g = input.tag.get_property("G", config.g);
601                                 config.b = input.tag.get_property("B", config.b);
602                                 config.a = input.tag.get_property("A", config.a);
603                         }
604                 }
605         }
606 }
607
608 #ifdef HAVE_GL
609 static void draw_box(float x1, float y1, float x2, float y2)
610 {
611         glBegin(GL_QUADS);
612         glVertex3f(x1, y1, 0.0);
613         glVertex3f(x2, y1, 0.0);
614         glVertex3f(x2, y2, 0.0);
615         glVertex3f(x1, y2, 0.0);
616         glEnd();
617 }
618 #endif
619
620 int ZoomBlurMain::handle_opengl()
621 {
622 #ifdef HAVE_GL
623         get_output()->to_texture();
624         get_output()->enable_opengl();
625         get_output()->init_screen();
626         get_output()->bind_texture(0);
627
628         //int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
629         glClearColor(0.0, 0.0, 0.0, 0.0);
630         glClear(GL_COLOR_BUFFER_BIT);
631
632 // Draw unselected channels
633         glEnable(GL_BLEND);
634         glBlendFunc(GL_ONE, GL_ONE);
635         glDrawBuffer(GL_BACK);
636
637         if(!config.r || !config.g || !config.b || !config.a)
638         {
639                 glColor4f(config.r ? 0 : 1,
640                         config.g ? 0 : 1,
641                         config.b ? 0 : 1,
642                         config.a ? 0 : 1);
643                 get_output()->draw_texture();
644         }
645         glAccum(GL_LOAD, 1.0);
646
647 // Blur selected channels
648         float fraction = 1.0 / config.steps;
649         for(int i = 0; i < config.steps; i++)
650         {
651                 glClear(GL_COLOR_BUFFER_BIT);
652                 glColor4f(config.r ? 1 : 0,
653                         config.g ? 1 : 0,
654                         config.b ? 1 : 0,
655                         config.a ? 1 : 0);
656
657                 get_output()->draw_texture(0,
658                         0,
659                         get_output()->get_w(),
660                         get_output()->get_h(),
661                         layer_table[i].x1,
662                         get_output()->get_h() - layer_table[i].y1,
663                         layer_table[i].x2,
664                         get_output()->get_h() - layer_table[i].y2,
665                         1);
666
667 // Fill YUV black
668                 glDisable(GL_TEXTURE_2D);
669                 if(BC_CModels::is_yuv(get_output()->get_color_model()))
670                 {
671                         glColor4f(config.r ? 0.0 : 0,
672                                 config.g ? 0.5 : 0,
673                                 config.b ? 0.5 : 0,
674                                 config.a ? 1.0 : 0);
675                         float center_x1 = 0.0;
676                         float center_x2 = get_output()->get_w();
677                         if(layer_table[i].x1 > 0)
678                         {
679                                 center_x1 = layer_table[i].x1;
680                                 draw_box(0, 0, layer_table[i].x1, -get_output()->get_h());
681                         }
682                         if(layer_table[i].x2 < get_output()->get_w())
683                         {
684                                 center_x2 = layer_table[i].x2;
685                                 draw_box(layer_table[i].x2, 0, get_output()->get_w(), -get_output()->get_h());
686                         }
687                         if(layer_table[i].y1 > 0)
688                         {
689                                 draw_box(center_x1,
690                                         -get_output()->get_h(),
691                                         center_x2,
692                                         -get_output()->get_h() + layer_table[i].y1);
693                         }
694                         if(layer_table[i].y2 < get_output()->get_h())
695                         {
696                                 draw_box(center_x1,
697                                         -get_output()->get_h() + layer_table[i].y2,
698                                         center_x2,
699                                         0);
700                         }
701                 }
702
703
704                 glAccum(GL_ACCUM, fraction);
705                 glEnable(GL_TEXTURE_2D);
706                 glColor4f(config.r ? 1 : 0,
707                         config.g ? 1 : 0,
708                         config.b ? 1 : 0,
709                         config.a ? 1 : 0);
710         }
711
712         glDisable(GL_BLEND);
713         glReadBuffer(GL_BACK);
714         glDisable(GL_TEXTURE_2D);
715         glAccum(GL_RETURN, 1.0);
716
717         glColor4f(1, 1, 1, 1);
718         get_output()->set_opengl_state(VFrame::SCREEN);
719 #endif
720         return 0;
721 }
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736 ZoomBlurPackage::ZoomBlurPackage()
737  : LoadPackage()
738 {
739 }
740
741
742
743
744 ZoomBlurUnit::ZoomBlurUnit(ZoomBlurEngine *server,
745         ZoomBlurMain *plugin)
746  : LoadClient(server)
747 {
748         this->plugin = plugin;
749         this->server = server;
750 }
751
752
753 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
754 { \
755         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
756         for(int j = pkg->y1; j < pkg->y2; j++) \
757         { \
758                 TEMP_TYPE *out_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
759                 int in_y = y_table[j]; \
760  \
761 /* Blend image */ \
762                 if(in_y >= 0 && in_y < h) \
763                 { \
764                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
765                         for(int k = 0; k < w; k++) \
766                         { \
767                                 int in_x = x_table[k]; \
768 /* Blend pixel */ \
769                                 if(in_x >= 0 && in_x < w) \
770                                 { \
771                                         int in_offset = in_x * COMPONENTS; \
772                                         *out_row++ += in_row[in_offset]; \
773                                         if(DO_YUV) \
774                                         { \
775                                                 *out_row++ += in_row[in_offset + 1]; \
776                                                 *out_row++ += in_row[in_offset + 2]; \
777                                         } \
778                                         else \
779                                         { \
780                                                 *out_row++ += (TEMP_TYPE)in_row[in_offset + 1]; \
781                                                 *out_row++ += (TEMP_TYPE)in_row[in_offset + 2]; \
782                                         } \
783                                         if(COMPONENTS == 4) \
784                                                 *out_row++ += in_row[in_offset + 3]; \
785                                 } \
786 /* Blend nothing */ \
787                                 else \
788                                 { \
789                                         out_row++; \
790                                         if(DO_YUV) \
791                                         { \
792                                                 *out_row++ += chroma_offset; \
793                                                 *out_row++ += chroma_offset; \
794                                         } \
795                                         else \
796                                         { \
797                                                 out_row += 2; \
798                                         } \
799                                         if(COMPONENTS == 4) out_row++; \
800                                 } \
801                         } \
802                 } \
803                 else \
804                 if(DO_YUV) \
805                 { \
806                         for(int k = 0; k < w; k++) \
807                         { \
808                                 out_row++; \
809                                 *out_row++ += chroma_offset; \
810                                 *out_row++ += chroma_offset; \
811                                 if(COMPONENTS == 4) out_row++; \
812                         } \
813                 } \
814         } \
815  \
816 /* Copy just selected blurred channels to output and combine with original \
817         unblurred channels */ \
818         if(i == plugin->config.steps - 1) \
819         { \
820                 for(int j = pkg->y1; j < pkg->y2; j++) \
821                 { \
822                         TEMP_TYPE *in_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
823                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
824                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
825                         for(int k = 0; k < w; k++) \
826                         { \
827                                 if(do_r) \
828                                 { \
829                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
830                                         in_backup++; \
831                                 } \
832                                 else \
833                                 { \
834                                         *out_row++ = *in_backup++; \
835                                         in_row++; \
836                                 } \
837  \
838                                 if(DO_YUV) \
839                                 { \
840                                         if(do_g) \
841                                         { \
842                                                 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
843                                                 in_backup++; \
844                                         } \
845                                         else \
846                                         { \
847                                                 *out_row++ = *in_backup++; \
848                                                 in_row++; \
849                                         } \
850  \
851                                         if(do_b) \
852                                         { \
853                                                 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
854                                                 in_backup++; \
855                                         } \
856                                         else \
857                                         { \
858                                                 *out_row++ = *in_backup++; \
859                                                 in_row++; \
860                                         } \
861                                 } \
862                                 else \
863                                 { \
864                                         if(do_g) \
865                                         { \
866                                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
867                                                 in_backup++; \
868                                         } \
869                                         else \
870                                         { \
871                                                 *out_row++ = *in_backup++; \
872                                                 in_row++; \
873                                         } \
874  \
875                                         if(do_b) \
876                                         { \
877                                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
878                                                 in_backup++; \
879                                         } \
880                                         else \
881                                         { \
882                                                 *out_row++ = *in_backup++; \
883                                                 in_row++; \
884                                         } \
885                                 } \
886  \
887                                 if(COMPONENTS == 4) \
888                                 { \
889                                         if(do_a) \
890                                         { \
891                                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
892                                                 in_backup++; \
893                                         } \
894                                         else \
895                                         { \
896                                                 *out_row++ = *in_backup++; \
897                                                 in_row++; \
898                                         } \
899                                 } \
900                         } \
901                 } \
902         } \
903 }
904
905 void ZoomBlurUnit::process_package(LoadPackage *package)
906 {
907         ZoomBlurPackage *pkg = (ZoomBlurPackage*)package;
908         int h = plugin->output->get_h();
909         int w = plugin->output->get_w();
910         int do_r = plugin->config.r;
911         int do_g = plugin->config.g;
912         int do_b = plugin->config.b;
913         int do_a = plugin->config.a;
914
915         int fraction = 0x10000 / plugin->config.steps;
916         for(int i = 0; i < plugin->config.steps; i++)
917         {
918                 int *x_table = plugin->scale_x_table[i];
919                 int *y_table = plugin->scale_y_table[i];
920
921                 switch(plugin->input->get_color_model())
922                 {
923                         case BC_RGB888:
924                                 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
925                                 break;
926                         case BC_RGB_FLOAT:
927                                 BLEND_LAYER(3, float, float, 1, 0)
928                                 break;
929                         case BC_RGBA_FLOAT:
930                                 BLEND_LAYER(4, float, float, 1, 0)
931                                 break;
932                         case BC_RGBA8888:
933                                 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
934                                 break;
935                         case BC_RGB161616:
936                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
937                                 break;
938                         case BC_RGBA16161616:
939                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
940                                 break;
941                         case BC_YUV888:
942                                 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
943                                 break;
944                         case BC_YUVA8888:
945                                 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
946                                 break;
947                         case BC_YUV161616:
948                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
949                                 break;
950                         case BC_YUVA16161616:
951                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
952                                 break;
953                 }
954         }
955 }
956
957
958
959
960
961
962 ZoomBlurEngine::ZoomBlurEngine(ZoomBlurMain *plugin,
963         int total_clients,
964         int total_packages)
965  : LoadServer(total_clients, total_packages)
966 {
967         this->plugin = plugin;
968 }
969
970 void ZoomBlurEngine::init_packages()
971 {
972         for(int i = 0; i < get_total_packages(); i++)
973         {
974                 ZoomBlurPackage *package = (ZoomBlurPackage*)get_package(i);
975                 package->y1 = plugin->output->get_h() * i / get_total_packages();
976                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
977         }
978 }
979
980 LoadClient* ZoomBlurEngine::new_client()
981 {
982         return new ZoomBlurUnit(this, plugin);
983 }
984
985 LoadPackage* ZoomBlurEngine::new_package()
986 {
987         return new ZoomBlurPackage;
988 }
989
990
991
992
993