r877: Fix files that were missing from a make dist tarball.
[cinelerra_cv.git] / plugins / unsharp / unsharp.C
blobfb21336de0fade935596a117230e32a28bd5bfcc
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "bchash.h"
4 #include "filexml.h"
5 #include "keyframe.h"
6 #include "language.h"
7 #include "unsharp.h"
8 #include "unsharpwindow.h"
9 #include "picon_png.h"
12 #include <errno.h>
13 #include <unistd.h>
15 REGISTER_PLUGIN(UnsharpMain)
19 UnsharpConfig::UnsharpConfig()
21         radius = 5;
22         amount = 0.5;
23         threshold = 0;
26 int UnsharpConfig::equivalent(UnsharpConfig &that)
28         return EQUIV(radius, that.radius) &&
29                 EQUIV(amount, that.amount) &&
30                 threshold == that.threshold;
33 void UnsharpConfig::copy_from(UnsharpConfig &that)
35         radius = that.radius;
36         amount = that.amount;
37         threshold = that.threshold;
40 void UnsharpConfig::interpolate(UnsharpConfig &prev, 
41         UnsharpConfig &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);
48         this->radius = prev.radius * prev_scale + next.radius * next_scale;
49         this->amount = prev.amount * prev_scale + next.amount * next_scale;
50         this->threshold = (int)(prev.threshold * prev_scale + next.threshold * next_scale);
71 UnsharpMain::UnsharpMain(PluginServer *server)
72  : PluginVClient(server)
74         PLUGIN_CONSTRUCTOR_MACRO
75         engine = 0;
78 UnsharpMain::~UnsharpMain()
80         PLUGIN_DESTRUCTOR_MACRO
81         delete engine;
84 char* UnsharpMain::plugin_title() { return N_("Unsharp"); }
85 int UnsharpMain::is_realtime() { return 1; }
87 NEW_PICON_MACRO(UnsharpMain)
89 SHOW_GUI_MACRO(UnsharpMain, UnsharpThread)
91 SET_STRING_MACRO(UnsharpMain)
93 RAISE_WINDOW_MACRO(UnsharpMain)
95 LOAD_CONFIGURATION_MACRO(UnsharpMain, UnsharpConfig)
99 void UnsharpMain::update_gui()
101         if(thread)
102         {
103                 if(load_configuration())
104                 {
105                         thread->window->lock_window("UnsharpMain::update_gui");
106                         thread->window->update();
107                         thread->window->unlock_window();
108                 }
109         }
113 int UnsharpMain::load_defaults()
115         char directory[BCTEXTLEN], string[BCTEXTLEN];
116 // set the default directory
117         sprintf(directory, "%sunsharp.rc", BCASTDIR);
119 // load the defaults
120         defaults = new BC_Hash(directory);
121         defaults->load();
123         config.radius = defaults->get("RADIUS", config.radius);
124         config.amount = defaults->get("AMOUNT", config.amount);
125         config.threshold = defaults->get("THRESHOLD", config.threshold);
126         return 0;
130 int UnsharpMain::save_defaults()
132         defaults->update("RADIUS", config.radius);
133         defaults->update("AMOUNT", config.amount);
134         defaults->update("THRESHOLD", config.threshold);
135         defaults->save();
136         return 0;
141 void UnsharpMain::save_data(KeyFrame *keyframe)
143         FileXML output;
145 // cause data to be stored directly in text
146         output.set_shared_string(keyframe->data, MESSAGESIZE);
147         output.tag.set_title("UNSHARP");
149         output.tag.set_property("RADIUS", config.radius);
150         output.tag.set_property("AMOUNT", config.amount);
151         output.tag.set_property("THRESHOLD", config.threshold);
152         output.append_tag();
153         output.terminate_string();
156 void UnsharpMain::read_data(KeyFrame *keyframe)
158         FileXML input;
160         input.set_shared_string(keyframe->data, strlen(keyframe->data));
162         int result = 0;
164         while(!result)
165         {
166                 result = input.read_tag();
168                 if(!result)
169                 {
170                         if(input.tag.title_is("UNSHARP"))
171                         {
172                                 config.radius = input.tag.get_property("RADIUS", config.radius);
173                                 config.amount = input.tag.get_property("AMOUNT", config.amount);
174                                 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
175                         }
176                 }
177         }
186 int UnsharpMain::process_buffer(VFrame *frame,
187         int64_t start_position,
188         double frame_rate)
190         int need_reconfigure = load_configuration();
192         if(!engine) engine = new UnsharpEngine(this, 
193                 get_project_smp() + 1,
194                 get_project_smp() + 1);
195         read_frame(frame,
196                 0, 
197                 get_source_position(),
198                 get_framerate());
199         engine->do_unsharp(frame);
200         return 0;
214 UnsharpPackage::UnsharpPackage()
215  : LoadPackage()
224 UnsharpUnit::UnsharpUnit(UnsharpEngine *server, 
225         UnsharpMain *plugin)
226  : LoadClient(server)
228         this->plugin = plugin;
229         this->server = server;
230         temp = 0;
233 UnsharpUnit::~UnsharpUnit()
235         delete temp;
239 // Derived from the Gimp.
240 // In the original file it says
242 // Copyright (C) 1999 Winston Chang
243 //                    <winstonc@cs.wisc.edu>
244 //                    <winston@stdout.org>
246 // Adapted for Cinelerra by Heroine Virtual Ltd.
248 static int calculate_convolution_matrix(double radius, double **cmatrix)
250         radius = fabs(radius) + 1.0;
251         double std_dev = radius;
252         radius = std_dev * 2;
253         int matrix_length = (int)(2 * ceil(radius - 0.5) + 1);
254         matrix_length = MAX(1, matrix_length);
255         int matrix_midpoint = matrix_length / 2 + 1;
256         (*cmatrix) = new double[matrix_length];
258 // Top right of matrix
259         for(int i = matrix_length / 2 + 1; i < matrix_length; i++)
260         {
261                 double base_x = i - floor(matrix_length / 2) - 0.5;
262                 double sum = 0;
263                 for(int j = 1; j <= 50; j++)
264                 {
265                         if(base_x + 0.02 * j <= radius)
266                         {
267                                 sum += exp(-(base_x + 0.02 * j) * 
268                                         (base_x + 0.02 * j) /
269                                         (2 * std_dev * std_dev));
270                         }
271                 }
272                 (*cmatrix)[i] = sum / 50;
273         }
275 // Top left of matrix
276         for(int i = 0; i < matrix_length / 2; i++)
277         {
278                 (*cmatrix)[i] = (*cmatrix)[matrix_length - 1 - i];
279         }
281 // Center value
282         double sum = 0;
283         for(int j = 0; j <= 50; j++)
284         {
285                 sum += exp(-(0.5 + 0.02 * j) *
286                         (0.5 + 0.02 * j) /
287                         (2 * std_dev * std_dev));
288         }
289         (*cmatrix)[matrix_length / 2] = sum / 51;
291 // Normalize
292         sum = 0;
293         for(int i = 0; i < matrix_length; i++)
294                 sum += (*cmatrix)[i];
295         for(int i = 0; i < matrix_length; i++)
296                 (*cmatrix)[i] = (*cmatrix)[i] / sum;
298         return matrix_length;
301 static double get_convolution(double *cmatrix,
302         float input,
303         int index)
305         return cmatrix[index] * input;
308 static void blur_pixels(double *cmatrix, 
309         int cmatrix_length,
310         float *input,
311         float *output,
312         int pixels,
313         int components)
315         if(cmatrix_length > pixels)
316         {
317                 for(int pixel = 0; pixel < pixels; pixel++)
318                 {
319                         double scale = 0;
320                         for(int j = 0; j < pixels; j++)
321                         {
322                                 if((j + cmatrix_length / 2 - pixel >= 0) &&
323                                         (j + cmatrix_length / 2 - pixel < cmatrix_length))
324                                 {
325                                         scale += cmatrix[j + cmatrix_length / 2 - pixel];
326                                 }
327                         }
329                         for(int i = 0; i < components; i++)
330                         {
331                                 double sum = 0;
332                                 for(int j = 0; j < pixels; j++)
333                                 {
334                                         if((j >= pixel - cmatrix_length / 2) &&
335                                                 (j <= pixel + cmatrix_length / 2))
336                                         {
337                                                 sum += input[j * components + i] * cmatrix[i];
338                                         }
339                                 }
340                                 output[pixel * components + i] = sum / scale;
341                         }
342                 }
343         }
344         else
345         {
346                 int cmatrix_middle = cmatrix_length / 2;
347                 int pixel;
348                 for(pixel = 0; pixel < cmatrix_middle; pixel++)
349                 {
350                         double scale = 0;
351                         for(int j = cmatrix_middle - pixel; j < cmatrix_length;j++)
352                         {
353                                 scale += cmatrix[j];
354                         }
356                         for(int i = 0; i < components; i++)
357                         {
358                                 double sum = 0;
359                                 for(int j = cmatrix_middle - pixel; j < cmatrix_length; j++)
360                                 {
361                                         sum += input[(pixel + j - cmatrix_middle) * components + i] *
362                                                 cmatrix[j];
363                                 }
364                                 output[pixel * components + i] = sum / scale;
365                         }
366                 }
368                 float *output_ptr = output + pixel * components;
369                 for( ; pixel < pixels - cmatrix_middle; pixel++)
370                 {
371                         float *input_ptr = input + (pixel - cmatrix_middle) * components;
372                         for(int i = 0; i < components; i++)
373                         {
374                                 double sum = 0;
375                                 float *input_ptr2 = input_ptr;
376                                 for(int j = cmatrix_length; j > 0; j--)
377                                 {
378                                         sum += get_convolution(cmatrix, 
379                                                 *input_ptr2, 
380                                                 cmatrix_length - j);
381                                         input_ptr2 += components;
382                                 }
383                                 input_ptr++;
384                                 *output_ptr++ = sum;
385                         }
386                 }
388                 for( ; pixel < pixels; pixel++)
389                 {
390                         double scale = 0;
391                         for(int j = 0; j < pixels - pixel + cmatrix_middle; j++)
392                         {
393                                 scale += cmatrix[j];
394                         }
396                         for(int i = 0; i < components; i++)
397                         {
398                                 double sum = 0;
399                                 for(int j = 0; j < pixels - pixel + cmatrix_middle; j++)
400                                 {
401                                         sum += input[(pixel + j - cmatrix_middle) * components + i] *
402                                                 cmatrix[j];
403                                 }
404                                 output[pixel * components + i] = sum / scale;
405                         }
406                 }
407         }
410 #define GET_ROW(type, components) \
411 { \
412         type *in_row = (type*)src->get_rows()[row]; \
413         int pixels = src->get_w() * components; \
414         for(int i = 0; i < pixels; i++) \
415         { \
416                 dst[i] = in_row[i]; \
417         } \
420 static void get_row(float *dst, VFrame *src, int row)
422         switch(src->get_color_model())
423         {
424                 case BC_RGB888:
425                 case BC_YUV888:
426                         GET_ROW(unsigned char, 3);
427                         break;
428                 case BC_RGB_FLOAT:
429                         GET_ROW(float, 3);
430                         break;
431                 case BC_RGBA8888:
432                 case BC_YUVA8888:
433                         GET_ROW(unsigned char, 4);
434                         break;
435                 case BC_RGBA_FLOAT:
436                         GET_ROW(float, 4);
437                         break;
438                 case BC_YUV161616:
439                         GET_ROW(uint16_t, 3);
440                         break;
441                 case BC_YUVA16161616:
442                         GET_ROW(uint16_t, 4);
443                         break;
444         }
447 static void get_column(float *dst, VFrame *src, int column)
449         int components = cmodel_components(src->get_color_model());
450         for(int i = 0; i < src->get_h(); i++)
451         {
452                 float *input_pixel = (float*)src->get_rows()[i] + column * components;
453                 memcpy(dst, input_pixel, sizeof(float) * components);
454                 dst += components;
455         }
458 static void put_column(float *src, VFrame *dst, int column)
460         int components = cmodel_components(dst->get_color_model());
461         for(int i = 0; i < dst->get_h(); i++)
462         {
463                 float *output_pixel = (float*)dst->get_rows()[i] + column * components;
464                 memcpy(output_pixel, src, sizeof(float) * components);
465                 src += components;
466         }
469 void UnsharpUnit::process_package(LoadPackage *package)
471         UnsharpPackage *pkg = (UnsharpPackage*)package;
472         int w = server->src->get_w();
473         int h = server->src->get_h();
474         int color_model = server->src->get_color_model();
475         int components = cmodel_components(color_model);
476         double *cmatrix = 0;
477         int cmatrix_length = 0;
478         int padded_y1 = pkg->y1;
479         int padded_y2 = pkg->y2;
481         cmatrix_length = calculate_convolution_matrix(
482                 plugin->config.radius, 
483                 &cmatrix);
486         if(padded_y2 < server->src->get_h())
487         {
488                 padded_y2 += cmatrix_length / 2;
489                 padded_y2 = MIN(server->src->get_h(), padded_y2);
490         }
491         if(padded_y1 > 0)
492         {
493                 padded_y1 -= cmatrix_length / 2;
494                 padded_y1 = MAX(0, padded_y1);
495         }
497         int padded_rows = padded_y2 - padded_y1;
499         if(!temp || temp->get_h() != padded_rows)
500         {
501                 delete temp;
502                 temp = 0;
503         }
505         if(!temp)
506         {
507                 temp = new VFrame(0,
508                         server->src->get_w(),
509                         padded_rows,
510                         components == 3 ? BC_RGB_FLOAT : BC_RGBA_FLOAT);
511         }
513         float *temp_in = new float[MAX(temp->get_w(), padded_rows) * components];
514         float *temp_out = new float[MAX(temp->get_w(), padded_rows) * components];
516 // Blur rows
517         for(int i = padded_y1; i < padded_y2; i++)
518         {
519                 get_row(temp_in, server->src, i);
520                 blur_pixels(cmatrix, 
521                         cmatrix_length,
522                         temp_in,
523                         temp_out,
524                         temp->get_w(),
525                         components);
526                 memcpy(temp->get_rows()[i - padded_y1],
527                         temp_out,
528                         temp->get_bytes_per_line());
529         }
531 //Now we're 100% floating point.  Blur the columns
532         for(int i = 0; i < temp->get_w(); i++)
533         {
534                 get_column(temp_in, temp, i);
535                 blur_pixels(cmatrix,
536                         cmatrix_length,
537                         temp_in,
538                         temp_out,
539                         padded_rows,
540                         components);
541                 put_column(temp_out, temp, i);
542         }
545 //printf("%f %f %d\n", plugin->config.radius,plugin->config.amount, plugin->config.threshold);
548 #define UNSHARPEN(type, components, max) \
549 { \
550         float threshold = (float)plugin->config.threshold * max / 0xff; \
551         float amount = plugin->config.amount; \
553         for(int i = pkg->y1; i < pkg->y2; i++) \
554         { \
555                 float *blurry_row = (float*)temp->get_rows()[i - padded_y1]; \
556                 type *orig_row = (type*)server->src->get_rows()[i]; \
557                 for(int j = 0; j < server->src->get_w(); j++) \
558                 { \
559                         for(int k = 0; k < components; k++) \
560                         { \
561                                 float diff = *orig_row - *blurry_row; \
562                                 if(fabsf(2 * diff) < threshold) \
563                                         diff = 0; \
564                                 float value = *orig_row + amount * diff; \
565                                 if(sizeof(type) == 4) \
566                                         *orig_row = (type)value; \
567                                 else \
568                                         *orig_row = (type)CLIP(value, 0, max); \
569                                 blurry_row++; \
570                                 orig_row++; \
571                         } \
572                 } \
573         } \
576 // Apply unsharpening
577         float threshold;
578         switch(color_model)
579         {
580                 case BC_RGB888:
581                 case BC_YUV888:
582                         UNSHARPEN(unsigned char, 3, 0xff);
583                         break;
584                 case BC_RGBA8888:
585                 case BC_YUVA8888:
586                         UNSHARPEN(unsigned char, 4, 0xff);
587                         break;
588                 case BC_RGB_FLOAT:
589                         UNSHARPEN(float, 3, 1.0);
590                         break;
591                 case BC_RGBA_FLOAT:
592                         UNSHARPEN(float, 4, 1.0);
593                         break;
594                 case BC_YUV161616:
595                         UNSHARPEN(uint16_t, 3, 0xffff);
596                         break;
597                 case BC_YUVA16161616:
598                         UNSHARPEN(uint16_t, 4, 0xffff);
599                         break;
600         }
602         delete [] temp_in;
603         delete [] temp_out;
604         delete [] cmatrix;
615 UnsharpEngine::UnsharpEngine(UnsharpMain *plugin, 
616         int total_clients,
617         int total_packages)
618  : LoadServer(
619 //1, 1 
620 total_clients, total_packages 
623         this->plugin = plugin;
626 UnsharpEngine::~UnsharpEngine()
631 void UnsharpEngine::init_packages()
633         for(int i = 0; i < get_total_packages(); i++)
634         {
635                 UnsharpPackage *pkg = (UnsharpPackage*)get_package(i);
636                 pkg->y1 = src->get_h() * i / get_total_packages();
637                 pkg->y2 = src->get_h() * (i + 1) / get_total_packages();
638         }
641 LoadClient* UnsharpEngine::new_client()
643         return new UnsharpUnit(this, plugin);
646 LoadPackage* UnsharpEngine::new_package()
648         return new UnsharpPackage;
652 void UnsharpEngine::do_unsharp(VFrame *src)
654         this->src = src;
656         process_packages();