r863: Merge 2.1:
[cinelerra_cv/ct.git] / plugins / linearize / linearize.C
blobd885c0ba3e2424080b14db3b3870ae7c7c6900b6
1 #include "clip.h"
2 #include "filexml.h"
3 #include "linearize.h"
4 #include "bchash.h"
5 #include "language.h"
6 #include "picon_png.h"
7 #include "../colors/plugincolors.h"
8 #include "workarounds.h"
10 #include <stdio.h>
11 #include <string.h>
13 #define SQR(a) ((a) * (a))
15 REGISTER_PLUGIN(LinearizeMain)
19 LinearizeConfig::LinearizeConfig()
21         max = 1;
22         gamma = 0.6;
23         automatic = 1;
26 int LinearizeConfig::equivalent(LinearizeConfig &that)
28         return (EQUIV(max, that.max) && 
29                 EQUIV(gamma, that.gamma) &&
30                 automatic == that.automatic);
33 void LinearizeConfig::copy_from(LinearizeConfig &that)
35         max = that.max;
36         gamma = that.gamma;
37         automatic = that.automatic;
40 void LinearizeConfig::interpolate(LinearizeConfig &prev, 
41         LinearizeConfig &next, 
42         int64_t prev_frame, 
43         int64_t next_frame, 
44         int64_t current_frame)
46         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
47         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
49         this->max = prev.max * prev_scale + next.max * next_scale;
50         this->gamma = prev.gamma * prev_scale + next.gamma * next_scale;
51         this->automatic = prev.automatic;
61 LinearizePackage::LinearizePackage()
62  : LoadPackage()
64         start = end = 0;
76 LinearizeUnit::LinearizeUnit(LinearizeMain *plugin)
78         this->plugin = plugin;
80         
81 void LinearizeUnit::process_package(LoadPackage *package)
83         LinearizePackage *pkg = (LinearizePackage*)package;
84         LinearizeEngine *engine = (LinearizeEngine*)get_server();
85         VFrame *data = engine->data;
86         int w = data->get_w();
87         float r, g, b, y, u, v;
89 // The same algorithm used by dcraw
90         if(engine->operation == LinearizeEngine::HISTOGRAM)
91         {
92 #define HISTOGRAM_HEAD(type) \
93                 for(int i = pkg->start; i < pkg->end; i++) \
94                 { \
95                         type *row = (type*)data->get_rows()[i]; \
96                         for(int j = 0; j < w; j++) \
97                         {
99 #define HISTOGRAM_TAIL(components) \
100                                 int slot; \
101                                 slot = (int)(r * HISTOGRAM_SIZE); \
102                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
103                                 slot = (int)(g * HISTOGRAM_SIZE); \
104                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
105                                 slot = (int)(b * HISTOGRAM_SIZE); \
106                                 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
107                                 row += components; \
108                         } \
109                 }
112                 switch(data->get_color_model())
113                 {
114                         case BC_RGB888:
115                                 HISTOGRAM_HEAD(unsigned char)
116                                 r = (float)row[0] / 0xff;
117                                 g = (float)row[1] / 0xff;
118                                 b = (float)row[2] / 0xff;
119                                 HISTOGRAM_TAIL(3)
120                                 break;
121                         case BC_RGBA8888:
122                                 HISTOGRAM_HEAD(unsigned char)
123                                 r = (float)row[0] / 0xff;
124                                 g = (float)row[1] / 0xff;
125                                 b = (float)row[2] / 0xff;
126                                 HISTOGRAM_TAIL(4)
127                                 break;
128                         case BC_RGB_FLOAT:
129                                 HISTOGRAM_HEAD(float)
130                                 r = row[0];
131                                 g = row[1];
132                                 b = row[2];
133                                 HISTOGRAM_TAIL(3)
134                                 break;
135                         case BC_RGBA_FLOAT:
136                                 HISTOGRAM_HEAD(float)
137                                 r = row[0];
138                                 g = row[1];
139                                 b = row[2];
140                                 HISTOGRAM_TAIL(4)
141                                 break;
142                         case BC_YUV888:
143                                 HISTOGRAM_HEAD(unsigned char)
144                                 y = row[0];
145                                 u = row[1];
146                                 v = row[2];
147                                 y /= 0xff;
148                                 u = (float)((u - 0x80) / 0xff);
149                                 v = (float)((v - 0x80) / 0xff);
150                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
151                                 HISTOGRAM_TAIL(3)
152                                 break;
153                         case BC_YUVA8888:
154                                 HISTOGRAM_HEAD(unsigned char)
155                                 y = (float)row[0] / 0xff;
156                                 u = (float)row[1] / 0xff;
157                                 v = (float)row[2] / 0xff;
158                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
159                                 HISTOGRAM_TAIL(4)
160                                 break;
161                 }
162         }
163         else
164         {
165                 float max = plugin->config.max;
166                 float scale = 1.0 / max;
167                 float gamma = plugin->config.gamma - 1.0;
169 #define LINEARIZE_HEAD(type) \
170                 for(int i = pkg->start; i < pkg->end; i++) \
171                 { \
172                         type *row = (type*)data->get_rows()[i]; \
173                         for(int j = 0; j < w; j++) \
174                         {
176 #define LINEARIZE_MID \
177                                 r = r * scale * powf(r * 2 / max, gamma); \
178                                 g = g * scale * powf(g * 2 / max, gamma); \
179                                 b = b * scale * powf(b * 2 / max, gamma); \
181 #define LINEARIZE_TAIL(components) \
182                                 row += components; \
183                         } \
184                 }
187                 switch(data->get_color_model())
188                 {
189                         case BC_RGB888:
190                                 LINEARIZE_HEAD(unsigned char)
191                                 r = (float)row[0] / 0xff;
192                                 g = (float)row[1] / 0xff;
193                                 b = (float)row[2] / 0xff;
194                                 LINEARIZE_MID
195                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
196                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
197                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
198                                 LINEARIZE_TAIL(3)
199                                 break;
200                         case BC_RGBA8888:
201                                 LINEARIZE_HEAD(unsigned char)
202                                 r = (float)row[0] / 0xff;
203                                 g = (float)row[1] / 0xff;
204                                 b = (float)row[2] / 0xff;
205                                 LINEARIZE_MID
206                                 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
207                                 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
208                                 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
209                                 LINEARIZE_TAIL(4)
210                                 break;
211                         case BC_RGB_FLOAT:
212                                 LINEARIZE_HEAD(float)
213                                 r = row[0];
214                                 g = row[1];
215                                 b = row[2];
216                                 LINEARIZE_MID
217                                 row[0] = r;
218                                 row[1] = g;
219                                 row[2] = b;
220                                 LINEARIZE_TAIL(3)
221                                 break;
222                         case BC_RGBA_FLOAT:
223                                 LINEARIZE_HEAD(float)
224                                 r = row[0];
225                                 g = row[1];
226                                 b = row[2];
227                                 LINEARIZE_MID
228                                 row[0] = r;
229                                 row[1] = g;
230                                 row[2] = b;
231                                 LINEARIZE_TAIL(4)
232                                 break;
233                         case BC_YUV888:
234                                 LINEARIZE_HEAD(unsigned char)
235                                 y = row[0];
236                                 u = row[1];
237                                 v = row[2];
238                                 y /= 0xff;
239                                 u = (float)((u - 0x80) / 0xff);
240                                 v = (float)((v - 0x80) / 0xff);
241                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
242                                 LINEARIZE_MID
243                                 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
244                                 y *= 0xff;
245                                 u = u * 0xff + 0x80;
246                                 v = v * 0xff + 0x80;
247                                 row[0] = (int)CLIP(y, 0, 0xff);
248                                 row[1] = (int)CLIP(u, 0, 0xff);
249                                 row[2] = (int)CLIP(v, 0, 0xff);
250                                 LINEARIZE_TAIL(3)
251                                 break;
252                         case BC_YUVA8888:
253                                 LINEARIZE_HEAD(unsigned char)
254                                 y = row[0];
255                                 u = row[1];
256                                 v = row[2];
257                                 y /= 0xff;
258                                 u = (float)((u - 0x80) / 0xff);
259                                 v = (float)((v - 0x80) / 0xff);
260                                 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
261                                 LINEARIZE_MID
262                                 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
263                                 y *= 0xff;
264                                 u = u * 0xff + 0x80;
265                                 v = v * 0xff + 0x80;
266                                 row[0] = (int)CLIP(y, 0, 0xff);
267                                 row[1] = (int)CLIP(u, 0, 0xff);
268                                 row[2] = (int)CLIP(v, 0, 0xff);
269                                 LINEARIZE_TAIL(4)
270                                 break;
271                 }
272         }
285 LinearizeEngine::LinearizeEngine(LinearizeMain *plugin)
286  : LoadServer(plugin->get_project_smp() + 1, 
287         plugin->get_project_smp() + 1)
289         this->plugin = plugin;
292 void LinearizeEngine::init_packages()
294         for(int i = 0; i < get_total_packages(); i++)
295         {
296                 LinearizePackage *package = (LinearizePackage*)get_package(i);
297                 package->start = data->get_h() * i / get_total_packages();
298                 package->end = data->get_h() * (i + 1) / get_total_packages();
299         }
301 // Initialize clients here in case some don't get run.
302         for(int i = 0; i < get_total_clients(); i++)
303         {
304                 LinearizeUnit *unit = (LinearizeUnit*)get_client(i);
305                 bzero(unit->accum, sizeof(int) * HISTOGRAM_SIZE);
306         }
307         bzero(accum, sizeof(int) * HISTOGRAM_SIZE);
310 LoadClient* LinearizeEngine::new_client()
312         return new LinearizeUnit(plugin);
315 LoadPackage* LinearizeEngine::new_package()
317         return new LinearizePackage;
320 void LinearizeEngine::process_packages(int operation, VFrame *data)
322         this->data = data;
323         this->operation = operation;
324         LoadServer::process_packages();
325         for(int i = 0; i < get_total_clients(); i++)
326         {
327                 LinearizeUnit *unit = (LinearizeUnit*)get_client(i);
328                 for(int j = 0; j < HISTOGRAM_SIZE; j++)
329                 {
330                         accum[j] += unit->accum[j];
331                 }
332         }
350 LinearizeMain::LinearizeMain(PluginServer *server)
351  : PluginVClient(server)
353         engine = 0;
354         PLUGIN_CONSTRUCTOR_MACRO
357 LinearizeMain::~LinearizeMain()
359         PLUGIN_DESTRUCTOR_MACRO
361         delete engine;
364 char* LinearizeMain::plugin_title() { return N_("Linearize"); }
365 int LinearizeMain::is_realtime() { return 1; }
371 NEW_PICON_MACRO(LinearizeMain)
372 LOAD_CONFIGURATION_MACRO(LinearizeMain, LinearizeConfig)
373 SHOW_GUI_MACRO(LinearizeMain, LinearizeThread)
374 RAISE_WINDOW_MACRO(LinearizeMain)
375 SET_STRING_MACRO(LinearizeMain)
381 int LinearizeMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
383         this->frame = input_ptr;
384         load_configuration();
387         if(config.automatic)
388         {
389                 calculate_max(input_ptr);
390                 send_render_gui(this);
391         }
392         else
393                 send_render_gui(this);
395         if(!engine) engine = new LinearizeEngine(this);
396         engine->process_packages(LinearizeEngine::APPLY, input_ptr);
397         return 0;
400 void LinearizeMain::calculate_max(VFrame *frame)
402         if(!engine) engine = new LinearizeEngine(this);
403         engine->process_packages(LinearizeEngine::HISTOGRAM, frame);
404         int total_pixels = frame->get_w() * frame->get_h() * 3;
405         int max_fraction = (int)((int64_t)total_pixels * 99 / 100);
406         int current = 0;
407         config.max = 1;
408         for(int i = 0; i < HISTOGRAM_SIZE; i++)
409         {
410                 current += engine->accum[i];
411                 if(current > max_fraction)
412                 {
413                         config.max = (float)i / HISTOGRAM_SIZE;
414                         break;
415                 }
416         }
420 void LinearizeMain::update_gui()
422         if(thread)
423         {
424                 if(load_configuration())
425                 {
426                         thread->window->lock_window("LinearizeMain::update_gui");
427                         thread->window->update();
428                         thread->window->unlock_window();
429                 }
430         }
433 void LinearizeMain::render_gui(void *data)
435         LinearizeMain *ptr = (LinearizeMain*)data;
436         config.max = ptr->config.max;
438         if(!engine) engine = new LinearizeEngine(this);
439         if(ptr->engine && ptr->config.automatic)
440         {
441                 memcpy(engine->accum, 
442                         ptr->engine->accum, 
443                         sizeof(int) * HISTOGRAM_SIZE);
444                 thread->window->lock_window("LinearizeMain::render_gui");
445                 thread->window->update();
446                 thread->window->unlock_window();
447         }
448         else
449         {
450                 engine->process_packages(LinearizeEngine::HISTOGRAM, 
451                         ptr->frame);
452                 thread->window->lock_window("LinearizeMain::render_gui");
453                 thread->window->update_histogram();
454                 thread->window->unlock_window();
455         }
460 int LinearizeMain::load_defaults()
462         char directory[1024], string[1024];
463 // set the default directory
464         sprintf(directory, "%slinearize.rc", BCASTDIR);
466 // load the defaults
467         defaults = new BC_Hash(directory);
468         defaults->load();
470         config.max = defaults->get("MAX", config.max);
471         config.gamma = defaults->get("GAMMA", config.gamma);
472         config.automatic = defaults->get("AUTOMATIC", config.automatic);
473         return 0;
476 int LinearizeMain::save_defaults()
478         defaults->update("MAX", config.max);
479         defaults->update("GAMMA", config.gamma);
480         defaults->update("AUTOMATIC", config.automatic);
481         defaults->save();
482         return 0;
485 void LinearizeMain::save_data(KeyFrame *keyframe)
487         FileXML output;
489 // cause data to be stored directly in text
490         output.set_shared_string(keyframe->data, MESSAGESIZE);
491         output.tag.set_title("LINEARIZE");
492         output.tag.set_property("MAX", config.max);
493         output.tag.set_property("GAMMA", config.gamma);
494         output.tag.set_property("AUTOMATIC",  config.automatic);
495         output.append_tag();
496         output.terminate_string();
499 void LinearizeMain::read_data(KeyFrame *keyframe)
501         FileXML input;
503         input.set_shared_string(keyframe->data, strlen(keyframe->data));
505         int result = 0;
507         while(!result)
508         {
509                 result = input.read_tag();
511                 if(!result)
512                 {
513                         if(input.tag.title_is("LINEARIZE"))
514                         {
515                                 config.max = input.tag.get_property("MAX", config.max);
516                                 config.gamma = input.tag.get_property("GAMMA", config.gamma);
517                                 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
518                         }
519                 }
520         }