Credit Andrew - fix bug in render farm usage when using in/out pointers or selection
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / swatch / swatch.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2024 Adam Williams <broadcast at earthling dot net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  * 
19  */
20
21
22 // draw a color swatch for a given brightness or saturation
23 // does not visualize but draws output to be processed
24
25 #include <math.h>
26 #include <stdint.h>
27 #include <string.h>
28
29 #include "bcdisplayinfo.h"
30 #include "bccolors.h"
31 #include "clip.h"
32 #include "bchash.h"
33 #include "filexml.h"
34 #include "keyframe.h"
35 #include "language.h"
36 #include "playback3d.h"
37 #include "swatch.h"
38 #include "vframe.h"
39
40
41
42
43 REGISTER_PLUGIN(SwatchMain)
44
45
46 #define MAX_VALUE 100
47
48
49
50 SwatchConfig::SwatchConfig()
51 {
52         brightness = MAX_VALUE;
53     saturation = MAX_VALUE;
54     fix_brightness = 0;
55     angle = 0;
56     draw_src = 0;
57 }
58
59 int SwatchConfig::equivalent(SwatchConfig &that)
60 {
61         return brightness == that.brightness &&
62         saturation == that.saturation &&
63         fix_brightness == that.fix_brightness &&
64         angle == that.angle &&
65         draw_src == that.draw_src;
66 }
67
68 void SwatchConfig::copy_from(SwatchConfig &that)
69 {
70         brightness = that.brightness;
71         saturation = that.saturation;
72     fix_brightness = that.fix_brightness;
73     angle = that.angle;
74     draw_src = that.draw_src;
75 }
76
77 void SwatchConfig::interpolate(SwatchConfig &prev, 
78         SwatchConfig &next, 
79         long prev_frame, 
80         long next_frame, 
81         long current_frame)
82 {
83         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
84         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
85
86
87         this->brightness = (int)(prev.brightness * prev_scale + next.brightness * next_scale);
88         this->saturation = (int)(prev.saturation * prev_scale + next.saturation * next_scale);
89         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale);
90     fix_brightness = prev.fix_brightness;
91     draw_src = prev.draw_src;
92 }
93
94
95
96
97 SwatchSlider::SwatchSlider(SwatchMain *plugin, 
98                            SwatchWindow *gui,
99                            int x, 
100                            int y,
101                            int min,
102                            int max,
103                            int *output)
104   : BC_ISlider(x,
105                y,
106                0, 
107                gui->get_w() - xS(10) - x, 
108                gui->get_w() - xS(10) - x, 
109                min, 
110                max, 
111                *output)
112 {
113   this->plugin = plugin;
114   this->output = output;
115 }
116
117 int SwatchSlider::handle_event ()
118 {
119         *output = get_value();
120         plugin->send_configure_change();
121         return 1;
122 }
123
124 SwatchRadial::SwatchRadial(SwatchMain *plugin, 
125     SwatchWindow *gui, 
126     int x, 
127     int y, 
128     const char *text,
129     int fix_brightness)
130  : BC_Radial(x, 
131         y, 
132         (plugin->config.fix_brightness && fix_brightness), 
133         text)
134 {
135         this->plugin = plugin;
136     this->gui = gui;
137     this->fix_brightness = fix_brightness;
138 }
139
140 int SwatchRadial::handle_event()
141 {
142     plugin->config.fix_brightness = fix_brightness;
143     gui->update_fixed();
144     plugin->send_configure_change();
145     return 1;
146 }
147
148
149 SwatchCheck::SwatchCheck(SwatchMain *plugin, 
150     int x, 
151     int y, 
152     const char *text,
153     int *output)
154  : BC_CheckBox(x, 
155         y, 
156         *output, 
157         text)
158 {
159         this->plugin = plugin;
160     this->output = output;
161 }
162
163 int SwatchCheck::handle_event()
164 {
165     *output = get_value();
166     plugin->send_configure_change();
167     return 1;
168 }
169
170
171
172
173 SwatchWindow::SwatchWindow(SwatchMain *plugin)
174  : PluginClientWindow(plugin,
175         xS(350), 
176         yS(300), 
177         xS(350), 
178         yS(300), 
179         0)
180 {
181         this->plugin = plugin;
182 }
183
184 SwatchWindow::~SwatchWindow()
185 {
186 }
187
188 void SwatchWindow::create_objects()
189 {
190         int margin = yS(10);
191         int x = margin, y = margin;
192         BC_Title *title;
193
194         add_subwindow(brightness_title = new BC_Title(x, y, _("Brightness:")));
195     y += brightness_title->get_h() + margin;
196         add_subwindow (brightness = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.brightness));
197     y += brightness->get_h() + margin;
198
199         add_subwindow(saturation_title = new BC_Title(x, y, _("Saturation:")));
200     y += saturation_title->get_h() + margin;
201         add_subwindow (saturation = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.saturation));
202     y += saturation->get_h() + margin;
203
204         add_subwindow(title = new BC_Title(x, y, _("Angle:")));
205     y += title->get_h() + margin;
206         add_subwindow (angle = new SwatchSlider(plugin, this, x, y, -180, 180, &plugin->config.angle));
207     y += saturation->get_h() + margin;
208
209     add_subwindow(fix_brightness = new SwatchRadial(plugin, 
210         this, 
211         x, 
212         y, 
213         _("Constant Brightness"),
214         1));
215     y += fix_brightness->get_h() + margin;
216     add_subwindow(fix_saturation = new SwatchRadial(plugin, 
217         this, 
218         x, 
219         y, 
220         _("Constant Saturation"),
221         0));
222     y += fix_saturation->get_h() + margin;
223     update_fixed();
224     
225     add_subwindow(draw_src = new SwatchCheck(plugin, 
226         x, 
227         y, 
228         _("Draw source"),
229         &plugin->config.draw_src));
230    
231     
232         show_window();
233 }
234
235 void SwatchWindow::update_fixed()
236 {
237     fix_brightness->update(plugin->config.fix_brightness);
238     fix_saturation->update(!plugin->config.fix_brightness);
239     if(plugin->config.fix_brightness)
240     {
241         saturation_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
242         brightness_title->set_color(BC_WindowBase::get_resources()->default_text_color);
243         saturation->disable();
244         brightness->enable();
245     }
246     else
247     {
248         saturation_title->set_color(BC_WindowBase::get_resources()->default_text_color);
249         brightness_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
250         saturation->enable();
251         brightness->disable();
252     }
253 }
254
255
256 SwatchMain::SwatchMain(PluginServer *server)
257  : PluginVClient(server)
258 {
259         need_reconfigure = 1;
260         engine = 0;
261     temp = 0;
262     src_temp = 0;
263 }
264
265 SwatchMain::~SwatchMain()
266 {
267         if(engine) delete engine;
268     if(temp) delete temp;
269     if(src_temp) delete src_temp;
270 }
271
272 const char* SwatchMain::plugin_title() { return N_("Color Swatch"); }
273 int SwatchMain::is_realtime() { return 1; }
274
275
276 NEW_WINDOW_MACRO(SwatchMain, SwatchWindow)
277
278 LOAD_CONFIGURATION_MACRO(SwatchMain, SwatchConfig)
279
280 int SwatchMain::is_synthesis()
281 {
282         return 1;
283 }
284
285 int SwatchMain::process_buffer(VFrame *frame,
286         int64_t start_position,
287         double frame_rate)
288 {
289         need_reconfigure |= load_configuration();
290         int use_opengl = get_use_opengl();
291
292 // have to draw output pixels out of order
293     if(config.draw_src) use_opengl = 0;
294
295         if(use_opengl) return run_opengl();
296
297         if(!engine) engine = new SwatchEngine(this,
298                 get_project_smp() + 1,
299                 get_project_smp() + 1);
300     if(config.draw_src)
301     {
302         if(!src_temp)
303             src_temp = new VFrame(0, 
304                         -1,
305                         frame->get_w(),
306                         frame->get_h(),
307                     frame->get_color_model(),
308                         -1);
309
310         read_frame(src_temp, 
311                     0, 
312                     start_position, 
313                     frame_rate,
314                     use_opengl);
315     }
316
317
318         if(!temp) temp = new VFrame(0, 
319                 -1,
320                 frame->get_w(),
321                 frame->get_h(),
322             frame->get_color_model(),
323                 -1);
324
325 // draw pattern once
326     if(need_reconfigure)
327             engine->draw_pattern();
328
329 // draw the pattern on the output
330     frame->copy_from(temp);
331 // draw input on the pattern
332     if(config.draw_src)
333         engine->draw_src();
334
335 //printf("SwatchMain::process_buffer %d %d\n", __LINE__, config.draw_src);
336         return 0;
337 }
338
339
340 void SwatchMain::update_gui()
341 {
342         if(thread)
343         {
344                 if(load_configuration())
345                 {
346                         ((SwatchWindow*)thread->window)->lock_window("SwatchMain::update_gui");
347                         ((SwatchWindow*)thread->window)->brightness->update(config.brightness);
348                         ((SwatchWindow*)thread->window)->saturation->update(config.saturation);
349                         ((SwatchWindow*)thread->window)->angle->update(config.angle);
350                         ((SwatchWindow*)thread->window)->draw_src->update(config.draw_src);
351                         ((SwatchWindow*)thread->window)->update_fixed();
352                         ((SwatchWindow*)thread->window)->unlock_window();
353                 }
354         }
355 }
356
357
358
359
360
361 void SwatchMain::save_data(KeyFrame *keyframe)
362 {
363         FileXML output;
364
365 // cause data to be stored directly in text
366         output.set_shared_output(keyframe->xbuf);
367         output.tag.set_title("SWATCH");
368
369         output.tag.set_property("BRIGHTNESS", config.brightness);
370         output.tag.set_property("SATURATION", config.saturation);
371         output.tag.set_property("ANGLE", config.angle);
372         output.tag.set_property("FIX_BRIGHTNESS", config.fix_brightness);
373         output.tag.set_property("DRAW_SRC", config.draw_src);
374         output.append_tag();
375         output.tag.set_title("/SWATCH");
376         output.append_tag();
377         output.append_newline();
378         output.terminate_string();
379 //printf("SwatchMain::save_data %d %s\n", __LINE__, output.string);
380 }
381
382 void SwatchMain::read_data(KeyFrame *keyframe)
383 {
384   FileXML input;
385
386   input.set_shared_input(keyframe->xbuf);
387
388   while( !input.read_tag() )
389   {
390     if(input.tag.title_is("SWATCH"))
391     {
392       config.brightness =
393         input.tag.get_property("BRIGHTNESS", config.brightness);
394       config.saturation =
395         input.tag.get_property("SATURATION", config.saturation);
396       config.angle =
397         input.tag.get_property("ANGLE", config.angle);
398       config.fix_brightness =
399         input.tag.get_property("FIX_BRIGHTNESS", config.fix_brightness);
400       config.draw_src =
401         input.tag.get_property("DRAW_SRC", config.draw_src);
402     }
403   }
404 }
405
406 int SwatchMain::handle_opengl()
407 {
408 #ifdef HAVE_GL
409         const char *head_frag =
410                 "uniform mat3 yuv_to_rgb_matrix;\n"
411                 "uniform mat3 rgb_to_yuv_matrix;\n"
412                 "uniform float yminf;\n"
413                 "uniform sampler2D tex;\n"
414                 "uniform vec2 texture_extents;\n"
415                 "uniform vec2 center_coord;\n"
416                 "uniform float value;\n"
417                 "uniform float saturation;\n"
418                 "uniform float angle;\n"
419                 "uniform bool fix_value;\n"
420                 "\n"
421                 "void main()\n"
422                 "{\n"
423                 "       vec2 out_coord = gl_TexCoord[0].st * texture_extents;\n"
424                 "       vec2 in_coord = vec2(out_coord.x - center_coord.x, out_coord.y - center_coord.y);\n"
425         "   float max_s = center_coord.x;\n"
426         "   if(center_coord.y < max_s) max_s = center_coord.y;\n"
427         "   vec4 pixel;\n"
428         "   pixel.a = 1.0;\n"
429         "   pixel.r = atan(in_coord.x, in_coord.y) / 2.0 / 3.14159 * 360.0 + angle; // hue\n"
430         "   if(pixel.r < 0.0) pixel.r += 360.0;\n"
431         "   if(fix_value)\n"
432         "   {\n"
433         "       pixel.g = length(vec2(in_coord.x, in_coord.y)) / max_s; // saturation\n"
434         "       if(pixel.g > 1.0) pixel.g = 1.0; \n"
435         "       pixel.b = value;\n"
436         "   }\n"
437         "   else\n"
438         "   {\n"
439         "       pixel.g = saturation;\n"
440         "       pixel.b = length(vec2(in_coord.x, in_coord.y)) / max_s; // value\n"
441         "       if(pixel.b > 1.0) pixel.b = 1.0; \n"
442         "   }\n"
443         HSV_TO_RGB_FRAG("pixel");
444
445         static const char *put_yuv_frag =
446                         RGB_TO_YUV_FRAG("pixel")
447                 "       gl_FragColor = pixel;\n"
448                 "}\n";
449
450         static const char *put_rgb_frag =
451                 "       gl_FragColor = pixel;\n"
452                 "}\n";
453
454
455
456
457         const char *shader_stack[5] = { 0, 0, 0, 0, 0 };
458         shader_stack[0] = head_frag;
459         if(BC_CModels::is_yuv(get_output()->get_color_model()))
460           shader_stack[1] = put_yuv_frag;
461         else
462           shader_stack[1] = put_rgb_frag;
463
464         get_output()->set_opengl_state(VFrame::TEXTURE);
465         get_output()->to_texture();
466         get_output()->enable_opengl();
467         get_output()->init_screen();
468         get_output()->bind_texture(0);
469
470         unsigned int frag = VFrame::make_shader(0, 
471                 shader_stack[0], 
472                 shader_stack[1], 
473                 0);
474
475         if(frag)
476         {
477                 glUseProgram(frag);
478                 float w = get_output()->get_w();
479                 float h = get_output()->get_h();
480                 float texture_w = get_output()->get_texture_w();
481                 float texture_h = get_output()->get_texture_h();
482                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
483                 glUniform2f(glGetUniformLocation(frag, "center_coord"), 
484                                 (GLfloat)w / 2,
485                                 (GLfloat)h / 2);
486                 glUniform2f(glGetUniformLocation(frag, "texture_extents"), 
487                                 (GLfloat)texture_w,
488                                 (GLfloat)texture_h);
489
490                 glUniform1f(glGetUniformLocation(frag, "value"), (float)config.brightness / MAX_VALUE);
491                 glUniform1f(glGetUniformLocation(frag, "saturation"), (float)config.saturation / MAX_VALUE);
492                 glUniform1f(glGetUniformLocation(frag, "angle"), (float)config.angle);
493                 glUniform1i(glGetUniformLocation(frag, "fix_value"), config.fix_brightness);
494                 if(BC_CModels::is_yuv(get_output()->get_color_model()))
495                         BC_GL_COLORS(frag);
496         }
497
498         get_output()->draw_texture();
499         glUseProgram(0);
500         get_output()->set_opengl_state(VFrame::SCREEN);
501         
502 #endif
503     return 0;
504 }
505
506
507
508
509
510
511
512
513
514
515
516 SwatchPackage::SwatchPackage()
517  : LoadPackage()
518 {
519 }
520
521
522
523
524 SwatchUnit::SwatchUnit(SwatchEngine *server, SwatchMain *plugin)
525  : LoadClient(server)
526 {
527         this->plugin = plugin;
528         this->server = server;
529 }
530
531
532
533
534 #define CREATE_SWATCH(type, components, max, is_yuv) \
535 { \
536         for(int i = pkg->y1; i < pkg->y2; i++) \
537         { \
538                 type *out_row = (type*)plugin->temp->get_rows()[i]; \
539         for(int j = 0; j < w; j++) \
540         { \
541             float hue = atan2(j - center_x, i - center_y) * 360 / 2 / M_PI + angle; \
542             if(fix_brightness) \
543             { \
544                 saturation = hypot(j - center_x, i - center_y) / max_s; \
545                 if(saturation > 1) saturation = 1; \
546             } \
547             else \
548             { \
549                 value = hypot(j - center_x, i - center_y) / max_s; \
550                 if(value > 1) value = 1; \
551             } \
552             if(hue < 0) hue += 360; \
553             if(is_yuv) \
554             { \
555                 int y, u, v; \
556                 HSV::hsv_to_yuv(y, u, v, hue, saturation, value, max); \
557                 out_row[0] = y; \
558                 out_row[1] = u; \
559                 out_row[2] = v; \
560             } \
561             else \
562             { \
563                 float r, g, b; \
564                 HSV::hsv_to_rgb(r, g, b, hue, saturation, value); \
565                 out_row[0] = (type)(r * max); \
566                 out_row[1] = (type)(g * max); \
567                 out_row[2] = (type)(b * max); \
568             } \
569  \
570 /* the alpha */ \
571             if(components == 4) out_row[3] = max; \
572             out_row += components; \
573         } \
574     } \
575 }
576
577
578 #define DRAW_SRC(type, components, max, is_yuv) \
579 { \
580     type **dst_rows = (type**)plugin->get_output()->get_rows(); \
581         for(int i = pkg->y1; i < pkg->y2; i++) \
582         { \
583                 type *src_row = (type*)plugin->src_temp->get_rows()[i]; \
584         for(int j = 0; j < w; j++) \
585         { \
586 /* the source values */ \
587             type r, g, b; \
588             float r2, g2, b2; \
589             if(is_yuv) \
590             { \
591                 YUV::yuv.yuv_to_rgb_f (r2, g2, b2, src_row[0], src_row[1], src_row[2]); \
592             } \
593             else \
594             { \
595                 r = src_row[0]; \
596                 g = src_row[1]; \
597                 b = src_row[2]; \
598                 r2 = (float)r / max; \
599                 g2 = (float)g / max; \
600                 b2 = (float)b / max; \
601             } \
602             float hue, s, value; \
603             HSV::rgb_to_hsv(r2, g2, b2, hue, s, value); \
604             float h_rad = TO_RAD(hue - angle); \
605 /* get coordinate of color in output */ \
606             int x_out, y_out; \
607             if(fix_brightness) \
608             { \
609                 x_out = center_x + (int)(sin(h_rad) * s * max_s); \
610                 y_out = center_y + (int)(cos(h_rad) * s * max_s); \
611             } \
612             else \
613             { \
614                 x_out = center_x + (int)(sin(h_rad) * value * max_s); \
615                 y_out = center_y + (int)(cos(h_rad) * value * max_s); \
616             } \
617             if(x_out >= 0 && x_out < w && y_out >= 0 && y_out < h) \
618             { \
619                 type *dst = dst_rows[y_out] + x_out * components; \
620                 if(is_yuv) \
621                 { \
622                     dst[0] = src_row[0]; \
623                     dst[1] = src_row[1]; \
624                     dst[2] = src_row[2]; \
625                 } \
626                 else \
627                 { \
628                     dst[0] = r; \
629                     dst[1] = g; \
630                     dst[2] = b; \
631                 } \
632             } \
633  \
634             src_row += components; \
635         } \
636     } \
637 }
638
639 #define DRAW_PATTERN_MODE 0
640 #define DRAW_SRC_MODE 1
641
642 void SwatchUnit::process_package(LoadPackage *package)
643 {
644         SwatchPackage *pkg = (SwatchPackage*)package;
645         int h = plugin->temp->get_h();
646         int w = plugin->temp->get_w();
647         int center_x = w / 2;
648         int center_y = h / 2;
649         int cmodel = plugin->temp->get_color_model();
650 // maximum saturation
651     float max_s = center_x;
652     if(center_y < max_s) max_s = center_y;
653     int fix_brightness = plugin->config.fix_brightness;
654     float saturation = (float)plugin->config.saturation / MAX_VALUE;
655     float value = (float)plugin->config.brightness / MAX_VALUE;
656     float angle = plugin->config.angle;
657
658     if(server->mode == DRAW_PATTERN_MODE)
659     {
660             switch(cmodel)
661             {
662                     case BC_RGB888:
663                             CREATE_SWATCH(unsigned char, 3, 0xff, 0)
664                             break;
665                     case BC_RGBA8888:
666                             CREATE_SWATCH(unsigned char, 4, 0xff, 0)
667                             break;
668                     case BC_RGB_FLOAT:
669                             CREATE_SWATCH(float, 3, 1.0, 0)
670                             break;
671                     case BC_RGBA_FLOAT:
672                             CREATE_SWATCH(float, 4, 1.0, 0)
673                             break;
674                     case BC_YUV888:
675                             CREATE_SWATCH(unsigned char, 3, 0xff, 1)
676                             break;
677                     case BC_YUVA8888:
678                             CREATE_SWATCH(unsigned char, 4, 0xff, 1)
679                             break;
680             }
681     }
682     else
683     {
684             switch(cmodel)
685             {
686                     case BC_RGB888:
687                             DRAW_SRC(unsigned char, 3, 0xff, 0)
688                             break;
689                     case BC_RGBA8888:
690                             DRAW_SRC(unsigned char, 4, 0xff, 0)
691                             break;
692                     case BC_RGB_FLOAT:
693                             DRAW_SRC(float, 3, 1.0, 0)
694                             break;
695                     case BC_RGBA_FLOAT:
696                             DRAW_SRC(float, 4, 1.0, 0)
697                             break;
698                     case BC_YUV888:
699                             DRAW_SRC(unsigned char, 3, 0xff, 1)
700                             break;
701                     case BC_YUVA8888:
702                             DRAW_SRC(unsigned char, 4, 0xff, 1)
703                             break;
704             }
705     }
706 }
707
708
709
710
711
712
713 SwatchEngine::SwatchEngine(SwatchMain *plugin, 
714         int total_clients, 
715         int total_packages)
716  : LoadServer(total_clients, total_packages)
717 {
718         this->plugin = plugin;
719 }
720
721 void SwatchEngine::draw_pattern()
722 {
723     mode = DRAW_PATTERN_MODE;
724     process_packages();
725 }
726
727 void SwatchEngine::draw_src()
728 {
729     mode = DRAW_SRC_MODE;
730     process_packages();
731 }
732
733 void SwatchEngine::init_packages()
734 {
735         for(int i = 0; i < get_total_packages(); i++)
736         {
737                 SwatchPackage *package = (SwatchPackage*)get_package(i);
738                 package->y1 = plugin->temp->get_h() * 
739                         i / 
740                         get_total_packages();
741                 package->y2 = plugin->temp->get_h() * 
742                         (i + 1) /
743                         get_total_packages();
744         }
745 }
746
747 LoadClient* SwatchEngine::new_client()
748 {
749         return new SwatchUnit(this, plugin);
750 }
751
752 LoadPackage* SwatchEngine::new_package()
753 {
754         return new SwatchPackage;
755 }
756
757
758
759
760