add BC_SCALE env var for hi def monitors, cleanup theme data
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / oilpainting / oil.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 "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "filexml.h"
26 #include "guicast.h"
27 #include "keyframe.h"
28 #include "language.h"
29 #include "loadbalance.h"
30 #include "pluginvclient.h"
31 #include "vframe.h"
32
33 #include <math.h>
34 #include <stdint.h>
35 #include <string.h>
36
37
38
39
40 // Algorithm by Torsten Martinsen
41 // Ported to Cinelerra by Heroine Virtual Ltd.
42
43
44
45
46 class OilEffect;
47 class OilWindow;
48 class OilReset;
49
50
51 class OilConfig
52 {
53 public:
54         OilConfig();
55         void reset();
56
57         void copy_from(OilConfig &src);
58         int equivalent(OilConfig &src);
59         void interpolate(OilConfig &prev,
60                 OilConfig &next,
61                 long prev_frame,
62                 long next_frame,
63                 long current_frame);
64         float radius;
65         int use_intensity;
66 };
67
68 class OilRadius : public BC_FSlider
69 {
70 public:
71         OilRadius(OilEffect *plugin, int x, int y);
72         int handle_event();
73         OilEffect *plugin;
74 };
75
76
77 class OilIntensity : public BC_CheckBox
78 {
79 public:
80         OilIntensity(OilEffect *plugin, int x, int y);
81         int handle_event();
82         OilEffect *plugin;
83 };
84
85 class OilReset : public BC_GenericButton
86 {
87 public:
88         OilReset(OilEffect *plugin, OilWindow *window, int x, int y);
89         ~OilReset();
90         int handle_event();
91         OilEffect *plugin;
92         OilWindow *window;
93 };
94
95
96 class OilWindow : public PluginClientWindow
97 {
98 public:
99         OilWindow(OilEffect *plugin);
100         ~OilWindow();
101         void create_objects();
102         void update();
103         OilEffect *plugin;
104         OilRadius *radius;
105         OilIntensity *intensity;
106         OilReset *reset;
107 };
108
109
110
111
112
113
114
115 class OilServer : public LoadServer
116 {
117 public:
118         OilServer(OilEffect *plugin, int cpus);
119         void init_packages();
120         LoadClient* new_client();
121         LoadPackage* new_package();
122         OilEffect *plugin;
123 };
124
125 class OilPackage : public LoadPackage
126 {
127 public:
128         OilPackage();
129         int row1, row2;
130 };
131
132 class OilUnit : public LoadClient
133 {
134 public:
135         OilUnit(OilEffect *plugin, OilServer *server);
136         void process_package(LoadPackage *package);
137         OilEffect *plugin;
138 };
139
140
141
142
143
144
145
146
147
148 class OilEffect : public PluginVClient
149 {
150 public:
151         OilEffect(PluginServer *server);
152         ~OilEffect();
153
154         PLUGIN_CLASS_MEMBERS(OilConfig);
155         int process_realtime(VFrame *input, VFrame *output);
156         int is_realtime();
157         void save_data(KeyFrame *keyframe);
158         void read_data(KeyFrame *keyframe);
159         void update_gui();
160
161         VFrame *temp_frame;
162         VFrame *input, *output;
163         OilServer *engine;
164         int need_reconfigure;
165 };
166
167
168
169
170
171
172
173
174
175
176
177 OilConfig::OilConfig()
178 {
179         reset();
180 }
181
182 void OilConfig::reset()
183 {
184         radius = 5;
185         use_intensity = 0;
186 }
187
188 void OilConfig::copy_from(OilConfig &src)
189 {
190         this->radius = src.radius;
191         this->use_intensity = src.use_intensity;
192 }
193
194 int OilConfig::equivalent(OilConfig &src)
195 {
196         return (EQUIV(this->radius, src.radius) &&
197                 this->use_intensity == src.use_intensity);
198 }
199
200 void OilConfig::interpolate(OilConfig &prev,
201                 OilConfig &next,
202                 long prev_frame,
203                 long next_frame,
204                 long current_frame)
205 {
206         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
207         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
208         this->radius = prev.radius * prev_scale + next.radius * next_scale;
209         this->use_intensity = prev.use_intensity;
210 // printf("OilConfig::interpolate prev_frame=%ld current_frame=%ld next_frame=%ld prev.radius=%f this->radius=%f next.radius=%f\n",
211 //      prev_frame, current_frame, next_frame, prev.radius, this->radius, next.radius);
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225 OilRadius::OilRadius(OilEffect *plugin, int x, int y)
226  : BC_FSlider(x,
227                 y,
228                 0,
229                 xS(200),
230                 yS(200),
231                 (float)0,
232                 (float)30,
233                 plugin->config.radius)
234 {
235         this->plugin = plugin;
236 }
237 int OilRadius::handle_event()
238 {
239         plugin->config.radius = get_value();
240         plugin->send_configure_change();
241         return 1;
242 }
243
244
245
246
247
248
249
250 OilIntensity::OilIntensity(OilEffect *plugin, int x, int y)
251  : BC_CheckBox(x, y, plugin->config.use_intensity, _("Use intensity"))
252 {
253         this->plugin = plugin;
254 }
255 int OilIntensity::handle_event()
256 {
257         plugin->config.use_intensity = get_value();
258         plugin->send_configure_change();
259         return 1;
260 }
261
262
263 OilReset::OilReset(OilEffect *plugin, OilWindow *window, int x, int y)
264  : BC_GenericButton(x, y, _("Reset"))
265 {
266         this->plugin = plugin;
267         this->window = window;
268 }
269 OilReset::~OilReset()
270 {
271 }
272 int OilReset::handle_event()
273 {
274         plugin->config.reset();
275         window->update();
276         plugin->send_configure_change();
277         return 1;
278 }
279
280
281
282
283
284 OilWindow::OilWindow(OilEffect *plugin)
285  : PluginClientWindow(plugin,
286         xS(300),
287         yS(120),
288         xS(300),
289         yS(120),
290         0)
291 {
292         this->plugin = plugin;
293 }
294
295 OilWindow::~OilWindow()
296 {
297 }
298
299 void OilWindow::create_objects()
300 {
301         int xs10 = xS(10), xs70 = xS(70);
302         int ys10 = yS(10), ys40 = yS(40);
303         int x = xs10, y = ys10;
304         add_subwindow(new BC_Title(x, y, _("Radius:")));
305         add_subwindow(radius = new OilRadius(plugin, x + xs70, y));
306         y += ys40;
307         add_subwindow(intensity = new OilIntensity(plugin, x, y));
308         y += ys40;
309         add_subwindow(reset = new OilReset(plugin, this, x, y));
310
311         show_window();
312         flush();
313 }
314
315
316 // for Reset button
317 void OilWindow::update()
318 {
319         radius->update(plugin->config.radius);
320         intensity->update(plugin->config.use_intensity);
321 }
322
323
324
325
326
327
328
329 REGISTER_PLUGIN(OilEffect)
330
331
332
333
334 OilEffect::OilEffect(PluginServer *server)
335  : PluginVClient(server)
336 {
337         temp_frame = 0;
338         need_reconfigure = 1;
339         engine = 0;
340
341 }
342
343 OilEffect::~OilEffect()
344 {
345
346
347         if(temp_frame) delete temp_frame;
348         if(engine) delete engine;
349 }
350
351
352 const char* OilEffect::plugin_title() { return N_("Oil painting"); }
353 int OilEffect::is_realtime() { return 1; }
354
355
356
357 NEW_WINDOW_MACRO(OilEffect, OilWindow)
358
359 void OilEffect::update_gui()
360 {
361         if(thread)
362         {
363                 thread->window->lock_window();
364                 load_configuration();
365 //printf("OilEffect::update_gui 1 %ld %f\n", get_source_position(), config.radius);
366
367                 ((OilWindow*)thread->window)->radius->update(config.radius);
368                 ((OilWindow*)thread->window)->intensity->update(config.use_intensity);
369                 thread->window->unlock_window();
370         }
371 }
372
373
374 LOAD_CONFIGURATION_MACRO(OilEffect, OilConfig)
375
376
377 void OilEffect::save_data(KeyFrame *keyframe)
378 {
379         FileXML output;
380
381 // cause data to be stored directly in text
382         output.set_shared_output(keyframe->xbuf);
383         output.tag.set_title("OIL_PAINTING");
384         output.tag.set_property("RADIUS", config.radius);
385         output.tag.set_property("USE_INTENSITY", config.use_intensity);
386         output.append_tag();
387         output.tag.set_title("/OIL_PAINTING");
388         output.append_tag();
389         output.append_newline();
390         output.terminate_string();
391 }
392
393 void OilEffect::read_data(KeyFrame *keyframe)
394 {
395         FileXML input;
396
397         input.set_shared_input(keyframe->xbuf);
398
399         while(!input.read_tag())
400         {
401                 if(input.tag.title_is("OIL_PAINTING"))
402                 {
403                         config.radius = input.tag.get_property("RADIUS", config.radius);
404                         config.use_intensity = input.tag.get_property("USE_INTENSITY", config.use_intensity);
405                 }
406         }
407 }
408
409
410 int OilEffect::process_realtime(VFrame *input, VFrame *output)
411 {
412         need_reconfigure |= load_configuration();
413
414
415
416 //printf("OilEffect::process_realtime %f %d\n", config.radius, config.use_intensity);
417         this->input = input;
418         this->output = output;
419
420         if(EQUIV(config.radius, 0))
421         {
422                 if(input->get_rows()[0] != output->get_rows()[0])
423                         output->copy_from(input);
424         }
425         else
426         {
427                 if(input->get_rows()[0] == output->get_rows()[0])
428                 {
429                         if(!temp_frame)
430                                 temp_frame = new VFrame(input->get_w(), input->get_h(),
431                                         input->get_color_model(), 0);
432                         temp_frame->copy_from(input);
433                         this->input = temp_frame;
434                 }
435
436
437                 if(!engine)
438                 {
439                         engine = new OilServer(this, (PluginClient::smp + 1));
440                 }
441
442                 engine->process_packages();
443         }
444
445
446
447         return 0;
448 }
449
450
451
452
453
454
455 OilPackage::OilPackage()
456  : LoadPackage()
457 {
458 }
459
460
461
462
463
464
465 OilUnit::OilUnit(OilEffect *plugin, OilServer *server)
466  : LoadClient(server)
467 {
468         this->plugin = plugin;
469 }
470
471
472 #define INTENSITY(p) ((unsigned int)(((p)[0]) * 77+ \
473                                                                         ((p)[1] * 150) + \
474                                                                         ((p)[2] * 29)) >> 8)
475
476
477 #define OIL_MACRO(type, hist_size, components) \
478 { \
479         type *src, *dest; \
480         type val[components]; \
481         int count[components], count2; \
482         int *hist[components]; \
483         int *hist2; \
484         type **src_rows = (type**)plugin->input->get_rows(); \
485  \
486         for(int i = 0; i < components; i++) \
487                 hist[i] = new int[hist_size + 1]; \
488         hist2 = new int[hist_size + 1]; \
489  \
490         for(int y1 = pkg->row1; y1 < pkg->row2; y1++) \
491         { \
492                 dest = (type*)plugin->output->get_rows()[y1]; \
493  \
494                 if(!plugin->config.use_intensity) \
495                 { \
496                         for(int x1 = 0; x1 < w; x1++) \
497                         { \
498                                 bzero(count, sizeof(count)); \
499                                 bzero(val, sizeof(val)); \
500                                 bzero(hist[0], sizeof(int) * (hist_size + 1)); \
501                                 bzero(hist[1], sizeof(int) * (hist_size + 1)); \
502                                 bzero(hist[2], sizeof(int) * (hist_size + 1)); \
503                                 if (components == 4) bzero(hist[3], sizeof(int) * (hist_size + 1)); \
504  \
505                                 int x3 = CLIP((x1 - n), 0, w - 1); \
506                                 int y3 = CLIP((y1 - n), 0, h - 1); \
507                                 int x4 = CLIP((x1 + n + 1), 0, w - 1); \
508                                 int y4 = CLIP((y1 + n + 1), 0, h - 1); \
509  \
510                                 for(int y2 = y3; y2 < y4; y2++) \
511                                 { \
512                                         src = src_rows[y2]; \
513                                         for(int x2 = x3; x2 < x4; x2++) \
514                                         { \
515                                                 int c; \
516                                                 int subscript; \
517                                                 type value; \
518  \
519                         value = src[x2 * components + 0]; \
520                                                 if(sizeof(type) == 4) \
521                                                 { \
522                                                         subscript = (int)(value * hist_size); \
523                                                         CLAMP(subscript, 0, hist_size); \
524                                                 } \
525                                                 else \
526                                                         subscript = (int)value; \
527  \
528                                                 if((c = ++hist[0][subscript]) > count[0]) \
529                                                 { \
530                                                         val[0] = value; \
531                                                         count[0] = c; \
532                                                 } \
533  \
534                         value = src[x2 * components + 1]; \
535                                                 if(sizeof(type) == 4) \
536                                                 { \
537                                                         subscript = (int)(value * hist_size); \
538                                                         CLAMP(subscript, 0, hist_size); \
539                                                 } \
540                                                 else \
541                                                         subscript = (int)value; \
542  \
543                                                 if((c = ++hist[1][subscript]) > count[1]) \
544                                                 { \
545                                                         val[1] = value; \
546                                                         count[1] = c; \
547                                                 } \
548  \
549                         value = src[x2 * components + 2]; \
550                                                 if(sizeof(type) == 4) \
551                                                 { \
552                                                         subscript = (int)(value * hist_size); \
553                                                         CLAMP(subscript, 0, hist_size); \
554                                                 } \
555                                                 else \
556                                                         subscript = (int)value; \
557  \
558                                                 if((c = ++hist[2][subscript]) > count[2]) \
559                                                 { \
560                                                         val[2] = value; \
561                                                         count[2] = c; \
562                                                 } \
563  \
564                                                 if(components == 4) \
565                                                 { \
566                                 value = src[x2 * components + 3]; \
567                                                         if(sizeof(type) == 4) \
568                                                         { \
569                                                                 subscript = (int)(value * hist_size); \
570                                                                 CLAMP(subscript, 0, hist_size); \
571                                                         } \
572                                                         else \
573                                                                 subscript = (int)value; \
574  \
575                                                         if((c = ++hist[3][subscript]) > count[3]) \
576                                                         { \
577                                                                 val[3] = value; \
578                                                                 count[3] = c; \
579                                                         } \
580                                                 } \
581                                         } \
582                                 } \
583  \
584                                 dest[x1 * components + 0] = val[0]; \
585                                 dest[x1 * components + 1] = val[1]; \
586                                 dest[x1 * components + 2] = val[2]; \
587                                 if(components == 4) dest[x1 * components + 3] = val[3]; \
588                         } \
589                 } \
590                 else \
591                 { \
592                         for(int x1 = 0; x1 < w; x1++) \
593                         { \
594                                 count2 = 0; \
595                                 bzero(val, sizeof(val)); \
596                                 bzero(hist2, sizeof(int) * (hist_size + 1)); \
597  \
598                                 int x3 = CLIP((x1 - n), 0, w - 1); \
599                         int y3 = CLIP((y1 - n), 0, h - 1); \
600                         int x4 = CLIP((x1 + n + 1), 0, w - 1); \
601                         int y4 = CLIP((y1 + n + 1), 0, h - 1); \
602  \
603                                 for(int y2 = y3; y2 < y4; y2++) \
604                                 { \
605                                         src = src_rows[y2]; \
606                                         for(int x2 = x3; x2 < x4; x2++) \
607                                         { \
608                                                 int c; \
609                                                 if((c = ++hist2[INTENSITY(src + x2 * components)]) > count2) \
610                                                 { \
611                                                         val[0] = src[x2 * components + 0]; \
612                                                         val[1] = src[x2 * components + 1]; \
613                                                         val[2] = src[x2 * components + 2]; \
614                                                         if(components == 4) val[3] = src[x2 * components + 3]; \
615                                                         count2 = c; \
616                                                 } \
617                                         } \
618                                 } \
619  \
620                                 dest[x1 * components + 0] = val[0]; \
621                                 dest[x1 * components + 1] = val[1]; \
622                                 dest[x1 * components + 2] = val[2]; \
623                                 if(components == 4) dest[x1 * components + 3] = val[3]; \
624                         } \
625                 } \
626         } \
627  \
628         for(int i = 0; i < components; i++) \
629                 delete [] hist[i]; \
630         delete [] hist2; \
631 }
632
633
634
635
636 void OilUnit::process_package(LoadPackage *package)
637 {
638         OilPackage *pkg = (OilPackage*)package;
639         int w = plugin->input->get_w();
640         int h = plugin->input->get_h();
641         int n = (int)(plugin->config.radius / 2);
642
643         switch(plugin->input->get_color_model())
644         {
645                 case BC_RGB_FLOAT:
646                         OIL_MACRO(float, 0xffff, 3)
647                         break;
648                 case BC_RGB888:
649                 case BC_YUV888:
650                         OIL_MACRO(unsigned char, 0xff, 3)
651                         break;
652                 case BC_RGB161616:
653                 case BC_YUV161616:
654                         OIL_MACRO(uint16_t, 0xffff, 3)
655                         break;
656                 case BC_RGBA_FLOAT:
657                         OIL_MACRO(float, 0xffff, 4)
658                         break;
659                 case BC_RGBA8888:
660                 case BC_YUVA8888:
661                         OIL_MACRO(unsigned char, 0xff, 4)
662                         break;
663                 case BC_RGBA16161616:
664                 case BC_YUVA16161616:
665                         OIL_MACRO(uint16_t, 0xffff, 4)
666                         break;
667         }
668
669
670
671
672 }
673
674
675
676
677
678
679 OilServer::OilServer(OilEffect *plugin, int cpus)
680  : LoadServer(cpus, cpus)
681 {
682         this->plugin = plugin;
683 }
684
685 void OilServer::init_packages()
686 {
687         for(int i = 0; i < LoadServer::get_total_packages(); i++)
688         {
689                 OilPackage *pkg = (OilPackage*)get_package(i);
690                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
691                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
692         }
693 }
694
695
696 LoadClient* OilServer::new_client()
697 {
698         return new OilUnit(plugin, this);
699 }
700
701 LoadPackage* OilServer::new_package()
702 {
703         return new OilPackage;
704 }
705