r877: Fix files that were missing from a make dist tarball.
[cinelerra_cv.git] / plugins / gamma / gamma.C
blob3f0cff0fc79eb6613cc94dd34ba6125e3e51066e
1 #include "bcsignals.h"
2 #include "filexml.h"
3 #include "gamma.h"
4 #include "bchash.h"
5 #include "language.h"
6 #include "picon_png.h"
7 #include "plugincolors.h"
8 #include "../interpolate/aggregated.h"
9 #include "playback3d.h"
10 #include "workarounds.h"
13 #include <stdio.h>
14 #include <string.h>
16 #include "aggregated.h"
18 #define SQR(a) ((a) * (a))
20 REGISTER_PLUGIN(GammaMain)
24 GammaConfig::GammaConfig()
26         max = 1;
27         gamma = 0.6;
28         automatic = 1;
29         plot = 1;
32 int GammaConfig::equivalent(GammaConfig &that)
34         return (EQUIV(max, that.max) && 
35                 EQUIV(gamma, that.gamma) &&
36                 automatic == that.automatic) &&
37                 plot == that.plot;
40 void GammaConfig::copy_from(GammaConfig &that)
42         max = that.max;
43         gamma = that.gamma;
44         automatic = that.automatic;
45         plot = that.plot;
48 void GammaConfig::interpolate(GammaConfig &prev, 
49         GammaConfig &next, 
50         int64_t prev_frame, 
51         int64_t next_frame, 
52         int64_t current_frame)
54         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
55         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
57         this->max = prev.max * prev_scale + next.max * next_scale;
58         this->gamma = prev.gamma * prev_scale + next.gamma * next_scale;
59         this->automatic = prev.automatic;
60         this->plot = prev.plot;
70 GammaPackage::GammaPackage()
71  : LoadPackage()
73         start = end = 0;
85 GammaUnit::GammaUnit(GammaMain *plugin)
87         this->plugin = plugin;
90         
91 void GammaUnit::process_package(LoadPackage *package)
93         GammaPackage *pkg = (GammaPackage*)package;
94         GammaEngine *engine = (GammaEngine*)get_server();
95         VFrame *data = engine->data;
96         int w = data->get_w();
97         float r, g, b, y, u, v;
99 // The same algorithm used by dcraw
100         if(engine->operation == GammaEngine::HISTOGRAM)
101         {
102 #define HISTOGRAM_HEAD(type) \
103                 for(int i = pkg->start; i < pkg->end; i++) \
104                 { \
105                         type *row = (type*)data->get_rows()[i]; \
106                         for(int j = 0; j < w; j++) \
107                         {
109 #define HISTOGRAM_TAIL(components) \
110                                 int slot; \
111                                 slot = (int)(r * HISTOGRAM_SIZE); \
112                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
113                                 slot = (int)(g * HISTOGRAM_SIZE); \
114                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
115                                 slot = (int)(b * HISTOGRAM_SIZE); \
116                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
117                                 row += components; \
118                         } \
119                 }
122                 switch(data->get_color_model())
123                 {
124                         case BC_RGB888:
125                                 HISTOGRAM_HEAD(unsigned char)
126                                 r = (float)row[0] / 0xff;
127                                 g = (float)row[1] / 0xff;
128                                 b = (float)row[2] / 0xff;
129                                 HISTOGRAM_TAIL(3)
130                                 break;
131                         case BC_RGBA8888:
132                                 HISTOGRAM_HEAD(unsigned char)
133                                 r = (float)row[0] / 0xff;
134                                 g = (float)row[1] / 0xff;
135                                 b = (float)row[2] / 0xff;
136                                 HISTOGRAM_TAIL(4)
137                                 break;
138                         case BC_RGB_FLOAT:
139                                 HISTOGRAM_HEAD(float)
140                                 r = row[0];
141                                 g = row[1];
142                                 b = row[2];
143                                 HISTOGRAM_TAIL(3)
144                                 break;
145                         case BC_RGBA_FLOAT:
146                                 HISTOGRAM_HEAD(float)
147                                 r = row[0];
148                                 g = row[1];
149                                 b = row[2];
150                                 HISTOGRAM_TAIL(4)
151                                 break;
152                         case BC_YUV888:
153                                 HISTOGRAM_HEAD(unsigned char)
154                                 y = row[0];
155                                 u = row[1];
156                                 v = row[2];
157                                 y /= 0xff;
158                                 u = (float)((u - 0x80) / 0xff);
159                                 v = (float)((v - 0x80) / 0xff);
160                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
161                                 HISTOGRAM_TAIL(3)
162                                 break;
163                         case BC_YUVA8888:
164                                 HISTOGRAM_HEAD(unsigned char)
165                                 y = (float)row[0] / 0xff;
166                                 u = (float)row[1] / 0xff;
167                                 v = (float)row[2] / 0xff;
168                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
169                                 HISTOGRAM_TAIL(4)
170                                 break;
171                 }
172         }
173         else
174         {
175                 float max = plugin->config.max;
176                 float scale = 1.0 / max;
177                 float gamma = plugin->config.gamma - 1.0;
179 #define GAMMA_HEAD(type) \
180                 for(int i = pkg->start; i < pkg->end; i++) \
181                 { \
182                         type *row = (type*)data->get_rows()[i]; \
183                         for(int j = 0; j < w; j++) \
184                         {
186 // powf errors don't show up until later in the pipeline, which makes
187 // this very hard to isolate.
188 #define MY_POW(x, y) ((x > 0.0) ? powf(x * 2 / max, y) : 0.0)
190 #define GAMMA_MID \
191                                 r = r * scale * MY_POW(r, gamma); \
192                                 g = g * scale * MY_POW(g, gamma); \
193                                 b = b * scale * MY_POW(b, gamma); \
195 #define GAMMA_TAIL(components) \
196                                 row += components; \
197                         } \
198                 }
201                 switch(data->get_color_model())
202                 {
203                         case BC_RGB888:
204                                 GAMMA_HEAD(unsigned char)
205                                 r = (float)row[0] / 0xff;
206                                 g = (float)row[1] / 0xff;
207                                 b = (float)row[2] / 0xff;
208                                 GAMMA_MID
209                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
210                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
211                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
212                                 GAMMA_TAIL(3)
213                                 break;
214                         case BC_RGBA8888:
215                                 GAMMA_HEAD(unsigned char)
216                                 r = (float)row[0] / 0xff;
217                                 g = (float)row[1] / 0xff;
218                                 b = (float)row[2] / 0xff;
219                                 GAMMA_MID
220                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
221                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
222                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
223                                 GAMMA_TAIL(4)
224                                 break;
225                         case BC_RGB_FLOAT:
226                                 GAMMA_HEAD(float)
227                                 r = row[0];
228                                 g = row[1];
229                                 b = row[2];
230                                 GAMMA_MID
231                                 row[0] = r;
232                                 row[1] = g;
233                                 row[2] = b;
234                                 GAMMA_TAIL(3)
235                                 break;
236                         case BC_RGBA_FLOAT:
237                                 GAMMA_HEAD(float)
238                                 r = row[0];
239                                 g = row[1];
240                                 b = row[2];
241                                 GAMMA_MID
242                                 row[0] = r;
243                                 row[1] = g;
244                                 row[2] = b;
245                                 GAMMA_TAIL(4)
246                                 break;
247                         case BC_YUV888:
248                                 GAMMA_HEAD(unsigned char)
249                                 y = row[0];
250                                 u = row[1];
251                                 v = row[2];
252                                 y /= 0xff;
253                                 u = (float)((u - 0x80) / 0xff);
254                                 v = (float)((v - 0x80) / 0xff);
255                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
256                                 GAMMA_MID
257                                 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
258                                 y *= 0xff;
259                                 u = u * 0xff + 0x80;
260                                 v = v * 0xff + 0x80;
261                                 row[0] = (int)CLIP(y, 0, 0xff);
262                                 row[1] = (int)CLIP(u, 0, 0xff);
263                                 row[2] = (int)CLIP(v, 0, 0xff);
264                                 GAMMA_TAIL(3)
265                                 break;
266                         case BC_YUVA8888:
267                                 GAMMA_HEAD(unsigned char)
268                                 y = row[0];
269                                 u = row[1];
270                                 v = row[2];
271                                 y /= 0xff;
272                                 u = (float)((u - 0x80) / 0xff);
273                                 v = (float)((v - 0x80) / 0xff);
274                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
275                                 GAMMA_MID
276                                 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
277                                 y *= 0xff;
278                                 u = u * 0xff + 0x80;
279                                 v = v * 0xff + 0x80;
280                                 row[0] = (int)CLIP(y, 0, 0xff);
281                                 row[1] = (int)CLIP(u, 0, 0xff);
282                                 row[2] = (int)CLIP(v, 0, 0xff);
283                                 GAMMA_TAIL(4)
284                                 break;
285                 }
286         }
299 GammaEngine::GammaEngine(GammaMain *plugin)
300  : LoadServer(plugin->get_project_smp() + 1, 
301         plugin->get_project_smp() + 1)
303         this->plugin = plugin;
306 void GammaEngine::init_packages()
308         for(int i = 0; i < get_total_packages(); i++)
309         {
310                 GammaPackage *package = (GammaPackage*)get_package(i);
311                 package->start = data->get_h() * i / get_total_packages();
312                 package->end = data->get_h() * (i + 1) / get_total_packages();
313         }
315 // Initialize clients here in case some don't get run.
316         for(int i = 0; i < get_total_clients(); i++)
317         {
318                 GammaUnit *unit = (GammaUnit*)get_client(i);
319                 bzero(unit->accum, sizeof(int) * HISTOGRAM_SIZE);
320         }
321         bzero(accum, sizeof(int) * HISTOGRAM_SIZE);
324 LoadClient* GammaEngine::new_client()
326         return new GammaUnit(plugin);
329 LoadPackage* GammaEngine::new_package()
331         return new GammaPackage;
334 void GammaEngine::process_packages(int operation, VFrame *data)
336         this->data = data;
337         this->operation = operation;
338         LoadServer::process_packages();
339         for(int i = 0; i < get_total_clients(); i++)
340         {
341                 GammaUnit *unit = (GammaUnit*)get_client(i);
342                 for(int j = 0; j < HISTOGRAM_SIZE; j++)
343                 {
344                         accum[j] += unit->accum[j];
345                 }
346         }
364 GammaMain::GammaMain(PluginServer *server)
365  : PluginVClient(server)
367         engine = 0;
368         PLUGIN_CONSTRUCTOR_MACRO
371 GammaMain::~GammaMain()
373         PLUGIN_DESTRUCTOR_MACRO
375         delete engine;
378 char* GammaMain::plugin_title() { return N_("Gamma"); }
379 int GammaMain::is_realtime() { return 1; }
385 NEW_PICON_MACRO(GammaMain)
386 LOAD_CONFIGURATION_MACRO(GammaMain, GammaConfig)
387 SHOW_GUI_MACRO(GammaMain, GammaThread)
388 RAISE_WINDOW_MACRO(GammaMain)
389 SET_STRING_MACRO(GammaMain)
395 int GammaMain::process_buffer(VFrame *frame,
396         int64_t start_position,
397         double frame_rate)
399         this->frame = frame;
400         load_configuration();
402         frame->get_params()->update("GAMMA_GAMMA", config.gamma);
403         frame->get_params()->update("GAMMA_MAX", config.max);
405         int use_opengl = get_use_opengl() &&
406                 !config.automatic && 
407                 (!config.plot || !gui_open());
409         read_frame(frame, 
410                 0, 
411                 start_position, 
412                 frame_rate,
413                 use_opengl);
415         if(use_opengl)
416         {
417 // Aggregate
418                 if(next_effect_is("Histogram"))
419                         return 0;
420                 if(next_effect_is("Color Balance"))
421                         return 0;
423         
424                 return run_opengl();
425         }
426         else
427         if(config.automatic)
428         {
429                 calculate_max(frame);
430 // Always plot to set the slider
431                 send_render_gui(this);
432         }
433         else
434         if(config.plot) 
435         {
436                 send_render_gui(this);
437         }
439         if(!engine) engine = new GammaEngine(this);
440         engine->process_packages(GammaEngine::APPLY, frame);
441         return 0;
444 void GammaMain::calculate_max(VFrame *frame)
446         if(!engine) engine = new GammaEngine(this);
447         engine->process_packages(GammaEngine::HISTOGRAM, frame);
448         int total_pixels = frame->get_w() * frame->get_h() * 3;
449         int max_fraction = (int)((int64_t)total_pixels * 99 / 100);
450         int current = 0;
451         config.max = 1;
452         for(int i = 0; i < HISTOGRAM_SIZE; i++)
453         {
454                 current += engine->accum[i];
455                 if(current > max_fraction)
456                 {
457                         config.max = (float)i / HISTOGRAM_SIZE;
458                         break;
459                 }
460         }
464 void GammaMain::update_gui()
466         if(thread)
467         {
468                 if(load_configuration())
469                 {
470                         thread->window->lock_window("GammaMain::update_gui");
471                         thread->window->update();
472                         thread->window->unlock_window();
473                 }
474         }
477 void GammaMain::render_gui(void *data)
479         GammaMain *ptr = (GammaMain*)data;
480         config.max = ptr->config.max;
482         if(!engine) engine = new GammaEngine(this);
483         if(ptr->engine && ptr->config.automatic)
484         {
485                 memcpy(engine->accum, 
486                         ptr->engine->accum, 
487                         sizeof(int) * HISTOGRAM_SIZE);
488                 thread->window->lock_window("GammaMain::render_gui");
489                 thread->window->update();
490                 thread->window->unlock_window();
491         }
492         else
493         {
494                 engine->process_packages(GammaEngine::HISTOGRAM, 
495                         ptr->frame);
496                 thread->window->lock_window("GammaMain::render_gui");
497                 thread->window->update_histogram();
498                 thread->window->unlock_window();
499         }
504 int GammaMain::load_defaults()
506         char directory[1024], string[1024];
507 // set the default directory
508         sprintf(directory, "%sgamma.rc", BCASTDIR);
510 // load the defaults
511         defaults = new BC_Hash(directory);
512         defaults->load();
514         config.max = defaults->get("MAX", config.max);
515         config.gamma = defaults->get("GAMMA", config.gamma);
516         config.automatic = defaults->get("AUTOMATIC", config.automatic);
517         config.plot = defaults->get("PLOT", config.plot);
518         return 0;
521 int GammaMain::save_defaults()
523         defaults->update("MAX", config.max);
524         defaults->update("GAMMA", config.gamma);
525         defaults->update("AUTOMATIC", config.automatic);
526         defaults->update("PLOT", config.plot);
527         defaults->save();
528         return 0;
531 void GammaMain::save_data(KeyFrame *keyframe)
533         FileXML output;
535 // cause data to be stored directly in text
536         output.set_shared_string(keyframe->data, MESSAGESIZE);
537         output.tag.set_title("GAMMA");
538         output.tag.set_property("MAX", config.max);
539         output.tag.set_property("GAMMA", config.gamma);
540         output.tag.set_property("AUTOMATIC",  config.automatic);
541         output.tag.set_property("PLOT",  config.plot);
542         output.append_tag();
543         output.terminate_string();
546 void GammaMain::read_data(KeyFrame *keyframe)
548         FileXML input;
550         input.set_shared_string(keyframe->data, strlen(keyframe->data));
552         int result = 0;
554         while(!result)
555         {
556                 result = input.read_tag();
558                 if(!result)
559                 {
560                         if(input.tag.title_is("GAMMA"))
561                         {
562                                 config.max = input.tag.get_property("MAX", config.max);
563                                 config.gamma = input.tag.get_property("GAMMA", config.gamma);
564                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
565                                 config.plot = input.tag.get_property("PLOT", config.plot);
566 //printf("GammaMain::read_data %f\n", config.max);
567                         }
568                 }
569         }
572 int GammaMain::handle_opengl()
574 #ifdef HAVE_GL
575 //printf("GammaMain::handle_opengl 1\n");
577         get_output()->to_texture();
578         get_output()->enable_opengl();
581         char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
582         int current_shader = 0;
585 // Aggregate with interpolate
586         int aggregate = 0;
587         if(prev_effect_is("Interpolate Pixels"))
588         {
589                 aggregate = 1;
590                 INTERPOLATE_COMPILE(shader_stack, current_shader)
591         }
593         GAMMA_COMPILE(shader_stack, current_shader, aggregate);
595         unsigned int shader = VFrame::make_shader(0,
596                                 shader_stack[0],
597                                 shader_stack[1],
598                                 shader_stack[2],
599                                 shader_stack[3],
600                                 shader_stack[4],
601                                 shader_stack[5],
602                                 shader_stack[6],
603                                 shader_stack[7],
604                                 0);
607         if(shader > 0) 
608         {
609                 glUseProgram(shader);
610                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
612                 if(aggregate)
613                 {
614                         INTERPOLATE_UNIFORMS(shader)
615                 }
616                 GAMMA_UNIFORMS(shader)
617         }
619         get_output()->init_screen();
620         get_output()->bind_texture(0);
621         get_output()->draw_texture();
622         glUseProgram(0);
623         get_output()->set_opengl_state(VFrame::SCREEN);
624 #endif