igor b reset btns, reframert bug fix
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / gamma / gamma.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 "bcsignals.h"
23 #include "clip.h"
24 #include "filexml.h"
25 #include "gamma.h"
26 #include "bchash.h"
27 #include "language.h"
28 #include "bccolors.h"
29 #include "../interpolate/aggregated.h"
30 #include "playback3d.h"
31 #include "workarounds.h"
32
33
34 #include <stdio.h>
35 #include <string.h>
36
37 #include "aggregated.h"
38
39
40 REGISTER_PLUGIN(GammaMain)
41
42
43
44 GammaConfig::GammaConfig()
45 {
46         reset();
47 }
48
49 void GammaConfig::reset()
50 {
51         max = 1;
52         gamma = 0.6;
53         automatic = 1;
54         plot = 1;
55 }
56
57 int GammaConfig::equivalent(GammaConfig &that)
58 {
59         return (EQUIV(max, that.max) &&
60                 EQUIV(gamma, that.gamma) &&
61                 automatic == that.automatic) &&
62                 plot == that.plot;
63 }
64
65 void GammaConfig::copy_from(GammaConfig &that)
66 {
67         max = that.max;
68         gamma = that.gamma;
69         automatic = that.automatic;
70         plot = that.plot;
71 }
72
73 void GammaConfig::interpolate(GammaConfig &prev,
74         GammaConfig &next,
75         int64_t prev_frame,
76         int64_t next_frame,
77         int64_t current_frame)
78 {
79         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
80         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
81
82         this->max = prev.max * prev_scale + next.max * next_scale;
83         this->gamma = prev.gamma * prev_scale + next.gamma * next_scale;
84         this->automatic = prev.automatic;
85         this->plot = prev.plot;
86 }
87
88
89
90
91
92
93
94
95 GammaPackage::GammaPackage()
96  : LoadPackage()
97 {
98         start = end = 0;
99 }
100
101
102
103
104
105
106
107
108
109
110 GammaUnit::GammaUnit(GammaMain *plugin)
111 {
112         this->plugin = plugin;
113 }
114
115
116 void GammaUnit::process_package(LoadPackage *package)
117 {
118         GammaPackage *pkg = (GammaPackage*)package;
119         GammaEngine *engine = (GammaEngine*)get_server();
120         VFrame *data = engine->data;
121         int w = data->get_w();
122         float r, g, b, y, u, v;
123
124 // The same algorithm used by dcraw
125         if(engine->operation == GammaEngine::HISTOGRAM)
126         {
127 #define HISTOGRAM_HEAD(type) \
128                 for(int i = pkg->start; i < pkg->end; i++) \
129                 { \
130                         type *row = (type*)data->get_rows()[i]; \
131                         for(int j = 0; j < w; j++) \
132                         {
133
134 #define HISTOGRAM_TAIL(components) \
135                                 int slot; \
136                                 slot = (int)(r * HISTOGRAM_SIZE); \
137                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
138                                 slot = (int)(g * HISTOGRAM_SIZE); \
139                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
140                                 slot = (int)(b * HISTOGRAM_SIZE); \
141                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
142                                 row += components; \
143                         } \
144                 }
145
146
147                 switch(data->get_color_model())
148                 {
149                         case BC_RGB888:
150                                 HISTOGRAM_HEAD(unsigned char)
151                                 r = (float)row[0] / 0xff;
152                                 g = (float)row[1] / 0xff;
153                                 b = (float)row[2] / 0xff;
154                                 HISTOGRAM_TAIL(3)
155                                 break;
156                         case BC_RGBA8888:
157                                 HISTOGRAM_HEAD(unsigned char)
158                                 r = (float)row[0] / 0xff;
159                                 g = (float)row[1] / 0xff;
160                                 b = (float)row[2] / 0xff;
161                                 HISTOGRAM_TAIL(4)
162                                 break;
163                         case BC_RGB_FLOAT:
164                                 HISTOGRAM_HEAD(float)
165                                 r = row[0];
166                                 g = row[1];
167                                 b = row[2];
168                                 HISTOGRAM_TAIL(3)
169                                 break;
170                         case BC_RGBA_FLOAT:
171                                 HISTOGRAM_HEAD(float)
172                                 r = row[0];
173                                 g = row[1];
174                                 b = row[2];
175                                 HISTOGRAM_TAIL(4)
176                                 break;
177                         case BC_YUV888:
178                                 HISTOGRAM_HEAD(unsigned char)
179                                 y = row[0];
180                                 u = row[1];
181                                 v = row[2];
182                                 y /= 0xff;
183                                 u = (float)((u - 0x80) / 0xff);
184                                 v = (float)((v - 0x80) / 0xff);
185                                 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
186                                 HISTOGRAM_TAIL(3)
187                                 break;
188                         case BC_YUVA8888:
189                                 HISTOGRAM_HEAD(unsigned char)
190                                 y = row[0];
191                                 u = row[1];
192                                 v = row[2];
193                                 y /= 0xff;
194                                 u = (float)((u - 0x80) / 0xff);
195                                 v = (float)((v - 0x80) / 0xff);
196                                 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
197                                 HISTOGRAM_TAIL(4)
198                                 break;
199                 }
200         }
201         else
202         {
203                 float max = plugin->config.max * plugin->config.gamma;
204                 float scale = 1.0 / max;
205                 float gamma = plugin->config.gamma - 1.0;
206
207 #define GAMMA_HEAD(type) \
208                 for(int i = pkg->start; i < pkg->end; i++) \
209                 { \
210                         type *row = (type*)data->get_rows()[i]; \
211                         for(int j = 0; j < w; j++) \
212                         {
213
214 // powf errors don't show up until later in the pipeline, which makes
215 // this very hard to isolate.
216 #define MY_POW(x, y) ((x > 0.0) ? powf(x * 2 / max, y) : 0.0)
217
218 #define GAMMA_MID \
219                                 r = r * scale * MY_POW(r, gamma); \
220                                 g = g * scale * MY_POW(g, gamma); \
221                                 b = b * scale * MY_POW(b, gamma); \
222
223 #define GAMMA_TAIL(components) \
224                                 row += components; \
225                         } \
226                 }
227
228
229                 switch(data->get_color_model())
230                 {
231                         case BC_RGB888:
232                                 GAMMA_HEAD(unsigned char)
233                                 r = (float)row[0] / 0xff;
234                                 g = (float)row[1] / 0xff;
235                                 b = (float)row[2] / 0xff;
236                                 GAMMA_MID
237                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
238                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
239                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
240                                 GAMMA_TAIL(3)
241                                 break;
242                         case BC_RGBA8888:
243                                 GAMMA_HEAD(unsigned char)
244                                 r = (float)row[0] / 0xff;
245                                 g = (float)row[1] / 0xff;
246                                 b = (float)row[2] / 0xff;
247                                 GAMMA_MID
248                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
249                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
250                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
251                                 GAMMA_TAIL(4)
252                                 break;
253                         case BC_RGB_FLOAT:
254                                 GAMMA_HEAD(float)
255                                 r = row[0];
256                                 g = row[1];
257                                 b = row[2];
258                                 GAMMA_MID
259                                 row[0] = r;
260                                 row[1] = g;
261                                 row[2] = b;
262                                 GAMMA_TAIL(3)
263                                 break;
264                         case BC_RGBA_FLOAT:
265                                 GAMMA_HEAD(float)
266                                 r = row[0];
267                                 g = row[1];
268                                 b = row[2];
269                                 GAMMA_MID
270                                 row[0] = r;
271                                 row[1] = g;
272                                 row[2] = b;
273                                 GAMMA_TAIL(4)
274                                 break;
275                         case BC_YUV888:
276                                 GAMMA_HEAD(unsigned char)
277                                 y = row[0];
278                                 u = row[1];
279                                 v = row[2];
280                                 y /= 0xff;
281                                 u = (float)((u - 0x80) / 0xff);
282                                 v = (float)((v - 0x80) / 0xff);
283                                 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
284                                 GAMMA_MID
285                                 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
286                                 y *= 0xff;
287                                 u = u * 0xff + 0x80;
288                                 v = v * 0xff + 0x80;
289                                 row[0] = (int)CLIP(y, 0, 0xff);
290                                 row[1] = (int)CLIP(u, 0, 0xff);
291                                 row[2] = (int)CLIP(v, 0, 0xff);
292                                 GAMMA_TAIL(3)
293                                 break;
294                         case BC_YUVA8888:
295                                 GAMMA_HEAD(unsigned char)
296                                 y = row[0];
297                                 u = row[1];
298                                 v = row[2];
299                                 y /= 0xff;
300                                 u = (float)((u - 0x80) / 0xff);
301                                 v = (float)((v - 0x80) / 0xff);
302                                 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
303                                 GAMMA_MID
304                                 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
305                                 y *= 0xff;
306                                 u = u * 0xff + 0x80;
307                                 v = v * 0xff + 0x80;
308                                 row[0] = (int)CLIP(y, 0, 0xff);
309                                 row[1] = (int)CLIP(u, 0, 0xff);
310                                 row[2] = (int)CLIP(v, 0, 0xff);
311                                 GAMMA_TAIL(4)
312                                 break;
313                 }
314         }
315 }
316
317
318
319
320
321
322
323
324
325
326
327 GammaEngine::GammaEngine(GammaMain *plugin)
328  : LoadServer(plugin->get_project_smp() + 1,
329         plugin->get_project_smp() + 1)
330 {
331         this->plugin = plugin;
332 }
333
334 void GammaEngine::init_packages()
335 {
336         for(int i = 0; i < get_total_packages(); i++)
337         {
338                 GammaPackage *package = (GammaPackage*)get_package(i);
339                 package->start = data->get_h() * i / get_total_packages();
340                 package->end = data->get_h() * (i + 1) / get_total_packages();
341         }
342
343 // Initialize clients here in case some don't get run.
344         for(int i = 0; i < get_total_clients(); i++)
345         {
346                 GammaUnit *unit = (GammaUnit*)get_client(i);
347                 bzero(unit->accum, sizeof(int) * HISTOGRAM_SIZE);
348         }
349         bzero(accum, sizeof(int) * HISTOGRAM_SIZE);
350 }
351
352 LoadClient* GammaEngine::new_client()
353 {
354         return new GammaUnit(plugin);
355 }
356
357 LoadPackage* GammaEngine::new_package()
358 {
359         return new GammaPackage;
360 }
361
362 void GammaEngine::process_packages(int operation, VFrame *data)
363 {
364         this->data = data;
365         this->operation = operation;
366         LoadServer::process_packages();
367         for(int i = 0; i < get_total_clients(); i++)
368         {
369                 GammaUnit *unit = (GammaUnit*)get_client(i);
370                 for(int j = 0; j < HISTOGRAM_SIZE; j++)
371                 {
372                         accum[j] += unit->accum[j];
373                 }
374         }
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392 GammaMain::GammaMain(PluginServer *server)
393  : PluginVClient(server)
394 {
395         engine = 0;
396
397 }
398
399 GammaMain::~GammaMain()
400 {
401
402
403         delete engine;
404 }
405
406 const char* GammaMain::plugin_title() { return N_("Gamma"); }
407 int GammaMain::is_realtime() { return 1; }
408
409
410
411
412
413 NEW_WINDOW_MACRO(GammaMain, GammaWindow)
414 LOAD_CONFIGURATION_MACRO(GammaMain, GammaConfig)
415
416
417
418
419
420 int GammaMain::process_buffer(VFrame *frame,
421         int64_t start_position,
422         double frame_rate)
423 {
424         this->frame = frame;
425         load_configuration();
426
427         frame->get_params()->update("GAMMA_GAMMA", config.gamma);
428         frame->get_params()->update("GAMMA_MAX", config.max);
429
430         int use_opengl = get_use_opengl() &&
431                 !config.automatic &&
432                 (!config.plot || !gui_open());
433
434         read_frame(frame,
435                 0,
436                 start_position,
437                 frame_rate,
438                 use_opengl);
439
440         if(use_opengl)
441         {
442 // Aggregate
443                 if(next_effect_is(_("Histogram")))
444                         return 0;
445                 if(next_effect_is(_("Color Balance")))
446                         return 0;
447
448
449                 return run_opengl();
450         }
451         else
452         if(config.automatic)
453         {
454                 calculate_max(frame);
455 // Always plot to set the slider
456                 send_render_gui(this);
457         }
458         else
459         if(config.plot)
460         {
461                 send_render_gui(this);
462         }
463
464         if(!engine) engine = new GammaEngine(this);
465         engine->process_packages(GammaEngine::APPLY, frame);
466         return 0;
467 }
468
469 void GammaMain::calculate_max(VFrame *frame)
470 {
471         if(!engine) engine = new GammaEngine(this);
472         engine->process_packages(GammaEngine::HISTOGRAM, frame);
473         int total_pixels = frame->get_w() * frame->get_h() * 3;
474         int max_fraction = (int)((int64_t)total_pixels * 99 / 100);
475         int current = 0;
476         config.max = 1;
477         for(int i = 0; i < HISTOGRAM_SIZE; i++)
478         {
479                 current += engine->accum[i];
480                 if(current > max_fraction)
481                 {
482                         config.max = (float)i / HISTOGRAM_SIZE;
483                         break;
484                 }
485         }
486 }
487
488
489 void GammaMain::update_gui()
490 {
491         if(thread)
492         {
493                 if(load_configuration())
494                 {
495                         thread->window->lock_window("GammaMain::update_gui");
496                         ((GammaWindow*)thread->window)->update();
497                         thread->window->unlock_window();
498                 }
499         }
500 }
501
502 void GammaMain::render_gui(void *data)
503 {
504         GammaMain *ptr = (GammaMain*)data;
505         config.max = ptr->config.max;
506
507         if(!engine) engine = new GammaEngine(this);
508         if(ptr->engine && ptr->config.automatic)
509         {
510                 memcpy(engine->accum,
511                         ptr->engine->accum,
512                         sizeof(int) * HISTOGRAM_SIZE);
513                 thread->window->lock_window("GammaMain::render_gui");
514                 ((GammaWindow*)thread->window)->update();
515                 thread->window->unlock_window();
516         }
517         else
518         {
519                 engine->process_packages(GammaEngine::HISTOGRAM,
520                         ptr->frame);
521                 thread->window->lock_window("GammaMain::render_gui");
522                 ((GammaWindow*)thread->window)->update_histogram();
523                 thread->window->unlock_window();
524         }
525
526
527 }
528
529 void GammaMain::save_data(KeyFrame *keyframe)
530 {
531         FileXML output;
532
533 // cause data to be stored directly in text
534         output.set_shared_output(keyframe->xbuf);
535         output.tag.set_title("GAMMA");
536         output.tag.set_property("MAX", config.max);
537         output.tag.set_property("GAMMA", config.gamma);
538         output.tag.set_property("AUTOMATIC",  config.automatic);
539         output.tag.set_property("PLOT",  config.plot);
540         output.append_tag();
541         output.tag.set_title("/GAMMA");
542         output.append_tag();
543         output.append_newline();
544         output.terminate_string();
545 }
546
547 void GammaMain::read_data(KeyFrame *keyframe)
548 {
549         FileXML input;
550
551         input.set_shared_input(keyframe->xbuf);
552
553         int result = 0;
554
555         while(!result)
556         {
557                 result = input.read_tag();
558
559                 if(!result)
560                 {
561                         if(input.tag.title_is("GAMMA"))
562                         {
563                                 config.max = input.tag.get_property("MAX", config.max);
564                                 config.gamma = input.tag.get_property("GAMMA", config.gamma);
565                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
566                                 config.plot = input.tag.get_property("PLOT", config.plot);
567 //printf("GammaMain::read_data %f\n", config.max);
568                         }
569                 }
570         }
571 }
572
573 int GammaMain::handle_opengl()
574 {
575 #ifdef HAVE_GL
576 //printf("GammaMain::handle_opengl 1\n");
577
578         get_output()->to_texture();
579         get_output()->enable_opengl();
580
581         const char *shader_stack[16];
582         memset(shader_stack,0, sizeof(shader_stack));
583         int current_shader = 0;
584
585         int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
586         if( need_color_matrix )
587                 shader_stack[current_shader++] = bc_gl_colors;
588
589 // Aggregate with interpolate
590         int aggregate = prev_effect_is(_("Interpolate Pixels")) ? 1 : 0;
591         if( aggregate )
592                 INTERPOLATE_COMPILE(shader_stack, current_shader);
593
594         GAMMA_COMPILE(shader_stack, current_shader, aggregate);
595
596         shader_stack[current_shader] = 0;
597         unsigned int shader = VFrame::make_shader(shader_stack);
598         if( shader > 0 ) {
599                 glUseProgram(shader);
600                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
601                 if(aggregate)
602                         INTERPOLATE_UNIFORMS(shader);
603                 GAMMA_UNIFORMS(shader);
604                 if( need_color_matrix ) BC_GL_COLORS(shader);
605         }
606
607         get_output()->init_screen();
608         get_output()->bind_texture(0);
609         get_output()->draw_texture();
610         glUseProgram(0);
611         get_output()->set_opengl_state(VFrame::SCREEN);
612 #endif
613         return 0;
614 }
615
616
617
618
619
620
621
622
623