5e5d4126e82f22b8cbb562e42878f52223c21fef
[goodguy/history.git] / cinelerra-5.1 / plugins / color3way / color3way.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2011 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
24 #include "filexml.h"
25 #include "color3way.h"
26 #include "bchash.h"
27 #include "language.h"
28 #include "playback3d.h"
29
30 #include "aggregated.h"
31 #include "../interpolate/aggregated.h"
32 #include "../gamma/aggregated.h"
33
34 #include <stdio.h>
35 #include <string.h>
36
37
38 REGISTER_PLUGIN(Color3WayMain)
39
40
41 Color3WayConfig::Color3WayConfig()
42 {
43         for(int i = 0; i < SECTIONS; i++)
44         {
45                 hue_x[i] = 0.0;
46                 hue_y[i] = 0.0;
47                 value[i] = 0.0;
48                 saturation[i] = 0.0;
49         }
50 }
51
52 int Color3WayConfig::equivalent(Color3WayConfig &that)
53 {
54         for(int i = 0; i < SECTIONS; i++)
55         {
56                 if(!EQUIV(hue_x[i], that.hue_x[i]) ||
57                         !EQUIV(hue_y[i], that.hue_y[i]) ||
58                         !EQUIV(value[i], that.value[i]) ||
59                         !EQUIV(saturation[i], that.saturation[i])) return 0;
60         }
61         return 1;
62 }
63
64 void Color3WayConfig::copy_from(Color3WayConfig &that)
65 {
66         for(int i = 0; i < SECTIONS; i++)
67         {
68                 hue_x[i] = that.hue_x[i];
69                 hue_y[i] = that.hue_y[i];
70                 value[i] = that.value[i];
71                 saturation[i] = that.saturation[i];
72         }
73 }
74
75 void Color3WayConfig::interpolate(Color3WayConfig &prev,
76         Color3WayConfig &next,
77         int64_t prev_frame,
78         int64_t next_frame,
79         int64_t current_frame)
80 {
81         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
82         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
83
84         for(int i = 0; i < SECTIONS; i++)
85         {
86                 hue_x[i] = prev.hue_x[i] * prev_scale + next.hue_x[i] * next_scale;
87                 hue_y[i] = prev.hue_y[i] * prev_scale + next.hue_y[i] * next_scale;
88                 value[i] = prev.value[i] * prev_scale + next.value[i] * next_scale;
89                 saturation[i] = prev.saturation[i] * prev_scale + next.saturation[i] * next_scale;
90         }
91 }
92
93
94 void Color3WayConfig::boundaries()
95 {
96         for(int i = 0; i < SECTIONS; i++)
97         {
98                 float point_radius = sqrt(SQR(hue_x[i]) + SQR(hue_y[i]));
99                 if(point_radius > 1)
100                 {
101                         float angle = atan2(hue_x[i],
102                                                 hue_y[i]);
103                         hue_x[i] = sin(angle);
104                         hue_y[i] = cos(angle);
105                 }
106         }
107 }
108
109 void Color3WayConfig::copy_to_all(int section)
110 {
111         for(int i = 0; i < SECTIONS; i++)
112         {
113                 hue_x[i] = hue_x[section];
114                 hue_y[i] = hue_y[section];
115                 value[i] = value[section];
116                 saturation[i] = saturation[section];
117         }
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 Color3WayPackage::Color3WayPackage()
135  : LoadPackage()
136 {
137 }
138
139
140
141
142
143
144 Color3WayUnit::Color3WayUnit(Color3WayMain *plugin,
145         Color3WayEngine *server)
146  : LoadClient(server)
147 {
148         this->plugin = plugin;
149 }
150
151 // Lower = sharper curve
152 #define SHADOW_GAMMA 32.0
153 #define HIGHLIGHT_GAMMA 32.0
154 // Keep value == 0 from blowing up
155 #define FUDGE (1.0 / 256.0)
156 // Scale curve from 0 - 1
157 #define SHADOW_BORDER (1.0 / ((1.0 / SHADOW_GAMMA + FUDGE) / FUDGE))
158 #define HIGHLIGHT_BORDER (1.0 / ((1.0 / HIGHLIGHT_GAMMA + FUDGE) / FUDGE))
159
160 #define SHADOW_CURVE(value) \
161         (((1.0 / (((value) / SHADOW_GAMMA + FUDGE) / FUDGE)) - SHADOW_BORDER) / (1.0 - SHADOW_BORDER))
162
163 #define SHADOW_LINEAR(value) (1.0 - (value))
164
165 #define HIGHLIGHT_CURVE(value) \
166         (((1.0 / (((1.0 - value) / HIGHLIGHT_GAMMA + FUDGE) / FUDGE)) - HIGHLIGHT_BORDER) / (1.0 - HIGHLIGHT_BORDER))
167
168 #define HIGHLIGHT_LINEAR(value) \
169         (value)
170
171 #define MIDTONE_CURVE(value, factor) \
172         ((factor) <= 0 ? \
173         (1.0 - SHADOW_LINEAR(value) - HIGHLIGHT_CURVE(value)) : \
174         (1.0 - SHADOW_CURVE(value) - HIGHLIGHT_LINEAR(value)))
175
176 #define TOTAL_TRANSFER(value, factor) \
177         (factor[SHADOWS] * SHADOW_LINEAR(value) + \
178         factor[MIDTONES] * MIDTONE_CURVE(value, factor[MIDTONES]) + \
179         factor[HIGHLIGHTS] * HIGHLIGHT_LINEAR(value))
180
181 #define PROCESS_PIXEL(r, g, b) \
182 /* Apply hue */ \
183         r = r + TOTAL_TRANSFER(r, r_factor); \
184         g = g + TOTAL_TRANSFER(g, g_factor); \
185         b = b + TOTAL_TRANSFER(b, b_factor); \
186 /* Apply saturation/value */ \
187         float h, s, v; \
188         HSV::rgb_to_hsv(r, g, b, h, s, v); \
189         v += TOTAL_TRANSFER(v, v_factor); \
190         s += TOTAL_TRANSFER(s, s_factor); \
191         s = MAX(s, 0); \
192         HSV::hsv_to_rgb(r, g, b, h, s, v);
193
194
195 #define PROCESS(type, max, components, is_yuv) \
196 { \
197         type *in = (type*)plugin->get_input()->get_rows()[i]; \
198         type *out = (type*)plugin->get_input()->get_rows()[i]; \
199         for(int j = 0; j < w; j++) \
200         { \
201 /* Convert to RGB float */ \
202                 float r; \
203                 float g; \
204                 float b; \
205                 if(is_yuv) \
206                 { \
207                         YUV::yuv_to_rgb_f(r, \
208                                 g, \
209                                 b, \
210                                 (float)in[0] / max, \
211                                 (float)in[1] / max - 0.5, \
212                                 (float)in[2] / max - 0.5); \
213                         in += 3; \
214                 } \
215                 else \
216                 { \
217                         r = (float)*in++ / max; \
218                         g = (float)*in++ / max; \
219                         b = (float)*in++ / max; \
220                 } \
221  \
222                 PROCESS_PIXEL(r, g, b) \
223  \
224 /* Convert to project colormodel */ \
225                 if(is_yuv) \
226                 { \
227                         float y, u, v; \
228                         YUV::rgb_to_yuv_f(r, g, b, y, u, v); \
229                         r = y; \
230                         g = u + 0.5; \
231                         b = v + 0.5; \
232                 } \
233                 if(max == 0xff) \
234                 { \
235                         CLAMP(r, 0, 1); \
236                         CLAMP(g, 0, 1); \
237                         CLAMP(b, 0, 1); \
238                 } \
239                 *out++ = (type)(r * max); \
240                 *out++ = (type)(g * max); \
241                 *out++ = (type)(b * max); \
242                 if(components == 4) \
243                 { \
244                         in++; \
245                         out++; \
246                 } \
247         } \
248 }
249
250 #define CALCULATE_FACTORS(s_out, v_out, s_in, v_in) \
251         s_out = s_in; \
252         v_out = v_in;
253
254
255
256 void Color3WayUnit::process_package(LoadPackage *package)
257 {
258         Color3WayPackage *pkg = (Color3WayPackage*)package;
259         int w = plugin->get_input()->get_w();
260
261         float r_factor[SECTIONS];
262         float g_factor[SECTIONS];
263         float b_factor[SECTIONS];
264         float s_factor[SECTIONS];
265         float v_factor[SECTIONS];
266
267
268         for(int i = 0; i < SECTIONS; i++)
269         {
270                 plugin->calculate_factors(&r_factor[i], &g_factor[i], &b_factor[i], i);
271                 CALCULATE_FACTORS(s_factor[i],
272                         v_factor[i],
273                         plugin->config.saturation[i],
274                         plugin->config.value[i])
275 // printf("Color3WayUnit::process_package %d %f %f %f %f %f\n",
276 // __LINE__,
277 // r_factor[i],
278 // g_factor[i],
279 // b_factor[i],
280 // s_factor[i],
281 // v_factor[i]);
282         }
283
284
285
286         for(int i = pkg->row1; i < pkg->row2; i++)
287         {
288                 switch(plugin->get_input()->get_color_model())
289                 {
290                         case BC_RGB888:
291                                 PROCESS(unsigned char, 0xff, 3, 0)
292                                 break;
293                         case BC_RGBA8888:
294                                 PROCESS(unsigned char, 0xff, 4, 0)
295                                 break;
296                         case BC_YUV888:
297                                 PROCESS(unsigned char, 0xff, 3, 1)
298                                 break;
299                         case BC_YUVA8888:
300                                 PROCESS(unsigned char, 0xff, 4, 1)
301                                 break;
302                         case BC_RGB_FLOAT:
303                                 PROCESS(float, 1.0, 3, 0)
304                                 break;
305                         case BC_RGBA_FLOAT:
306                                 PROCESS(float, 1.0, 4, 0)
307                                 break;
308                 }
309         }
310 }
311
312
313
314
315
316
317 Color3WayEngine::Color3WayEngine(Color3WayMain *plugin, int cpus)
318  : LoadServer(cpus, cpus)
319 {
320         this->plugin = plugin;
321 }
322
323 Color3WayEngine::~Color3WayEngine()
324 {
325 }
326
327 void Color3WayEngine::init_packages()
328 {
329
330 #if 0
331 printf("Color3WayEngine::init_packages %d\n", __LINE__);
332 for(int i = 0; i <= 255; i++)
333 {
334         printf("%f\t%f\t%f\n",
335                 SHADOW_CURVE((float)i / 255),
336                 MIDTONE_CURVE((float)i / 255),
337                 HIGHLIGHT_CURVE((float)i / 255));
338 }
339 #endif
340
341         for(int i = 0; i < LoadServer::get_total_packages(); i++)
342         {
343                 Color3WayPackage *pkg = (Color3WayPackage*)get_package(i);
344                 pkg->row1 = plugin->get_input()->get_h() * i / LoadServer::get_total_packages();
345                 pkg->row2 = plugin->get_input()->get_h() * (i + 1) / LoadServer::get_total_packages();
346         }
347 }
348
349
350 LoadClient* Color3WayEngine::new_client()
351 {
352         return new Color3WayUnit(plugin, this);
353 }
354
355 LoadPackage* Color3WayEngine::new_package()
356 {
357         return new Color3WayPackage;
358 }
359
360
361
362
363
364
365
366
367
368
369 Color3WayMain::Color3WayMain(PluginServer *server)
370  : PluginVClient(server)
371 {
372         need_reconfigure = 1;
373         engine = 0;
374         w = 500;
375         h = 300;
376         for(int i = 0; i < SECTIONS; i++) copy_to_all[i] = 0;
377 }
378
379 Color3WayMain::~Color3WayMain()
380 {
381
382         delete engine;
383 }
384
385 const char* Color3WayMain::plugin_title() { return _("Color 3 Way"); }
386 int Color3WayMain::is_realtime() { return 1; }
387
388
389 int Color3WayMain::reconfigure()
390 {
391
392         return 0;
393 }
394
395 void Color3WayMain::process_pixel(float *r,
396         float *g,
397         float *b,
398         float r_in,
399         float g_in,
400         float b_in,
401         float x,
402         float y)
403 {
404         float r_factor[SECTIONS];
405         float g_factor[SECTIONS];
406         float b_factor[SECTIONS];
407         float s_factor[SECTIONS];
408         float v_factor[SECTIONS];
409         for(int i = 0; i < SECTIONS; i++)
410         {
411                 calculate_factors(r_factor + i,
412                         g_factor + i,
413                         b_factor + i,
414                         x,
415                         y);
416                 CALCULATE_FACTORS(s_factor[i], v_factor[i], 0, 0)
417         }
418
419         PROCESS_PIXEL(r_in, g_in, b_in);
420         *r = r_in;
421         *g = g_in;
422         *b = b_in;
423 }
424
425 void Color3WayMain::calculate_factors(float *r,
426         float *g,
427         float *b,
428         float x,
429         float y)
430 {
431 //      float h = atan2(-x, -y) * 360 / 2 / M_PI + 180;
432 //      float v = 1.0;
433 //      float s = sqrt(SQR(x) + SQR(y));
434 //      HSV::hsv_to_rgb(*r, *g, *b, h, s, v);
435 //printf("Color3WayMain::calculate_factors %d %f %f %f %f %f\n", __LINE__, x, y, h, s, v);
436
437         *r = sqrt(SQR(x) + SQR(y - -1));
438         *g = sqrt(SQR(x - -1.0 / ROOT_2) + SQR(y - 1.0 / ROOT_2));
439         *b = sqrt(SQR(x - 1.0 / ROOT_2) + SQR(y - 1.0 / ROOT_2));
440
441         *r = 1.0 - *r;
442         *g = 1.0 - *g;
443         *b = 1.0 - *b;
444 }
445
446 void Color3WayMain::calculate_factors(float *r, float *g, float *b, int section)
447 {
448         calculate_factors(r, g, b, config.hue_x[section], config.hue_y[section]);
449
450
451 //printf("Color3WayMain::calculate_factors %d %f %f %f\n", __LINE__, *r, *g, *b);
452 }
453
454
455
456
457
458
459
460 LOAD_CONFIGURATION_MACRO(Color3WayMain, Color3WayConfig)
461 NEW_WINDOW_MACRO(Color3WayMain, Color3WayWindow)
462
463
464
465
466
467 int Color3WayMain::process_buffer(VFrame *frame,
468         int64_t start_position,
469         double frame_rate)
470 {
471         need_reconfigure |= load_configuration();
472
473         if(!engine) engine = new Color3WayEngine(this,
474 //              1);
475                 PluginClient::smp + 1);
476
477 //printf("Color3WayMain::process_realtime 1 %d\n", need_reconfigure);
478         if(need_reconfigure)
479         {
480
481                 reconfigure();
482                 need_reconfigure = 0;
483         }
484
485
486
487         read_frame(frame,
488                 0,
489                 get_source_position(),
490                 get_framerate(),
491                 get_use_opengl());
492
493         int aggregate_interpolate = 0;
494         int aggregate_gamma = 0;
495         get_aggregation(&aggregate_interpolate,
496                 &aggregate_gamma);
497
498
499
500         engine->process_packages();
501
502
503         return 0;
504 }
505
506
507 void Color3WayMain::update_gui()
508 {
509         if(thread)
510         {
511                 load_configuration();
512                 ((Color3WayWindow*)thread->window)->lock_window("Color3WayMain::update_gui");
513                 ((Color3WayWindow*)thread->window)->update();
514                 ((Color3WayWindow*)thread->window)->unlock_window();
515         }
516 }
517
518
519
520
521 void Color3WayMain::save_data(KeyFrame *keyframe)
522 {
523         FileXML output;
524
525 // cause data to be stored directly in text
526         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
527         output.tag.set_title("COLOR3WAY");
528         for(int i = 0; i < SECTIONS; i++)
529         {
530                 char string[BCTEXTLEN];
531                 sprintf(string, "HUE_X_%d", i);
532                 output.tag.set_property(string, config.hue_x[i]);
533                 sprintf(string, "HUE_Y_%d", i);
534                 output.tag.set_property(string, config.hue_y[i]);
535                 sprintf(string, "VALUE_%d", i);
536                 output.tag.set_property(string, config.value[i]);
537                 sprintf(string, "SATURATION_%d", i);
538                 output.tag.set_property(string, config.saturation[i]);
539                 if(is_defaults())
540                 {
541                         sprintf(string, "COPY_TO_ALL_%d", i);
542                         output.tag.set_property(string, copy_to_all[i]);
543                 }
544         }
545
546         if(is_defaults())
547         {
548                 output.tag.set_property("W",  w);
549                 output.tag.set_property("H",  h);
550         }
551
552         output.append_tag();
553         output.tag.set_title("/COLOR3WAY");
554         output.append_tag();
555         output.append_newline();
556         output.terminate_string();
557 }
558
559 void Color3WayMain::read_data(KeyFrame *keyframe)
560 {
561         FileXML input;
562
563         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
564
565         int result = 0;
566
567         while(!result)
568         {
569                 result = input.read_tag();
570
571                 if(!result)
572                 {
573                         if(input.tag.title_is("COLOR3WAY"))
574                         {
575                                 for(int i = 0; i < SECTIONS; i++)
576                                 {
577                                         char string[BCTEXTLEN];
578                                         sprintf(string, "HUE_X_%d", i);
579                                         config.hue_x[i] = input.tag.get_property(string, config.hue_x[i]);
580                                         sprintf(string, "HUE_Y_%d", i);
581                                         config.hue_y[i] = input.tag.get_property(string, config.hue_y[i]);
582                                         sprintf(string, "VALUE_%d", i);
583                                         config.value[i] = input.tag.get_property(string, config.value[i]);
584                                         sprintf(string, "SATURATION_%d", i);
585                                         config.saturation[i] = input.tag.get_property(string, config.saturation[i]);
586
587                                         if(is_defaults())
588                                         {
589                                                 sprintf(string, "COPY_TO_ALL_%d", i);
590                                                 copy_to_all[i] = input.tag.get_property(string, copy_to_all[i]);
591                                         }
592                                 }
593
594                                 if(is_defaults())
595                                 {
596                                         w = input.tag.get_property("W", w);
597                                         h = input.tag.get_property("H", h);
598                                 }
599                         }
600                 }
601         }
602 }
603
604 void Color3WayMain::get_aggregation(int *aggregate_interpolate,
605         int *aggregate_gamma)
606 {
607         if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) &&
608                 !strcmp(get_output()->get_prev_effect(0), _("Gamma")))
609         {
610                 *aggregate_interpolate = 1;
611                 *aggregate_gamma = 1;
612         }
613         else
614         if(!strcmp(get_output()->get_prev_effect(0), _("Interpolate Pixels")))
615         {
616                 *aggregate_interpolate = 1;
617         }
618         else
619         if(!strcmp(get_output()->get_prev_effect(0), _("Gamma")))
620         {
621                 *aggregate_gamma = 1;
622         }
623 }
624
625 int Color3WayMain::handle_opengl()
626 {
627 #ifdef HAVE_GL
628
629         get_output()->to_texture();
630         get_output()->enable_opengl();
631
632         unsigned int shader = 0;
633         const char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
634         int current_shader = 0;
635         int aggregate_interpolate = 0;
636         int aggregate_gamma = 0;
637
638         get_aggregation(&aggregate_interpolate,
639                 &aggregate_gamma);
640
641 printf("Color3WayMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma);
642         if(aggregate_interpolate)
643                 INTERPOLATE_COMPILE(shader_stack, current_shader)
644
645         if(aggregate_gamma)
646                 GAMMA_COMPILE(shader_stack, current_shader, aggregate_interpolate)
647
648         COLORBALANCE_COMPILE(shader_stack,
649                 current_shader,
650                 aggregate_gamma || aggregate_interpolate)
651
652         shader = VFrame::make_shader(0,
653                 shader_stack[0],
654                 shader_stack[1],
655                 shader_stack[2],
656                 shader_stack[3],
657                 shader_stack[4],
658                 shader_stack[5],
659                 shader_stack[6],
660                 shader_stack[7],
661                 0);
662
663         if(shader > 0)
664         {
665                 glUseProgram(shader);
666                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
667
668                 if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader);
669                 if(aggregate_gamma) GAMMA_UNIFORMS(shader);
670
671                 COLORBALANCE_UNIFORMS(shader);
672
673         }
674
675         get_output()->init_screen();
676         get_output()->bind_texture(0);
677         get_output()->draw_texture();
678         glUseProgram(0);
679         get_output()->set_opengl_state(VFrame::SCREEN);
680 #endif
681         return 0;
682 }
683
684
685
686
687
688
689