yuv colorspace/range + prefs, ffmpeg colorrange probe, x11 direct force colormodel...
[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.yuv_to_rgb_f(r, g, b, \
208                                 (float)in[0] / max, \
209                                 (float)in[1] / max - 0.5, \
210                                 (float)in[2] / max - 0.5); \
211                         in += 3; \
212                 } \
213                 else \
214                 { \
215                         r = (float)*in++ / max; \
216                         g = (float)*in++ / max; \
217                         b = (float)*in++ / max; \
218                 } \
219  \
220                 PROCESS_PIXEL(r, g, b) \
221  \
222 /* Convert to project colormodel */ \
223                 if(is_yuv) \
224                 { \
225                         float y, u, v; \
226                         YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
227                         r = y; \
228                         g = u + 0.5; \
229                         b = v + 0.5; \
230                 } \
231                 if(max == 0xff) \
232                 { \
233                         CLAMP(r, 0, 1); \
234                         CLAMP(g, 0, 1); \
235                         CLAMP(b, 0, 1); \
236                 } \
237                 *out++ = (type)(r * max); \
238                 *out++ = (type)(g * max); \
239                 *out++ = (type)(b * max); \
240                 if(components == 4) \
241                 { \
242                         in++; \
243                         out++; \
244                 } \
245         } \
246 }
247
248 #define CALCULATE_FACTORS(s_out, v_out, s_in, v_in) \
249         s_out = s_in; \
250         v_out = v_in;
251
252
253
254 void Color3WayUnit::process_package(LoadPackage *package)
255 {
256         Color3WayPackage *pkg = (Color3WayPackage*)package;
257         int w = plugin->get_input()->get_w();
258
259         float r_factor[SECTIONS];
260         float g_factor[SECTIONS];
261         float b_factor[SECTIONS];
262         float s_factor[SECTIONS];
263         float v_factor[SECTIONS];
264
265
266         for(int i = 0; i < SECTIONS; i++)
267         {
268                 plugin->calculate_factors(&r_factor[i], &g_factor[i], &b_factor[i], i);
269                 CALCULATE_FACTORS(s_factor[i],
270                         v_factor[i],
271                         plugin->config.saturation[i],
272                         plugin->config.value[i])
273 // printf("Color3WayUnit::process_package %d %f %f %f %f %f\n",
274 // __LINE__,
275 // r_factor[i],
276 // g_factor[i],
277 // b_factor[i],
278 // s_factor[i],
279 // v_factor[i]);
280         }
281
282
283
284         for(int i = pkg->row1; i < pkg->row2; i++)
285         {
286                 switch(plugin->get_input()->get_color_model())
287                 {
288                         case BC_RGB888:
289                                 PROCESS(unsigned char, 0xff, 3, 0)
290                                 break;
291                         case BC_RGBA8888:
292                                 PROCESS(unsigned char, 0xff, 4, 0)
293                                 break;
294                         case BC_YUV888:
295                                 PROCESS(unsigned char, 0xff, 3, 1)
296                                 break;
297                         case BC_YUVA8888:
298                                 PROCESS(unsigned char, 0xff, 4, 1)
299                                 break;
300                         case BC_RGB_FLOAT:
301                                 PROCESS(float, 1.0, 3, 0)
302                                 break;
303                         case BC_RGBA_FLOAT:
304                                 PROCESS(float, 1.0, 4, 0)
305                                 break;
306                 }
307         }
308 }
309
310
311
312
313
314
315 Color3WayEngine::Color3WayEngine(Color3WayMain *plugin, int cpus)
316  : LoadServer(cpus, cpus)
317 {
318         this->plugin = plugin;
319 }
320
321 Color3WayEngine::~Color3WayEngine()
322 {
323 }
324
325 void Color3WayEngine::init_packages()
326 {
327
328 #if 0
329 printf("Color3WayEngine::init_packages %d\n", __LINE__);
330 for(int i = 0; i <= 255; i++)
331 {
332         printf("%f\t%f\t%f\n",
333                 SHADOW_CURVE((float)i / 255),
334                 MIDTONE_CURVE((float)i / 255),
335                 HIGHLIGHT_CURVE((float)i / 255));
336 }
337 #endif
338
339         for(int i = 0; i < LoadServer::get_total_packages(); i++)
340         {
341                 Color3WayPackage *pkg = (Color3WayPackage*)get_package(i);
342                 pkg->row1 = plugin->get_input()->get_h() * i / LoadServer::get_total_packages();
343                 pkg->row2 = plugin->get_input()->get_h() * (i + 1) / LoadServer::get_total_packages();
344         }
345 }
346
347
348 LoadClient* Color3WayEngine::new_client()
349 {
350         return new Color3WayUnit(plugin, this);
351 }
352
353 LoadPackage* Color3WayEngine::new_package()
354 {
355         return new Color3WayPackage;
356 }
357
358
359
360
361
362
363
364
365
366
367 Color3WayMain::Color3WayMain(PluginServer *server)
368  : PluginVClient(server)
369 {
370         need_reconfigure = 1;
371         engine = 0;
372         w = 500;
373         h = 300;
374         for(int i = 0; i < SECTIONS; i++) copy_to_all[i] = 0;
375 }
376
377 Color3WayMain::~Color3WayMain()
378 {
379
380         delete engine;
381 }
382
383 const char* Color3WayMain::plugin_title() { return _("Color 3 Way"); }
384 int Color3WayMain::is_realtime() { return 1; }
385
386
387 int Color3WayMain::reconfigure()
388 {
389
390         return 0;
391 }
392
393 void Color3WayMain::process_pixel(float *r,
394         float *g,
395         float *b,
396         float r_in,
397         float g_in,
398         float b_in,
399         float x,
400         float y)
401 {
402         float r_factor[SECTIONS];
403         float g_factor[SECTIONS];
404         float b_factor[SECTIONS];
405         float s_factor[SECTIONS];
406         float v_factor[SECTIONS];
407         for(int i = 0; i < SECTIONS; i++)
408         {
409                 calculate_factors(r_factor + i,
410                         g_factor + i,
411                         b_factor + i,
412                         x,
413                         y);
414                 CALCULATE_FACTORS(s_factor[i], v_factor[i], 0, 0)
415         }
416
417         PROCESS_PIXEL(r_in, g_in, b_in);
418         *r = r_in;
419         *g = g_in;
420         *b = b_in;
421 }
422
423 void Color3WayMain::calculate_factors(float *r,
424         float *g,
425         float *b,
426         float x,
427         float y)
428 {
429 //      float h = atan2(-x, -y) * 360 / 2 / M_PI + 180;
430 //      float v = 1.0;
431 //      float s = sqrt(SQR(x) + SQR(y));
432 //      HSV::hsv_to_rgb(*r, *g, *b, h, s, v);
433 //printf("Color3WayMain::calculate_factors %d %f %f %f %f %f\n", __LINE__, x, y, h, s, v);
434
435         *r = sqrt(SQR(x) + SQR(y - -1));
436         *g = sqrt(SQR(x - -1.0 / ROOT_2) + SQR(y - 1.0 / ROOT_2));
437         *b = sqrt(SQR(x - 1.0 / ROOT_2) + SQR(y - 1.0 / ROOT_2));
438
439         *r = 1.0 - *r;
440         *g = 1.0 - *g;
441         *b = 1.0 - *b;
442 }
443
444 void Color3WayMain::calculate_factors(float *r, float *g, float *b, int section)
445 {
446         calculate_factors(r, g, b, config.hue_x[section], config.hue_y[section]);
447
448
449 //printf("Color3WayMain::calculate_factors %d %f %f %f\n", __LINE__, *r, *g, *b);
450 }
451
452
453
454
455
456
457
458 LOAD_CONFIGURATION_MACRO(Color3WayMain, Color3WayConfig)
459 NEW_WINDOW_MACRO(Color3WayMain, Color3WayWindow)
460
461
462
463
464
465 int Color3WayMain::process_buffer(VFrame *frame,
466         int64_t start_position,
467         double frame_rate)
468 {
469         need_reconfigure |= load_configuration();
470
471         if(!engine) engine = new Color3WayEngine(this,
472 //              1);
473                 PluginClient::smp + 1);
474
475 //printf("Color3WayMain::process_realtime 1 %d\n", need_reconfigure);
476         if(need_reconfigure)
477         {
478
479                 reconfigure();
480                 need_reconfigure = 0;
481         }
482
483
484
485         read_frame(frame,
486                 0,
487                 get_source_position(),
488                 get_framerate(),
489                 get_use_opengl());
490
491         int aggregate_interpolate = 0;
492         int aggregate_gamma = 0;
493         get_aggregation(&aggregate_interpolate,
494                 &aggregate_gamma);
495
496
497
498         engine->process_packages();
499
500
501         return 0;
502 }
503
504
505 void Color3WayMain::update_gui()
506 {
507         if(thread)
508         {
509                 load_configuration();
510                 ((Color3WayWindow*)thread->window)->lock_window("Color3WayMain::update_gui");
511                 ((Color3WayWindow*)thread->window)->update();
512                 ((Color3WayWindow*)thread->window)->unlock_window();
513         }
514 }
515
516
517
518
519 void Color3WayMain::save_data(KeyFrame *keyframe)
520 {
521         FileXML output;
522
523 // cause data to be stored directly in text
524         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
525         output.tag.set_title("COLOR3WAY");
526         for(int i = 0; i < SECTIONS; i++)
527         {
528                 char string[BCTEXTLEN];
529                 sprintf(string, "HUE_X_%d", i);
530                 output.tag.set_property(string, config.hue_x[i]);
531                 sprintf(string, "HUE_Y_%d", i);
532                 output.tag.set_property(string, config.hue_y[i]);
533                 sprintf(string, "VALUE_%d", i);
534                 output.tag.set_property(string, config.value[i]);
535                 sprintf(string, "SATURATION_%d", i);
536                 output.tag.set_property(string, config.saturation[i]);
537                 if(is_defaults())
538                 {
539                         sprintf(string, "COPY_TO_ALL_%d", i);
540                         output.tag.set_property(string, copy_to_all[i]);
541                 }
542         }
543
544         if(is_defaults())
545         {
546                 output.tag.set_property("W",  w);
547                 output.tag.set_property("H",  h);
548         }
549
550         output.append_tag();
551         output.tag.set_title("/COLOR3WAY");
552         output.append_tag();
553         output.append_newline();
554         output.terminate_string();
555 }
556
557 void Color3WayMain::read_data(KeyFrame *keyframe)
558 {
559         FileXML input;
560
561         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
562
563         int result = 0;
564
565         while(!result)
566         {
567                 result = input.read_tag();
568
569                 if(!result)
570                 {
571                         if(input.tag.title_is("COLOR3WAY"))
572                         {
573                                 for(int i = 0; i < SECTIONS; i++)
574                                 {
575                                         char string[BCTEXTLEN];
576                                         sprintf(string, "HUE_X_%d", i);
577                                         config.hue_x[i] = input.tag.get_property(string, config.hue_x[i]);
578                                         sprintf(string, "HUE_Y_%d", i);
579                                         config.hue_y[i] = input.tag.get_property(string, config.hue_y[i]);
580                                         sprintf(string, "VALUE_%d", i);
581                                         config.value[i] = input.tag.get_property(string, config.value[i]);
582                                         sprintf(string, "SATURATION_%d", i);
583                                         config.saturation[i] = input.tag.get_property(string, config.saturation[i]);
584
585                                         if(is_defaults())
586                                         {
587                                                 sprintf(string, "COPY_TO_ALL_%d", i);
588                                                 copy_to_all[i] = input.tag.get_property(string, copy_to_all[i]);
589                                         }
590                                 }
591
592                                 if(is_defaults())
593                                 {
594                                         w = input.tag.get_property("W", w);
595                                         h = input.tag.get_property("H", h);
596                                 }
597                         }
598                 }
599         }
600 }
601
602 void Color3WayMain::get_aggregation(int *aggregate_interpolate,
603         int *aggregate_gamma)
604 {
605         if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) &&
606                 !strcmp(get_output()->get_prev_effect(0), _("Gamma")))
607         {
608                 *aggregate_interpolate = 1;
609                 *aggregate_gamma = 1;
610         }
611         else
612         if(!strcmp(get_output()->get_prev_effect(0), _("Interpolate Pixels")))
613         {
614                 *aggregate_interpolate = 1;
615         }
616         else
617         if(!strcmp(get_output()->get_prev_effect(0), _("Gamma")))
618         {
619                 *aggregate_gamma = 1;
620         }
621 }
622
623 int Color3WayMain::handle_opengl()
624 {
625 #ifdef HAVE_GL
626
627         get_output()->to_texture();
628         get_output()->enable_opengl();
629
630         unsigned int shader = 0;
631         const char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
632         int current_shader = 0;
633         int aggregate_interpolate = 0;
634         int aggregate_gamma = 0;
635
636         get_aggregation(&aggregate_interpolate,
637                 &aggregate_gamma);
638
639 printf("Color3WayMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma);
640         if(aggregate_interpolate)
641                 INTERPOLATE_COMPILE(shader_stack, current_shader)
642
643         if(aggregate_gamma)
644                 GAMMA_COMPILE(shader_stack, current_shader, aggregate_interpolate)
645
646         COLORBALANCE_COMPILE(shader_stack,
647                 current_shader,
648                 aggregate_gamma || aggregate_interpolate)
649
650         shader = VFrame::make_shader(0,
651                 shader_stack[0],
652                 shader_stack[1],
653                 shader_stack[2],
654                 shader_stack[3],
655                 shader_stack[4],
656                 shader_stack[5],
657                 shader_stack[6],
658                 shader_stack[7],
659                 0);
660
661         if(shader > 0)
662         {
663                 glUseProgram(shader);
664                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
665
666                 if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader);
667                 if(aggregate_gamma) GAMMA_UNIFORMS(shader);
668
669                 COLORBALANCE_UNIFORMS(shader);
670
671         }
672
673         get_output()->init_screen();
674         get_output()->bind_texture(0);
675         get_output()->draw_texture();
676         glUseProgram(0);
677         get_output()->set_opengl_state(VFrame::SCREEN);
678 #endif
679         return 0;
680 }
681
682
683
684
685
686
687