initial commit
[goodguy/history.git] / cinelerra-5.0 / plugins / unsharp / unsharp.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 "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "bcsignals.h"
26 #include "filexml.h"
27 #include "keyframe.h"
28 #include "language.h"
29 #include "unsharp.h"
30 #include "unsharpwindow.h"
31
32
33 #include <errno.h>
34 #include <unistd.h>
35
36 REGISTER_PLUGIN(UnsharpMain)
37
38
39
40 UnsharpConfig::UnsharpConfig()
41 {
42         radius = 5;
43         amount = 0.5;
44         threshold = 0;
45 }
46
47 int UnsharpConfig::equivalent(UnsharpConfig &that)
48 {
49         return EQUIV(radius, that.radius) &&
50                 EQUIV(amount, that.amount) &&
51                 threshold == that.threshold;
52 }
53
54 void UnsharpConfig::copy_from(UnsharpConfig &that)
55 {
56         radius = that.radius;
57         amount = that.amount;
58         threshold = that.threshold;
59 }
60
61 void UnsharpConfig::interpolate(UnsharpConfig &prev, 
62         UnsharpConfig &next, 
63         int64_t prev_frame, 
64         int64_t next_frame, 
65         int64_t current_frame)
66 {
67         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
68         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
69         this->radius = prev.radius * prev_scale + next.radius * next_scale;
70         this->amount = prev.amount * prev_scale + next.amount * next_scale;
71         this->threshold = (int)(prev.threshold * prev_scale + next.threshold * next_scale);
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 UnsharpMain::UnsharpMain(PluginServer *server)
93  : PluginVClient(server)
94 {
95         
96         engine = 0;
97 }
98
99 UnsharpMain::~UnsharpMain()
100 {
101         
102         delete engine;
103 }
104
105 const char* UnsharpMain::plugin_title() { return N_("Unsharp"); }
106 int UnsharpMain::is_realtime() { return 1; }
107
108
109 NEW_WINDOW_MACRO(UnsharpMain, UnsharpWindow)
110
111 LOAD_CONFIGURATION_MACRO(UnsharpMain, UnsharpConfig)
112
113
114
115 void UnsharpMain::update_gui()
116 {
117         if(thread)
118         {
119                 if(load_configuration())
120                 {
121                         thread->window->lock_window("UnsharpMain::update_gui");
122                         ((UnsharpWindow*)thread->window)->update();
123                         thread->window->unlock_window();
124                 }
125         }
126 }
127
128
129
130
131 void UnsharpMain::save_data(KeyFrame *keyframe)
132 {
133         FileXML output;
134
135 // cause data to be stored directly in text
136         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
137         output.tag.set_title("UNSHARP");
138
139         output.tag.set_property("RADIUS", config.radius);
140         output.tag.set_property("AMOUNT", config.amount);
141         output.tag.set_property("THRESHOLD", config.threshold);
142         output.append_tag();
143         output.terminate_string();
144 }
145
146 void UnsharpMain::read_data(KeyFrame *keyframe)
147 {
148         FileXML input;
149
150         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
151
152         int result = 0;
153
154         while(!result)
155         {
156                 result = input.read_tag();
157
158                 if(!result)
159                 {
160                         if(input.tag.title_is("UNSHARP"))
161                         {
162                                 config.radius = input.tag.get_property("RADIUS", config.radius);
163                                 config.amount = input.tag.get_property("AMOUNT", config.amount);
164                                 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
165                         }
166                 }
167         }
168 }
169
170
171
172
173
174
175
176 int UnsharpMain::process_buffer(VFrame *frame,
177         int64_t start_position,
178         double frame_rate)
179 {
180         /*int need_reconfigure =*/ load_configuration();
181
182         if(!engine) engine = new UnsharpEngine(this, 
183                 get_project_smp() + 1, get_project_smp() + 1);
184         read_frame(frame, 0, get_source_position(), get_framerate());
185         engine->do_unsharp(frame);
186         return 0;
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200 UnsharpPackage::UnsharpPackage()
201  : LoadPackage()
202 {
203 }
204
205
206
207
208
209
210 UnsharpUnit::UnsharpUnit(UnsharpEngine *server, 
211         UnsharpMain *plugin)
212  : LoadClient(server)
213 {
214         this->plugin = plugin;
215         this->server = server;
216         temp = 0;
217 }
218
219 UnsharpUnit::~UnsharpUnit()
220 {
221         delete temp;
222 }
223
224
225 // Derived from the Gimp.
226 // In the original file it says
227 //
228 // Copyright (C) 1999 Winston Chang
229 //                    <winstonc@cs.wisc.edu>
230 //                    <winston@stdout.org>
231 //
232 // Adapted for Cinelerra by Heroine Virtual Ltd.
233
234 static int calculate_convolution_matrix(double radius, double **cmatrix)
235 {
236         radius = fabs(radius) + 1.0;
237         double std_dev = radius;
238         radius = std_dev * 2;
239         int matrix_length = (int)(2 * ceil(radius - 0.5) + 1);
240         matrix_length = MAX(1, matrix_length);
241 //      int matrix_midpoint = matrix_length / 2 + 1;
242         (*cmatrix) = new double[matrix_length];
243
244 // Top right of matrix
245         for(int i = matrix_length / 2 + 1; i < matrix_length; i++)
246         {
247                 double base_x = i - floor(matrix_length / 2) - 0.5;
248                 double sum = 0;
249                 for(int j = 1; j <= 50; j++)
250                 {
251                         if(base_x + 0.02 * j <= radius)
252                         {
253                                 sum += exp(-(base_x + 0.02 * j) * 
254                                         (base_x + 0.02 * j) /
255                                         (2 * std_dev * std_dev));
256                         }
257                 }
258                 (*cmatrix)[i] = sum / 50;
259         }
260
261 // Top left of matrix
262         for(int i = 0; i < matrix_length / 2; i++)
263         {
264                 (*cmatrix)[i] = (*cmatrix)[matrix_length - 1 - i];
265         }
266
267 // Center value
268         double sum = 0;
269         for(int j = 0; j <= 50; j++)
270         {
271                 sum += exp(-(0.5 + 0.02 * j) *
272                         (0.5 + 0.02 * j) /
273                         (2 * std_dev * std_dev));
274         }
275         (*cmatrix)[matrix_length / 2] = sum / 51;
276
277 // Normalize
278         sum = 0;
279         for(int i = 0; i < matrix_length; i++)
280                 sum += (*cmatrix)[i];
281         for(int i = 0; i < matrix_length; i++)
282                 (*cmatrix)[i] = (*cmatrix)[i] / sum;
283
284         return matrix_length;
285 }
286
287 static double get_convolution(double *cmatrix,
288         float input,
289         int index)
290 {
291         return cmatrix[index] * input;
292 }
293
294 static void blur_pixels(double *cmatrix, 
295         int cmatrix_length,
296         float *input,
297         float *output,
298         int pixels,
299         int components)
300 {
301         if(cmatrix_length > pixels)
302         {
303                 for(int pixel = 0; pixel < pixels; pixel++)
304                 {
305                         double scale = 0;
306                         for(int j = 0; j < pixels; j++)
307                         {
308                                 if((j + cmatrix_length / 2 - pixel >= 0) &&
309                                         (j + cmatrix_length / 2 - pixel < cmatrix_length))
310                                 {
311                                         scale += cmatrix[j + cmatrix_length / 2 - pixel];
312                                 }
313                         }
314
315                         for(int i = 0; i < components; i++)
316                         {
317                                 double sum = 0;
318                                 for(int j = 0; j < pixels; j++)
319                                 {
320                                         if((j >= pixel - cmatrix_length / 2) &&
321                                                 (j <= pixel + cmatrix_length / 2))
322                                         {
323                                                 sum += input[j * components + i] * cmatrix[i];
324                                         }
325                                 }
326                                 output[pixel * components + i] = sum / scale;
327                         }
328                 }
329         }
330         else
331         {
332                 int cmatrix_middle = cmatrix_length / 2;
333                 int pixel;
334                 for(pixel = 0; pixel < cmatrix_middle; pixel++)
335                 {
336                         double scale = 0;
337                         for(int j = cmatrix_middle - pixel; j < cmatrix_length;j++)
338                         {
339                                 scale += cmatrix[j];
340                         }
341
342                         for(int i = 0; i < components; i++)
343                         {
344                                 double sum = 0;
345                                 for(int j = cmatrix_middle - pixel; j < cmatrix_length; j++)
346                                 {
347                                         sum += input[(pixel + j - cmatrix_middle) * components + i] *
348                                                 cmatrix[j];
349                                 }
350                                 output[pixel * components + i] = sum / scale;
351                         }
352                 }
353
354                 float *output_ptr = output + pixel * components;
355                 for( ; pixel < pixels - cmatrix_middle; pixel++)
356                 {
357                         float *input_ptr = input + (pixel - cmatrix_middle) * components;
358                         for(int i = 0; i < components; i++)
359                         {
360                                 double sum = 0;
361                                 float *input_ptr2 = input_ptr;
362                                 for(int j = cmatrix_length; j > 0; j--)
363                                 {
364                                         sum += get_convolution(cmatrix, 
365                                                 *input_ptr2, 
366                                                 cmatrix_length - j);
367                                         input_ptr2 += components;
368                                 }
369                                 input_ptr++;
370                                 *output_ptr++ = sum;
371                         }
372                 }
373
374                 for( ; pixel < pixels; pixel++)
375                 {
376                         double scale = 0;
377                         for(int j = 0; j < pixels - pixel + cmatrix_middle; j++)
378                         {
379                                 scale += cmatrix[j];
380                         }
381
382                         for(int i = 0; i < components; i++)
383                         {
384                                 double sum = 0;
385                                 for(int j = 0; j < pixels - pixel + cmatrix_middle; j++)
386                                 {
387                                         sum += input[(pixel + j - cmatrix_middle) * components + i] *
388                                                 cmatrix[j];
389                                 }
390                                 output[pixel * components + i] = sum / scale;
391                         }
392                 }
393         }
394 }
395
396 #define GET_ROW(type, components) \
397 { \
398         type *in_row = (type*)src->get_rows()[row]; \
399         int pixels = src->get_w() * components; \
400         for(int i = 0; i < pixels; i++) \
401         { \
402                 dst[i] = in_row[i]; \
403         } \
404 }
405
406 static void get_row(float *dst, VFrame *src, int row)
407 {
408         switch(src->get_color_model())
409         {
410                 case BC_RGB888:
411                 case BC_YUV888:
412                         GET_ROW(unsigned char, 3);
413                         break;
414                 case BC_RGB_FLOAT:
415                         GET_ROW(float, 3);
416                         break;
417                 case BC_RGBA8888:
418                 case BC_YUVA8888:
419                         GET_ROW(unsigned char, 4);
420                         break;
421                 case BC_RGBA_FLOAT:
422                         GET_ROW(float, 4);
423                         break;
424                 case BC_YUV161616:
425                         GET_ROW(uint16_t, 3);
426                         break;
427                 case BC_YUVA16161616:
428                         GET_ROW(uint16_t, 4);
429                         break;
430         }
431 }
432
433 static void get_column(float *dst, VFrame *src, int column)
434 {
435         int components = BC_CModels::components(src->get_color_model());
436         for(int i = 0; i < src->get_h(); i++)
437         {
438                 float *input_pixel = (float*)src->get_rows()[i] + column * components;
439                 memcpy(dst, input_pixel, sizeof(float) * components);
440                 dst += components;
441         }
442 }
443
444 static void put_column(float *src, VFrame *dst, int column)
445 {
446         int components = BC_CModels::components(dst->get_color_model());
447         for(int i = 0; i < dst->get_h(); i++)
448         {
449                 float *output_pixel = (float*)dst->get_rows()[i] + column * components;
450                 memcpy(output_pixel, src, sizeof(float) * components);
451                 src += components;
452         }
453 }
454
455 void UnsharpUnit::process_package(LoadPackage *package)
456 {
457         UnsharpPackage *pkg = (UnsharpPackage*)package;
458 //      int w = server->src->get_w();
459 //      int h = server->src->get_h();
460         int color_model = server->src->get_color_model();
461         int components = BC_CModels::components(color_model);
462         double *cmatrix = 0;
463         int cmatrix_length = 0;
464         int padded_y1 = pkg->y1;
465         int padded_y2 = pkg->y2;
466
467         cmatrix_length = calculate_convolution_matrix(
468                 plugin->config.radius, 
469                 &cmatrix);
470
471
472         if(padded_y2 < server->src->get_h())
473         {
474                 padded_y2 += cmatrix_length / 2;
475                 padded_y2 = MIN(server->src->get_h(), padded_y2);
476         }
477         if(padded_y1 > 0)
478         {
479                 padded_y1 -= cmatrix_length / 2;
480                 padded_y1 = MAX(0, padded_y1);
481         }
482
483         int padded_rows = padded_y2 - padded_y1;
484
485         if(!temp || temp->get_h() != padded_rows)
486         {
487                 delete temp;
488                 temp = 0;
489         }
490
491         if(!temp)
492         {
493                 temp = new VFrame;
494                 temp->set_use_shm(0);
495                 temp->reallocate(0,
496                         -1,
497                         0,
498                         0,
499                         0,
500                         server->src->get_w(),
501                         padded_rows,
502                         components == 3 ? BC_RGB_FLOAT : BC_RGBA_FLOAT,
503                         -1);
504         }
505
506         float *temp_in = new float[MAX(temp->get_w(), padded_rows) * components];
507         float *temp_out = new float[MAX(temp->get_w(), padded_rows) * components];
508
509 // Blur rows
510         for(int i = padded_y1; i < padded_y2; i++)
511         {
512                 get_row(temp_in, server->src, i);
513                 blur_pixels(cmatrix, 
514                         cmatrix_length,
515                         temp_in,
516                         temp_out,
517                         temp->get_w(),
518                         components);
519 // printf("UnsharpUnit::process_package %d %p %p %p %d %d\n", 
520 // __LINE__, 
521 // temp, 
522 // temp->get_rows()[0], 
523 // temp_out,
524 // i - padded_y1,
525 // temp->get_bytes_per_line());
526                 memcpy(temp->get_rows()[i - padded_y1],
527                         temp_out,
528                         temp->get_bytes_per_line());
529         }
530
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         }
543
544
545 //printf("%f %f %d\n", plugin->config.radius,plugin->config.amount, plugin->config.threshold);
546
547
548 #define UNSHARPEN(type, components, max) \
549 { \
550         float threshold = (float)plugin->config.threshold * max / 0xff; \
551         float amount = plugin->config.amount; \
552  \
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         } \
574 }
575
576 // Apply unsharpening
577         switch(color_model)
578         {
579                 case BC_RGB888:
580                 case BC_YUV888:
581                         UNSHARPEN(unsigned char, 3, 0xff);
582                         break;
583                 case BC_RGBA8888:
584                 case BC_YUVA8888:
585                         UNSHARPEN(unsigned char, 4, 0xff);
586                         break;
587                 case BC_RGB_FLOAT:
588                         UNSHARPEN(float, 3, 1.0);
589                         break;
590                 case BC_RGBA_FLOAT:
591                         UNSHARPEN(float, 4, 1.0);
592                         break;
593                 case BC_YUV161616:
594                         UNSHARPEN(uint16_t, 3, 0xffff);
595                         break;
596                 case BC_YUVA16161616:
597                         UNSHARPEN(uint16_t, 4, 0xffff);
598                         break;
599         }
600
601         delete [] temp_in;
602         delete [] temp_out;
603         delete [] cmatrix;
604 }
605
606
607
608
609
610
611
612
613
614 UnsharpEngine::UnsharpEngine(UnsharpMain *plugin, 
615         int total_clients,
616         int total_packages)
617  : LoadServer(
618 //1, 1 
619 total_clients, total_packages 
620 )
621 {
622         this->plugin = plugin;
623 }
624
625 UnsharpEngine::~UnsharpEngine()
626 {
627 }
628
629
630 void UnsharpEngine::init_packages()
631 {
632         for(int i = 0; i < get_total_packages(); i++)
633         {
634                 UnsharpPackage *pkg = (UnsharpPackage*)get_package(i);
635                 pkg->y1 = src->get_h() * i / get_total_packages();
636                 pkg->y2 = src->get_h() * (i + 1) / get_total_packages();
637         }
638 }
639
640 LoadClient* UnsharpEngine::new_client()
641 {
642         return new UnsharpUnit(this, plugin);
643 }
644
645 LoadPackage* UnsharpEngine::new_package()
646 {
647         return new UnsharpPackage;
648 }
649
650
651 void UnsharpEngine::do_unsharp(VFrame *src)
652 {
653         this->src = src;
654
655         process_packages();
656 }
657
658
659
660
661
662
663
664
665