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