update internationalization data
[goodguy/history.git] / cinelerra-5.0 / 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 "cicolors.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_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 = (float)row[0] / 0xff;
186                                 u = (float)row[1] / 0xff;
187                                 v = (float)row[2] / 0xff;
188                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
189                                 HISTOGRAM_TAIL(4)
190                                 break;
191                 }
192         }
193         else
194         {
195                 float max = plugin->config.max * plugin->config.gamma;
196                 float scale = 1.0 / max;
197                 float gamma = plugin->config.gamma - 1.0;
198
199 #define GAMMA_HEAD(type) \
200                 for(int i = pkg->start; i < pkg->end; i++) \
201                 { \
202                         type *row = (type*)data->get_rows()[i]; \
203                         for(int j = 0; j < w; j++) \
204                         {
205
206 // powf errors don't show up until later in the pipeline, which makes
207 // this very hard to isolate.
208 #define MY_POW(x, y) ((x > 0.0) ? powf(x * 2 / max, y) : 0.0)
209
210 #define GAMMA_MID \
211                                 r = r * scale * MY_POW(r, gamma); \
212                                 g = g * scale * MY_POW(g, gamma); \
213                                 b = b * scale * MY_POW(b, gamma); \
214
215 #define GAMMA_TAIL(components) \
216                                 row += components; \
217                         } \
218                 }
219
220
221                 switch(data->get_color_model())
222                 {
223                         case BC_RGB888:
224                                 GAMMA_HEAD(unsigned char)
225                                 r = (float)row[0] / 0xff;
226                                 g = (float)row[1] / 0xff;
227                                 b = (float)row[2] / 0xff;
228                                 GAMMA_MID
229                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
230                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
231                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
232                                 GAMMA_TAIL(3)
233                                 break;
234                         case BC_RGBA8888:
235                                 GAMMA_HEAD(unsigned char)
236                                 r = (float)row[0] / 0xff;
237                                 g = (float)row[1] / 0xff;
238                                 b = (float)row[2] / 0xff;
239                                 GAMMA_MID
240                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
241                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
242                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
243                                 GAMMA_TAIL(4)
244                                 break;
245                         case BC_RGB_FLOAT:
246                                 GAMMA_HEAD(float)
247                                 r = row[0];
248                                 g = row[1];
249                                 b = row[2];
250                                 GAMMA_MID
251                                 row[0] = r;
252                                 row[1] = g;
253                                 row[2] = b;
254                                 GAMMA_TAIL(3)
255                                 break;
256                         case BC_RGBA_FLOAT:
257                                 GAMMA_HEAD(float)
258                                 r = row[0];
259                                 g = row[1];
260                                 b = row[2];
261                                 GAMMA_MID
262                                 row[0] = r;
263                                 row[1] = g;
264                                 row[2] = b;
265                                 GAMMA_TAIL(4)
266                                 break;
267                         case BC_YUV888:
268                                 GAMMA_HEAD(unsigned char)
269                                 y = row[0];
270                                 u = row[1];
271                                 v = row[2];
272                                 y /= 0xff;
273                                 u = (float)((u - 0x80) / 0xff);
274                                 v = (float)((v - 0x80) / 0xff);
275                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
276                                 GAMMA_MID
277                                 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
278                                 y *= 0xff;
279                                 u = u * 0xff + 0x80;
280                                 v = v * 0xff + 0x80;
281                                 row[0] = (int)CLIP(y, 0, 0xff);
282                                 row[1] = (int)CLIP(u, 0, 0xff);
283                                 row[2] = (int)CLIP(v, 0, 0xff);
284                                 GAMMA_TAIL(3)
285                                 break;
286                         case BC_YUVA8888:
287                                 GAMMA_HEAD(unsigned char)
288                                 y = row[0];
289                                 u = row[1];
290                                 v = row[2];
291                                 y /= 0xff;
292                                 u = (float)((u - 0x80) / 0xff);
293                                 v = (float)((v - 0x80) / 0xff);
294                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
295                                 GAMMA_MID
296                                 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
297                                 y *= 0xff;
298                                 u = u * 0xff + 0x80;
299                                 v = v * 0xff + 0x80;
300                                 row[0] = (int)CLIP(y, 0, 0xff);
301                                 row[1] = (int)CLIP(u, 0, 0xff);
302                                 row[2] = (int)CLIP(v, 0, 0xff);
303                                 GAMMA_TAIL(4)
304                                 break;
305                 }
306         }
307 }
308
309
310
311
312
313
314
315
316
317
318
319 GammaEngine::GammaEngine(GammaMain *plugin)
320  : LoadServer(plugin->get_project_smp() + 1, 
321         plugin->get_project_smp() + 1)
322 {
323         this->plugin = plugin;
324 }
325
326 void GammaEngine::init_packages()
327 {
328         for(int i = 0; i < get_total_packages(); i++)
329         {
330                 GammaPackage *package = (GammaPackage*)get_package(i);
331                 package->start = data->get_h() * i / get_total_packages();
332                 package->end = data->get_h() * (i + 1) / get_total_packages();
333         }
334
335 // Initialize clients here in case some don't get run.
336         for(int i = 0; i < get_total_clients(); i++)
337         {
338                 GammaUnit *unit = (GammaUnit*)get_client(i);
339                 bzero(unit->accum, sizeof(int) * HISTOGRAM_SIZE);
340         }
341         bzero(accum, sizeof(int) * HISTOGRAM_SIZE);
342 }
343
344 LoadClient* GammaEngine::new_client()
345 {
346         return new GammaUnit(plugin);
347 }
348
349 LoadPackage* GammaEngine::new_package()
350 {
351         return new GammaPackage;
352 }
353
354 void GammaEngine::process_packages(int operation, VFrame *data)
355 {
356         this->data = data;
357         this->operation = operation;
358         LoadServer::process_packages();
359         for(int i = 0; i < get_total_clients(); i++)
360         {
361                 GammaUnit *unit = (GammaUnit*)get_client(i);
362                 for(int j = 0; j < HISTOGRAM_SIZE; j++)
363                 {
364                         accum[j] += unit->accum[j];
365                 }
366         }
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384 GammaMain::GammaMain(PluginServer *server)
385  : PluginVClient(server)
386 {
387         engine = 0;
388         
389 }
390
391 GammaMain::~GammaMain()
392 {
393         
394
395         delete engine;
396 }
397
398 const char* GammaMain::plugin_title() { return _("Gamma"); }
399 int GammaMain::is_realtime() { return 1; }
400
401
402
403
404
405 NEW_WINDOW_MACRO(GammaMain, GammaWindow)
406 LOAD_CONFIGURATION_MACRO(GammaMain, GammaConfig)
407
408
409
410
411
412 int GammaMain::process_buffer(VFrame *frame,
413         int64_t start_position,
414         double frame_rate)
415 {
416         this->frame = frame;
417         load_configuration();
418
419         frame->get_params()->update("GAMMA_GAMMA", config.gamma);
420         frame->get_params()->update("GAMMA_MAX", config.max);
421
422         int use_opengl = get_use_opengl() &&
423                 !config.automatic && 
424                 (!config.plot || !gui_open());
425
426         read_frame(frame, 
427                 0, 
428                 start_position, 
429                 frame_rate,
430                 use_opengl);
431
432         if(use_opengl)
433         {
434 // Aggregate
435                 if(next_effect_is(_("Histogram")))
436                         return 0;
437                 if(next_effect_is(_("Color Balance")))
438                         return 0;
439
440         
441                 return run_opengl();
442         }
443         else
444         if(config.automatic)
445         {
446                 calculate_max(frame);
447 // Always plot to set the slider
448                 send_render_gui(this);
449         }
450         else
451         if(config.plot) 
452         {
453                 send_render_gui(this);
454         }
455
456         if(!engine) engine = new GammaEngine(this);
457         engine->process_packages(GammaEngine::APPLY, frame);
458         return 0;
459 }
460
461 void GammaMain::calculate_max(VFrame *frame)
462 {
463         if(!engine) engine = new GammaEngine(this);
464         engine->process_packages(GammaEngine::HISTOGRAM, frame);
465         int total_pixels = frame->get_w() * frame->get_h() * 3;
466         int max_fraction = (int)((int64_t)total_pixels * 99 / 100);
467         int current = 0;
468         config.max = 1;
469         for(int i = 0; i < HISTOGRAM_SIZE; i++)
470         {
471                 current += engine->accum[i];
472                 if(current > max_fraction)
473                 {
474                         config.max = (float)i / HISTOGRAM_SIZE;
475                         break;
476                 }
477         }
478 }
479
480
481 void GammaMain::update_gui()
482 {
483         if(thread)
484         {
485                 if(load_configuration())
486                 {
487                         thread->window->lock_window("GammaMain::update_gui");
488                         ((GammaWindow*)thread->window)->update();
489                         thread->window->unlock_window();
490                 }
491         }
492 }
493
494 void GammaMain::render_gui(void *data)
495 {
496         GammaMain *ptr = (GammaMain*)data;
497         config.max = ptr->config.max;
498
499         if(!engine) engine = new GammaEngine(this);
500         if(ptr->engine && ptr->config.automatic)
501         {
502                 memcpy(engine->accum, 
503                         ptr->engine->accum, 
504                         sizeof(int) * HISTOGRAM_SIZE);
505                 thread->window->lock_window("GammaMain::render_gui");
506                 ((GammaWindow*)thread->window)->update();
507                 thread->window->unlock_window();
508         }
509         else
510         {
511                 engine->process_packages(GammaEngine::HISTOGRAM, 
512                         ptr->frame);
513                 thread->window->lock_window("GammaMain::render_gui");
514                 ((GammaWindow*)thread->window)->update_histogram();
515                 thread->window->unlock_window();
516         }
517
518
519 }
520
521 void GammaMain::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("GAMMA");
528         output.tag.set_property("MAX", config.max);
529         output.tag.set_property("GAMMA", config.gamma);
530         output.tag.set_property("AUTOMATIC",  config.automatic);
531         output.tag.set_property("PLOT",  config.plot);
532         output.append_tag();
533         output.terminate_string();
534 }
535
536 void GammaMain::read_data(KeyFrame *keyframe)
537 {
538         FileXML input;
539
540         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
541
542         int result = 0;
543
544         while(!result)
545         {
546                 result = input.read_tag();
547
548                 if(!result)
549                 {
550                         if(input.tag.title_is("GAMMA"))
551                         {
552                                 config.max = input.tag.get_property("MAX", config.max);
553                                 config.gamma = input.tag.get_property("GAMMA", config.gamma);
554                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
555                                 config.plot = input.tag.get_property("PLOT", config.plot);
556 //printf("GammaMain::read_data %f\n", config.max);
557                         }
558                 }
559         }
560 }
561
562 int GammaMain::handle_opengl()
563 {
564 #ifdef HAVE_GL
565 //printf("GammaMain::handle_opengl 1\n");
566
567         get_output()->to_texture();
568         get_output()->enable_opengl();
569
570
571         const char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
572         int current_shader = 0;
573
574
575 // Aggregate with interpolate
576         int aggregate = 0;
577         if(prev_effect_is(_("Interpolate Pixels")))
578         {
579                 aggregate = 1;
580                 INTERPOLATE_COMPILE(shader_stack, current_shader)
581         }
582
583         GAMMA_COMPILE(shader_stack, current_shader, aggregate);
584
585         unsigned int shader = VFrame::make_shader(0,
586                                 shader_stack[0],
587                                 shader_stack[1],
588                                 shader_stack[2],
589                                 shader_stack[3],
590                                 shader_stack[4],
591                                 shader_stack[5],
592                                 shader_stack[6],
593                                 shader_stack[7],
594                                 0);
595
596
597         if(shader > 0) 
598         {
599                 glUseProgram(shader);
600                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
601
602                 if(aggregate)
603                 {
604                         INTERPOLATE_UNIFORMS(shader)
605                 }
606                 GAMMA_UNIFORMS(shader)
607         }
608
609         get_output()->init_screen();
610         get_output()->bind_texture(0);
611         get_output()->draw_texture();
612         glUseProgram(0);
613         get_output()->set_opengl_state(VFrame::SCREEN);
614 #endif
615         return 0;
616 }
617
618
619
620
621
622
623
624
625