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